socket 下 recv 数据的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
moxiaowei
V2EX    Linux

socket 下 recv 数据的问题

  •  
  •   moxiaowei 2019-05-27 08:41:39 +08:00 4449 次点击
    这是一个创建于 2379 天前的主题,其中的信息可能已经有所发展或是发生改变。
     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 的图片而已,小的文件是可以的,不知道为啥!!!求教

    23 条回复    2019-05-28 08:41:31 +08:00
    swulling
        1
    swulling  
       2019-05-27 09:01:34 +08:00 via iPhone
    socket 是流,你这个 recv 返回空,就认为断开…那自然是不行的。

    你需要先定义一个应用层协议,比如有起始帧加长度,或者有结束帧。
    swulling
        2
    swulling  
       2019-05-27 09:06:08 +08:00 via iPhone
    另外从你返回 errno 为 11,应该是前面来了非阻塞模式吧,然后加立马加了一个 sleep …

    这不还是 block 了么,为啥要用非阻塞模式…
    robot1
        3
    robot1  
       2019-05-27 09:53:30 +08:00
    还是没有弄懂流式协议,所以代码就写不对,建议跟一遍<<UNIX 网络编程>>
    firebroo
        4
    firebroo  
       2019-05-27 09:56:56 +08:00
    getNullTime >= 7 这种试探性手法怎么可以用上来。。
    mywaiting
        5
    mywaiting  
       2019-05-27 10:31:46 +08:00   2
    虽然我不知道贴主写的是什么鬼,但是我觉得很多讨论 TCP 粘包 的同志正在赶来的路上,然后说贴主写的代码导致粘包了~ [手动狗头~]
    cshlxm
        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()
    moxiaowei
        7
    moxiaowei  
    OP
       2019-05-27 10:40:24 +08:00
    @swulling 生产服务器用阻塞模式,会不会效率太低,一个 socket 一直卡住咋办?
    moxiaowei
        8
    moxiaowei  
    OP
       2019-05-27 10:42:21 +08:00
    @swulling 这个 sleep 是我实在无奈才加上的,其实跟不加一个效果
    swulling
        9
    swulling  
       2019-05-27 10:47:04 +08:00 via iPhone
    @moxiaowei 非阻塞你加 sleep 和重试就没意义啥,非阻塞读不到就退出是符合预期的,你需要有别的方法之后来继续。


    错误的非阻塞远不如正确的阻塞模式。你还是老老实实写阻塞模式,多开几个线程就好了。
    moxiaowei
        10
    moxiaowei  
    OP
       2019-05-27 11:23:08 +08:00
    具体代码我扔到了 github 上,有没有大神帮我看看问题到底在哪! https://github.com/tangwei123/webSocketServer
    moxiaowei
        11
    moxiaowei  
    OP
       2019-05-27 12:09:46 +08:00
    @firebroo 这个不是试探,如果连续多次接收不到数据,则证明本次接收完毕,可以对接收到的数据进行处理了!
    momocraft
        12
    momocraft  
       2019-05-27 12:12:20 +08:00
    没读到数据凭什么就证明本次接收完毕,要是有人把线拔了呢
    BingoXuan
        13
    BingoXuan  
       2019-05-27 13:11:39 +08:00 via Android
    1.不需要 sleep,会堵塞
    2.不要 epollet,边缘触发一旦你不去处理,epoll 不会重新通知你
    BingoXuan
        14
    BingoXuan  
       2019-05-27 13:14:33 +08:00 via Android
    @momocraft
    这是 epoll 的处理,空字符代表关闭,收到对端发送的空字符会忽略
    jason94
        15
    jason94  
       2019-05-27 13:16:54 +08:00
    我们之前 tcp 连接用 tornado 写的,问题比较少。
    liuminghao233
        16
    liuminghao233  
       2019-05-27 13:32:49 +08:00
    我觉得是 EPOLLET 的问题

    没用 python 写过网络程序
    但觉得 python+epoll 组合有点奇怪
    就像写 golang 用 epoll 一样莫名其妙
    用 py,go 就是为了写起来爽一点,要是写得跟 c 一样了,那要来干嘛
    我 c++ asio+协程都比这东西爽太多
    moxiaowei
        17
    moxiaowei  
    OP
       2019-05-27 13:56:53 +08:00
    @BingoXuan 是的
    moxiaowei
        18
    moxiaowei  
    OP
       2019-05-27 13:57:59 +08:00
    @BingoXuan 肯定要用 epolllet 的边缘出发,代码能保证每个请求都被处理
    sujin190
        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 时又会得到通知
    BingoXuan
        20
    BingoXuan  
       2019-05-27 17:06:38 +08:00
    @moxiaowei
    我觉得你不应该再重试,如果 errno 为 11 时候,代表着没有新数据了。那么你应该跳出去,等待新 epollin 事件。

    对我而言,堵塞 socket 是通过循环不断去读取数据。为了避免线程堵死,会加上 timeout。通过捕捉 timeout 异常和设定重试次数确保堵塞一定事件就放弃,避免线程堵死。当引入了 epoll 就应该变为等待 epoll 事件而不是等待数据。
    moxiaowei
        21
    moxiaowei  
    OP
       2019-05-27 17:15:06 +08:00
    @BingoXuan 我的 socket 都是非阻塞的模式,数据缓存区有多少读多少
    BingoXuan
        22
    BingoXuan  
       2019-05-27 17:29:38 +08:00
    @moxiaowei
    既然如此,为什么在 errno=11 时候,你还要继续 recv 呢?这时候缓存区已经没有数据了。你已经完成处理 epoll 的事件的所有东西了。你应该跳出去等待下一次 epoll 事件。
    moxiaowei
        23
    moxiaowei  
    OP
       2019-05-28 08:41:31 +08:00
    @BingoXuan 其实我是以防万一的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1069 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 17:32 PVG 01:32 LAX 09:32 JFK 12:32
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86