def recvMessage(self, sockHandle):#读取来自客户端的数据
strings = b""
getNullTime = 0
client = self.dictSocketHandle[sockHandle]
num = 1
totalLen = 0
while True:
try:
print("第"+str(num)+"次读取数据")
data = client.recv(1024) # 这儿如果没有拿够 1024 个字节的数据,那么会循环回来拿,但是,如果发现没有数据能拿到,socket 会自动中止,扔出一个异常,代码就结束执行,所以需要 try 一下。
print(len(data))
totalLen += len(data)
if len(data) == 0: # 通道断开或者 close 之后,就会一直收到空字符串。 而不是所谓的-1 或者报异常。这个跟 C 和 java 等其他语言很不一样。
self.epollHandle.modify(sockHandle, select.EPOLLHUP | select.EPOLLET)
break
# print("本次接收到的数据........", data)
strings = strings + data
getNullTime = 0
except IOError as err:
if err.errno == 11: # 发生 Resource temporarily unavailable 错误 错误码为 11,意为:数据尚未准备好,需要等待
if getNullTime >= 7:
break
else:
getNullTime = getNullTime + 1
print("第" + str(getNullTime) + "次获取到空数据,继续尝试中.......")
time.sleep(0.1)
# getNullTime = getNullTime + 1
# print("第" + str(getNullTime) + "次获取到空数据,继续尝试中.......")
else:
print("读取数据,未知 IO 错误")
self.epollHandle.modify(sockHandle, select.EPOLLHUP | select.EPOLLET)
break
except:
print("未知错误")
self.epollHandle.modify(sockHandle, select.EPOLLHUP | select.EPOLLET)
break
num += 1
print("数据总长度", totalLen)
return strings
socket 接收数据,数据量稍大,就会断开接收,然后该 socket 会立马再次接收,是什么问题?
第 75 次读取数据
2896
第 76 次读取数据
4344
第 77 次读取数据
7240
第 78 次读取数据
第 1 次获取到空数据,继续尝试中.......
第 79 次读取数据
第 2 次获取到空数据,继续尝试中.......
第 80 次读取数据
第 3 次获取到空数据,继续尝试中.......
第 81 次读取数据
数据总长度 755856
帧为:204
Unknown opcode %#x.12
解析数据帧暂时不用的状态
第 1 次读取数据
10240
第 2 次读取数据
10240
第 3 次读取数据
10240
第 4 次读取数据
10240
第 5 次读取数据
我很确定,此时没有第二个 socket 进来,我只是单纯发了一张 1.8M 的图片而已,小的文件是可以的,不知道为啥!!!求教
1
swulling 2019-05-27 09:01:34 +08:00 via iPhone
socket 是流,你这个 recv 返回空,就认为断开…那自然是不行的。
你需要先定义一个应用层协议,比如有起始帧加长度,或者有结束帧。 |
2
swulling 2019-05-27 09:06:08 +08:00 via iPhone
另外从你返回 errno 为 11,应该是前面来了非阻塞模式吧,然后加立马加了一个 sleep …
这不还是 block 了么,为啥要用非阻塞模式… |
3
robot1 2019-05-27 09:53:30 +08:00
还是没有弄懂流式协议,所以代码就写不对,建议跟一遍<<UNIX 网络编程>>
|
4
firebroo 2019-05-27 09:56:56 +08:00
getNullTime >= 7 这种试探性手法怎么可以用上来。。
|
5
mywaiting 2019-05-27 10:31:46 +08:00 2
虽然我不知道贴主写的是什么鬼,但是我觉得很多讨论 TCP 粘包 的同志正在赶来的路上,然后说贴主写的代码导致粘包了~ [手动狗头~]
|
6
cshlxm 2019-05-27 10:32:12 +08:00
def recvdata(self,array):
view =memoryview(array).cast('B') while len(view)>0: try: nsent=self.socket.recv_into(view) view = view[nsent:] except socket.error as err: self.logger.error('%s connection error %s ,will reconnect '%(self.ip,err)) self.reconnect() |
9
swulling 2019-05-27 10:47:04 +08:00 via iPhone
@moxiaowei 非阻塞你加 sleep 和重试就没意义啥,非阻塞读不到就退出是符合预期的,你需要有别的方法之后来继续。
错误的非阻塞远不如正确的阻塞模式。你还是老老实实写阻塞模式,多开几个线程就好了。 |
10
moxiaowei OP 具体代码我扔到了 github 上,有没有大神帮我看看问题到底在哪! https://github.com/tangwei123/webSocketServer
|
12
momocraft 2019-05-27 12:12:20 +08:00
没读到数据凭什么就证明本次接收完毕,要是有人把线拔了呢
|
13
BingoXuan 2019-05-27 13:11:39 +08:00 via Android
1.不需要 sleep,会堵塞
2.不要 epollet,边缘触发一旦你不去处理,epoll 不会重新通知你 |
15
jason94 2019-05-27 13:16:54 +08:00
我们之前 tcp 连接用 tornado 写的,问题比较少。
|
16
liuminghao233 2019-05-27 13:32:49 +08:00
我觉得是 EPOLLET 的问题
没用 python 写过网络程序 但觉得 python+epoll 组合有点奇怪 就像写 golang 用 epoll 一样莫名其妙 用 py,go 就是为了写起来爽一点,要是写得跟 c 一样了,那要来干嘛 我 c++ asio+协程都比这东西爽太多 |
19
sujin190 2019-05-27 15:06:15 +08:00
if getNullTime >= 7 有啥作用,except 出 errno.EWOULDBLOCK, errno.EAGAIN 就表示数据还未准备好需要稍等通知啊,这个很正常啊,网络栈都是组合好数据一次可以读取多个 ip 包的,那么从内核 copy 到用户空间肯定比网络栈处理速度快啊,所以网络栈出 errno.EWOULDBLOCK, errno.EAGAIN 是正常的,没必要重试,下次如果数据准备好了,再调用 epoll 时又会得到通知
|
20
BingoXuan 2019-05-27 17:06:38 +08:00
@moxiaowei
我觉得你不应该再重试,如果 errno 为 11 时候,代表着没有新数据了。那么你应该跳出去,等待新 epollin 事件。 对我而言,堵塞 socket 是通过循环不断去读取数据。为了避免线程堵死,会加上 timeout。通过捕捉 timeout 异常和设定重试次数确保堵塞一定事件就放弃,避免线程堵死。当引入了 epoll 就应该变为等待 epoll 事件而不是等待数据。 |