内容大纲:
一、黏包现象
二、解决方法
一、黏包现象
1、合包现象:当一串数据很短且发送间隔很段的时候,在发送端缓存区会将这两串数据合为一串发送出去。
2、拆包现象:当一串数据过长时,tcp会将这次发送的数据拆成几个包发送出去,可能会和后面的数据进行合包
3、黏包现象只发生在tcp中:
原因1:tcp传输 协议是面向流的,接收端可以选择一次性接收2个字节或3个字节,每一条信息与信息之间是没有边界的,应用看到的数据是一个整体或者说是一个流(stream)
原因2:tcp在传输数据时,不存在对包大小的限制,如果这段数据比较长,会被分段发送,如果较短,可能会等待和下一个数据一起发送
4、udp不会发生黏包现象:
原因1:udp的传输是面向无连接的,是面向消息的,每个udp包中都有消息头,就是有了消息保护边界,接收端能区分出哪些是属于同一条消息,应用程序必须以消息为单位进行提取,不能一次提取任意字节。
但是消息在传输过程中丢失的话接收端就无法提取
所以,udp适合短数据的发送,过长数据会增大数据丢死的几率
sendto函数最大能发送的数据长度是65535字节
总结:会发生黏包现象的原因:
1、发送方和接收方的缓存机制,tcp协议的面向流通信的特点
2、收发数据的边界不清晰,接收数据端不知道一次性该接收多少数据
二、解决方案
struct模块:把一个类型,如数字,转化成固定长度的bytes类型
import struct bnum = struct.pack('i',12345) print(bnum,len(bnum)) # b'90x00x00' 4 num = struct.unpack('i',bnum) print(num) # (12345,) print(num[0],type(num[0])) # 12345 <class 'int'>
在网络中的应用:
server端:
import struct import socket sk = socket.socket() ip_port = ('127.0.0.1',9000) sk.bind(('127.0.0.1',9001)) sk.listen() conn, addr = sk.accept() while True: s = input('>>>').encode('utf-8') # 用pack将数据的长度转化为四字节的bytes类型 s_len_byte = struct.pack('i',len(s)) # 先发送bytes类型的数据的长度 conn.send(s_len_byte) # 再发送数据 conn.send(s) # 先接收数据的长度 r_len_byte = conn.recv(4) # 将长度转化为int类型,unpack返回一个元组,我们要的数据在第一位 r_len = struct.unpack('i',r_len_byte)[0] print(r_len_byte,r_len) # 再接收这个长度的数据 recmsg = conn.recv(r_len) print(recmsg.decode('utf-8')) conn.close() sk.close()
client端:
import struct import socket ip_port = ('127.0.0.1',9000) sk = socket.socket() sk.connect(('127.0.0.1',9001)) while True: # 接收数据 r_len_byte = sk.recv(4) r_len = struct.unpack('i',r_len_byte)[0] print(r_len_byte,r_len) rmsg = sk.recv(r_len) print(rmsg.decode('utf-8')) # 发送数据 s = input('>>>').encode('utf-8') s_len_byte = struct.pack('i',len(s)) sk.send(s_len_byte) sk.send(s) sk.close()