TCP协议的黏包问题与解决方法:

黏包:接收到的数据包乱了,有部分本次请求的数据临时存入了缓冲区,导致下次请求时一起显示

TCP协议会黏包,但是不会丢包

UDP协议不会黏包,但可能丢包

TCP黏包原因:1.多个send连在一起发送,且数据量小  2.多个recv接收,第一个数据量小   

本质上是TCP算法进行了内部优化 一个单位时间内,连续发送较小数据包会默认合并发送,降低多次发送的延时

为什么会黏包:

因为tcp是面向流的协议,在数据发送传输中有缓存机制来避免数据丢失,连续发送接收小数据时会黏包,根本是不知道接收数据的大小

****************************************************************************************

服务器端:

 1 import socket
 2 sk=socket.socket()
 3 sk.bind(('127.0.0.1',8080))
 4 sk.listen()
 5 
 6 conn,addr=sk.accept()
 7 while True:
 8     cmd=input('请输入...')
 9     if cmd=='q':
10         conn.send(b'q')
11         break
12     conn.send(cmd.encode('gbk'))
13     long=conn.recv(1024).decode('utf-8')
14     conn.send(b'ok')  #服务器确认收到
15 
16     res=conn.recv(int(long)).decode('gbk')
17     print(res)
18 conn.close()
19 sk.close()

客户端:

 1 import socket
 2 import subprocess
 3 sk=socket.socket()
 4 
 5 sk.connect(('127.0.0.1',8080))
 6 while True:
 7     cmd=sk.recv(1024).decode('gbk')
 8     if cmd=='q':
 9         sk.send(b'q')
10         break
11     res=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
12     stdout=res.stdout.read()
13     stderr=res.stderr.read()
14     sk.send(str(len(stdout)+len(stderr)).encode('utf-8')) #告诉服务器要接收数据的长度
15     sk.recv(1024)
16     sk.send(stdout)
17     sk.send(stderr)
18 sk.close()

 但是上述代码额外增加了一次send和recv 数据阻塞的时间延长了

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

用struct模块可以省去这次不必要的数据发送

struct可以将各种数据类型转为byte类型  struct.pack(‘i’,num)  i模式代表int类型 转为byte 通常为4个字节

struct.pack(‘i’,num)包装    struct.unpack(‘i’,num)[0]  还原 为元组形式取第一个元素就是值

server:

 1 import struct
 2 import socket
 3 sk=socket.socket()
 4 sk.bind(('127.0.0.1',8080))
 5 sk.listen()
 6 
 7 conn,addr=sk.accept()
 8 while True:
 9     cmd=input('请输入...')
10     if cmd=='q':
11         conn.send(b'q')
12         break
13     conn.send(cmd.encode('gbk'))
14     long=conn.recv(4)
15     long=struct.unpack('i',long)[0]     #取元组的第一个元素
16 
17     res=conn.recv(long).decode('gbk')
18     print(res)
19 conn.close()
20 sk.close()

client:

 1 import struct
 2 import socket
 3 import subprocess
 4 sk=socket.socket()
 5 
 6 sk.connect(('127.0.0.1',8080))
 7 while True:
 8     cmd=sk.recv(1024).decode('gbk')
 9     if cmd=='q':
10         sk.send(b'q')
11         break
12     res=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
13     stdout=res.stdout.read()
14     stderr=res.stderr.read()
15     long=len(stdout)+len(stderr)
16     long=struct.pack('i',long)
17     sk.send(long) #告诉服务器要接收数据的长度 数字为4字节
18     
19     sk.send(stdout)
20     sk.send(stderr)
21 sk.close()

 

posted @ 2018-07-30 22:54  青红*皂了个白  阅读(217)  评论(0编辑  收藏  举报