黏包:接收到的数据包乱了,有部分本次请求的数据临时存入了缓冲区,导致下次请求时一起显示
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()