• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

python网络编程粘包处理

武飞扬头像
可乐我是雪碧
帮助1

在python网络编程中两台电脑在进行收发数据时,其实不是直接将数据传输给对方。

对于发送者,执行 sendall/send 发送消息时,是将数据先发送至自己网卡的 写缓冲区 ,再由缓冲区将数据

对于接受者,执行 recv 接收消息时,是从自己网卡的读缓冲区获取数据。

所以,如果发送者连续快速的发送了2条信息,接收者在读取时会认为这是1条信息,即:2个数据包粘在了一起。

粘包示例:这里ip是以本机默认ip,方便测试

客户端:发送方

  1.  
    import socket
  2.  
    #创建连接
  3.  
    client = socket.socket()
  4.  
    client.connect(('127.0.0.1', 8001))
  5.  
     
  6.  
    #发送两条消息
  7.  
    client.sendall('学如初出之苗,不见其增'.encode('utf-8'))
  8.  
    client.sendall('日有所长'.encode('utf-8'))
  9.  
    #关闭连接
  10.  
    client.close()

服务端:接收方

  1.  
    import socket
  2.  
    #创建连接
  3.  
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  4.  
    sock.bind(('127.0.0.1', 8001))
  5.  
    sock.listen(5)
  6.  
     
  7.  
    #等待连接
  8.  
    conn, addr = sock.accept()
  9.  
     
  10.  
    读取收到的数据并打印
  11.  
    client_data = conn.recv(1024)
  12.  
    print(client_data.decode('utf-8'))
  13.  
    #输出结果:学如初出之苗,不风其增日有所长
  14.  
     
  15.  
    conn.close()
  16.  
    sock.close()
学新通

发送方分开发送了2条的消息,接收却是1条,显然是不合适的

解决粘包的问题

每次发送的消息时,都将消息划分为 头部(固定字节长度) 和 数据 两部分。例如:头部,用4个字节表示后面数据的长度。

发送数据,先发送数据的长度,再发送数据(或拼接起来再发送)。

接收数据,先读4个字节就可以知道自己这个数据包中的数据长度,再根据长度读取到数据。

对于头部需要一个数字并固定为4个字节,这个功能可以借助python的struct包来实现:

  1.  
    import struct
  2.  
    # i 表示4个字节的int
  3.  
    # 199 表示字节大小为199
  4.  
    v1 = struct.pack('i', 199)# 把199转成固定4字节
  5.  
    print(v1) # b'\xc7\x00\x00\x00'
  6.  
     
  7.  
    v2 = struct.unpack('i', v1) # 4个字节转换为数字
  8.  
    print(v2) # 199

 附上转字节大小图

学新通

 接下来示例

发送方

  1.  
    import socket
  2.  
    import struct
  3.  
     
  4.  
    client = socket.socket()
  5.  
    client.connect(('127.0.0.1', 8001))
  6.  
     
  7.  
    # 第一条数据
  8.  
    data1 = '惰如磨刀之石'.encode('utf-8')
  9.  
     
  10.  
    len1 = struct.pack('i', len(data1))# 获取要发送数据的字节长度
  11.  
     
  12.  
    client.sendall(len1)# 先发送数据字节长度
  13.  
    client.sendall(data)# 发送数据
  14.  
     
  15.  
    # 第二条数据
  16.  
    data2 = '不见其损,日有所耗'.encode('utf-8')
  17.  
    len2 = struct.pack('i', len(data2))
  18.  
     
  19.  
    client.sendall(len2)
  20.  
    client.sendall(data2)
  21.  
     
  22.  
    client.close()
学新通

接收方

  1.  
    import socket
  2.  
    import struct
  3.  
    #创建连接,等待连接
  4.  
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  5.  
    sock.bind(('127.0.0.1', 8001))
  6.  
    sock.listen(5)
  7.  
    conn, addr = sock.accept()
  8.  
     
  9.  
    # 固定读取4字节
  10.  
    header1 = conn.recv(4)
  11.  
    data_length1 = struct.unpack('i', header1)[0]
  12.  
     
  13.  
    has_recv_len = 0
  14.  
    data1 = b""
  15.  
    while True:
  16.  
    length = data_length1 - has_recv_len
  17.  
    if length > 1024:
  18.  
    lth = 1024
  19.  
    else:
  20.  
    lth = length
  21.  
    chunk = conn.recv(lth)
  22.  
    # 可能一次收不完,自己可以计算长度再次使用recv收取,只到收完为止。 接收最大字节1024*8 = 8196
  23.  
    data1 = chunk
  24.  
    has_recv_len = len(chunk)
  25.  
    if has_recv_len == data_length1:
  26.  
    break
  27.  
    print(data1.decode('utf-8'))# 惰如磨刀之石
  28.  
     
  29.  
    # 固定读取4字节
  30.  
    header2 = conn.recv(4)
  31.  
    data_length2 = struct.unpack('i', header2)[0] # 数据字节长度
  32.  
     
  33.  
    data2 = conn.recv(data_length2) # 读取数据
  34.  
    print(data2.decode('utf-8')) # 不见其损,日有所耗
  35.  
     
  36.  
    conn.close()
  37.  
    sock.close()
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfcgei
系列文章
更多 icon
同类精品
更多 icon
继续加载