Python3的tcp socket接收不定长数据包接收到的数据不全。

Python Socket API参考出处:http://blog.csdn.net/xiangpingli/article/details/47706707

使用socket.recv(pack_length)接收不定长的数据,如果数据包长度超过一定值,则接收的数据不全,同时还会多触发一次 socket.recv().

参照python3.4的文档可发现:

socket.recv(bufsize[, flags])

Receive data from the socket. The return value is a bytes object representing the data received. The maximum amount of data to be received at once is specified by bufsize.

上述的英文的大体意思为:从socket中接收数据。返回值是byts类型。接收的最大数量的byte为指定的bufsize.

root@iZ94nil6ddfZ:~# cat setsockopt_test.py        
#!/usr/bin/python

import socket

SEND_BUF_SIZE = 4096 # 发送缓冲区的大小
RECV_BUF_SIZE = 4096 # 接收缓冲区的大小

def modify_buff_size():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
    print "Buffer size [Before]: %d" %bufsize

    sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, SEND_BUF_SIZE)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, RECV_BUF_SIZE)
    
    bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
    print( "Buffer size [After]: %d" %bufsize)

if __name__ == '__main__':
    modify_buff_size()

执行  

  bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)

得到电脑上的默认接收缓冲区的值为:8192

问题场景描述:

server端是erlang实现的,client端是Python3实现的,通讯协议为自定义的格式,每次交互的数据包都是不定长的数据包。

从server端取数据,因为没有指定查询条件,返回的数据的数据量在1000条左右,每一条的数据都包含多个的整数型和字符串型,byte大小为:26782。

如果取得的数据量比较多,每一次的请求数据,client 的socket.recv(packet_size)会执行多次。

解决方案:

            receiverBufsize = self._client_socket.getsockopt(
                                    socket.SOL_SOCKET, 
                                    socket.SO_RCVBUF)      
           data_body = None
            if receiverBufsize < pack_length:
                
                data_body = bytes()
                left_pack_length = pack_length
                while left_pack_length > 0:
                    
                    if left_pack_length > receiverBufsize:
                        body_part =self._client_socket.recv(receiverBufsize) 
                    else:
                        body_part =self._client_socket.recv(left_pack_length) 
                    data_body  +=  body_part 
                    left_pack_length -= receiverBufsize
                
            else:
                data_body= self._client_socket.recv(pack_length)        

个人注解:

Python的socket一次最多只能读出缓冲区的全部的数据,如果指定的数据包的大小大于缓冲区的大小,则读出的有效数据仅仅为缓冲区的数据。

如果能确定发送过来的数据大于缓冲区的大小,则需要多次:socket.recv(receiverBufsize),然后将收到的数据拼接成完整的数据包后再解析。

二次错误修正:

使用上边的解决方案,在收到较大的数据的时候,偶尔会出现

一个数据包读出来了,但是数据包的较靠后的一部分数据有问题,将数据包解析后发现只有前边的部分能正确解析、后边的部分解析出来全是无效的数据。同时还会多触发几次 socket.recv().

body_part =self._client_socket.recv(pack_length) 
body_part_length = len(body_part)  # body_part_length 、left_pack_length、以及上边提到的缓冲区的大小,这三个值都不一样大。

缓冲区,bufsize: 8192

下列的两个值是我电脑传输特定的数据包的时候的值:

pack_length :26782

body_part_length: 24460

热闹了,。。。。。函数api有问题

二次解决方案:

            had_received = 0     
            data_body = bytes()  
            while had_received < pack_length:
                    part_body= self._client_socket.recv(pack_length - had_received)
                    data_body +=  part_body
                    part_body_length = len(part_body)
                    #print('part_body_length', part_body_length)
                    had_received += part_body_length

手动点击测试 :至少在五分钟内的连续点击测试并没有出现第一次的解决方案的的部分数据无效的情况。

个人注解:

  未能确定出错的原因,个人猜测:对缓冲区的读数据尽量少次数的读吧。。。。。。

原文地址:https://www.cnblogs.com/ribavnu/p/4816919.html