Python Socket编程:解决粘包与长数据精确传输的两种方案
本文深入分析Python Socket编程中常见的粘包与长数据截断问题。针对粘包,介绍了等待机制与握手确认两种方案,推荐握手机制以平衡效率与可靠性。针对长数据发送不全问题,提出先发送数据长度再循环接收的策略,并提供服务端与客户端的完整代码示例,适用于Shell命令执行及文件传输场景。
本文通过具体实例,详细解析Python Socket连接过程中遇到的粘包现象及长数据精确传输问题,旨在为开发者提供实用的解决方案与参考代码。
-
发生原因:
当程序调用send方法时,数据并非立即通过网络发送给客户端,而是先写入操作系统的Socket发送缓冲区。只有当缓冲区填满或等待超时后,数据才会真正发出。因此,如果发送速度过快,前一份数据尚未完全传输给客户端,后续数据便可能紧随其后一起发出,从而导致“粘包”现象。
-
解决方案:
解决粘包问题的核心思路是确保不同段的数据不会混合发送,即通过某种机制将数据流在逻辑上分隔开。
- 方案1:在发送前一段数据后,强制等待一段时间再发送第二段数据。该方法的缺点在于时间效率低下,且无法从根本上保证前一份数据已完全发送,因此难以完全避免粘包问题,具体等待时长也难以确定。
- 方案2:采用握手机制。在前一段数据发送完毕后,发送方尝试调用recv接收客户端的确认信号(ACK)。只有在收到客户端确认第一段数据已成功接收后,才发送第二段数据。这被视为一种较为完美的解决方案。
方案二的演示:
服务端【发送方】代码:
import socket
server=socket.socket()
server.bind(("localhost",1234))
server.listen()
while True:
print("正在等待。。。")
conn,addr=server.accept()
while True:
try:
conn.send(b"first info")
ack=conn.recv(1024) #接收客户端确认
print(ack)
conn.send(b"second info")
except ConnectionResetError as e:
print(e)
break
server.close()
客户端【接收方】代码:
import socket
client=socket.socket()
client.connect(("localhost",1234))
data=client.recv(1024)
print(data.decode())
client.send(b"ack")#发送确认
data=client.recv(1024)
print(data.decode())
client.close()
发生原因:
当发送的数据量过大,单次send操作无法一次性全部发出,而接收方仅调用一次recv接收数据时,会导致剩余数据滞留缓冲区或丢失,进而影响后续数据的正常传输。
解决方案:
解决该问题的根本思路是调整接收方的接收策略,即通过改变recv的调用次数来完整获取数据。
- 方案:发送方先将数据的总大小(长度)发送给接收方,接收方根据该长度信息,决定需要调用recv的次数,从而确保数据的完整接收。
方案实现代码【以解决长数据shell命令传输为例】:
服务端【发送方】:
import socket,os
server=socket.socket()
server.bind(("localhost",1234))
server.listen()
while True:
print("正在等待...")
conn,addr=server.accept()
print("连接成功!")
while True:
try:
cmd=conn.recv(1024)
data=os.popen(cmd.decode()).read()
# print(data)
cmd_len=len(data.encode())
print(cmd_len)
#发现这里如果cmd_len为0会导致异常,有些是没有返回值的command
if cmd_len==0:
data="command has nothing return"
cmd_len=len(data.encode())
##因为这里前面没有发送操作,所以不用担心粘包,如果有则要考虑处理
conn.send(str(cmd_len).encode())#因为len结果是int,所以还要转换
#这里要处理粘包
ack=conn.recv(1024)
conn.send(data.encode())
except ConnectionResetError as e:
print(e)
break
server.close()
客户端【接收方】:
import socket
client=socket.socket()
client.connect(("localhost",1234))
while True:
cmd = input(">>:")
client.send(cmd.encode())
data_len=client.recv(1024)
data_len=int(data_len.decode())
print(data_len)
recv_len=0
client.send(b'ack')
total_data=b''
while recv_len<data_len:
data=client.recv(1024)
recv_len+=len(data)
total_data+=data
print(total_data.decode())
client.close()
- 利用上述原理可以实现大文件的可靠传输。只要能够准确确定接收次数(即数据总长度),就能保证文件传输的大小和内容的正确性。
更多关于Python相关内容可查看本站专题:《Python Socket编程技巧总结》、《Python数据结构与算法教程》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python入门与进阶经典教程》及《Python文件与目录操作技巧汇总》
希望本文所述对大家Python程序设计有所帮助。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









