网络编程值粘包

Posted on 2019-03-02 16:17  杜卡迪S11  阅读(64)  评论(0编辑  收藏  举报

粘包问题的产生:

  TCP流式协议:基于数据流的协议

接收方产生粘包问题

  1.发送方:发送端需要等缓冲区满了才能发送出去,造称粘包(发送数据时间间隔很短,数据量很小,会粘到一起,这样会产生粘包)

  2.接收方:不及时接收缓冲区的包,造成了多个包接收(客户端发送了一段数据,服务端只接收了一小部分,等下一次再接收的时候,服务端会从缓冲区拿出上次遗留的数据,这样会产生粘包)

 

TCP会造成粘包,UDP不会造称粘包

 

send(字节流)和 recv(1024)及sendall

recv里指定的1024意思是从缓存里一次拿出1024个字节的数据

send的字节流是先放入自己这边缓存,然后由协议控制将缓存内容发往对方,如果待发送的字节流大小大于缓存剩余空间,那么数据丢失,用sendall就会循环调用send,数据不会丢失

 

struct模块

该模块可以把一个类型,入数字,转换成固定长度的bytes

import json,struct
#假设通过客户端上传1T:1073741824000的文件a.txt

#为避免粘包,必须自定制报头
header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T数据,文件路径和md5值

#为了该报头能传送,需要序列化并且转为bytes
head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并转成bytes,用于传输

#为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节
head_len_bytes=struct.pack('i',len(head_bytes)) #这4个字节里只包含了一个数字,该数字是报头的长度

#客户端开始发送
conn.send(head_len_bytes) #先发报头的长度,4个bytes
conn.send(head_bytes) #再发报头的字节格式
conn.sendall(文件内容) #然后发真实内容的字节格式

#服务端开始接收
head_len_bytes=s.recv(4) #先收报头4个bytes,得到报头长度的字节格式
x=struct.unpack('i',head_len_bytes)[0] #提取报头的长度

head_bytes=s.recv(x) #按照报头长度x,收取报头的bytes格式
header=json.loads(json.dumps(header)) #提取报头

#最后根据报头的内容提取真实的数据,比如
real_data_len=s.recv(header['file_size'])
s.recv(real_data_len)

 

我们可以把报头做成字典,字典里包含将要发送的真是数据的详细信息,然后json序列化,然后用struct将序列化后的数据长度打包成4个字节

发送时:先发报头长度,再编码报头内容然后发送,最后发真是内容

接收时:先收报头长度,用struct取出来,根据取出的长度收取报头内容,然后解码,反序列化

从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容