事情起因是这样的:
今天学习了单线程实现并发操作,学习了gevent第三方包(周末会更新并发编程知识点,里面会有详细介绍)
老师今天布置了一个作业,其中有一道题目是这样的:
- 使用协程完成TCP套接字编程 支持多客户端同时访问
然后我就开始写代码,写完了就报错了
错误代码示例:
# 因为是测试代码,所以写的没有很规范,望见谅
'''
使用协程完成TCP套接字编程 支持多客户端同时访问
'''
# 服务端
import gevent
from gevent import monkey
from socket import *
monkey.patch_all()
server = socket(AF_INET, SOCK_STREAM)
server.bind(('localhost', 9989))
server.listen(5)
def communication(conn):
while True:
print("00000")
data = conn.recv(1024)
print('asd')
conn.send(data.upper())
while True:
conn, addr = server.accept()
print('新客户端连入', addr)
g = gevent.spawn(communication, conn)
# 客户端
from socket import *
client = socket(AF_INET, SOCK_STREAM)
client.connect(('localhost', 9989))
while True:
msg = input('>>:').strip()
client.send(msg.encode('utf-8'))
data = client.recv(2014)
print(data.decode('utf-8'))
代码看起来是没有任何问题的,但是就是无法实现并发执行的效果,很郁闷,纠结了一个晚上,和老师讨论什么的,都没有找到错误点在哪里。
我们讨论完,也没有个结果,老师回去以后用他的电脑也写了一份,代码只有一点点的差距
正确示例:
'''
使用协程完成TCP套接字编程 支持多客户端同时访问
'''
# 服务端
import gevent
import gevent.monkey
gevent.monkey.patch_all()
from socket import *
server = socket()
# 这是为了实现端口重用的代码。当程序不正常结束时,端口可能还没有被操作系统回收,就会导致需要手动更改绑定端口号,测试时候使用!
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(("127.0.0.1",9898))
server.listen(5)
def data_handler(conn,addr):
while True:
data = conn.recv(1024)
print("来自%s : %s" % (addr[1],data.decode("utf-8")))
conn.send(data.upper())
while True:
con,addr = server.accept()
g = gevent.spawn(data_handler,con,addr)
print("新连接")
我就在拿我的代码和老师的代码作对比,发现只有monkey的位置不同,一开始我根本没想到,monkey的位置会导致代码运行效果完全不同。
然后我就试着把我的monkey位置调动了一下,神奇的事情发生了,代码运行成功了,单线程下并发效果实现了!
错误原因:猴子补丁其实是做替换来的,gvent中有第三方的socket是修改过的不再是以前的那个socket了,只是把使用方法做的和原生的一模一样 。
举个例子:monkey就好比一个装饰器,会把下面的一些东西给偷梁换柱,你以为你在用的是你认识的那个socket,其实已经变成了被装饰过的socket。
- 装饰器:在不改变函数源代码及调用方式的情况下,为函数增加新功能
我们看图片中,值为True的都会被猴子给装饰一下。
猴子一定要放在导入模块的前面!!!
大家一定要注意这个问题,避免掉坑。
这个坑困扰了我一晚上,希望能给各位带来解决方法不再困扰你们。