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

本文探讨了Python中Socket API的recv方法在处理不定长数据包时的问题及解决方案。详细介绍了如何通过调整缓冲区大小确保完整接收数据,并提供了解决部分数据丢失问题的两种方法。

Python Socket API参考出处:http://blog.youkuaiyun.com/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

### 使用 Python Socket接收数据包 为了使用 Python 的 `socket` 库来接收数据包,通常会创建一个服务器端程序监听特定的端口。当客户端连接并发送消息时,服务器可以调用相应的方法读取这些信息。 下面是一个简单的例子展示如何设置一个能够接收来自任意客户端的数据包的服务端: ```python import socket def start_server(host=&#39;127.0.0.1&#39;, port=65432): # 创建一个新的套接字对象,默认为AF_INET和SOCK_STREAM类型 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: # 绑定地址到套接字上 s.bind((host, port)) # 开始监听传入连接;参数指定了最大等待队列长度 s.listen() print(f"Listening on {host}:{port}") while True: conn, addr = s.accept() # 接受新的连接请求 try: with conn: print(&#39;Connected by&#39;, addr) while True: data = conn.recv(1024) # 尝试从已建立的连接中接收最多1024字节的数据 if not data: break # 如果没有更多数据,则退出循环 print("Received:", data.decode()) # 打印收到的信息 except Exception as e: print(e) if __name__ == "__main__": start_server() ``` 此脚本会在本地主机上的指定端口启动TCP服务,并持续等待直到有新客户机尝试连接。一旦建立了连接,它就会进入内部循环,在那里它可以反复调用 `recv()` 方法来获取通过网络传输过来的数据流[^2]。 对于更复杂的应用场景,比如处理视频流或其他大文件传输的情况,可能还需要考虑多线程或多进程的支持以及优化缓冲区大小等问题[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值