一、 什么是粘包
1.须知:只有TCP有粘包现象,UDP永远不会粘包
2.所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
二、两种情况下会发生粘包。
1.发送数据时间间隔很短,数据了很小,会合到一起,产生粘包
1 from socket import * 2 import time 3 phone=socket(AF_INET,SOCK_STREAM) 4 phone.connect(('127.0.0.1',8080)) 5 6 phone.send('helloworld'.encode('utf-8')) 7 phone.send('egon'.encode('utf-8'))
2.接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
from socket import * import time phone=socket(AF_INET,SOCK_STREAM) phone.connect(('127.0.0.1',8080)) phone.send('helloworld'.encode('utf-8')) time.sleep(5) phone.send('egon'.encode('utf-8'))
from socket import * phone=socket(AF_INET,SOCK_STREAM) phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) phone.bind(('127.0.0.1',8080)) phone.listen(5) conn,client_addr=phone.accept() data1=conn.recv(8) print('data1: ',data1) data2=conn.recv(20) print('data2:',data2) data3=conn.recv(20) print('data3:',data3) # 结果 # data1: b'hellowor' # data2: b'ld' # data3: b'egon'
三、
拆包的发生情况
当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。
补充问题一:为何tcp是可靠传输,udp是不可靠传输
基于tcp的数据传输请参考我的另一篇文章http://www.cnblogs.com/linhaifeng/articles/5937962.html,tcp在数据传输时,发送端先把数据发送到自己的缓存中,然后协议控制将缓存中的数据发往对端,对端返回一个ack=1,发送端则清理缓存中的数据,对端返回ack=0,则重新发送数据,所以tcp是可靠的
而udp发送数据,对端是不会返回确认信息的,因此不可靠
补充问题二:send(字节流)和recv(1024)及sendall
recv里指定的1024意思是从缓存里一次拿出1024个字节的数据
send的字节流是先放入己端缓存,然后由协议控制将缓存内容发往对端,如果待发送的字节流大小大于缓存剩余空间,那么数据丢失,用sendall就会循环调用send,数据不会丢失
四、解决粘包的low比处理方法
1 import socket 2 import subprocess 3 import struct 4 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 5 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加 6 phone.bind(('127.0.0.1',8082)) #绑定手机卡 7 phone.listen(5) #开机 8 9 print('starting...') 10 while True: #链接循环 11 conn,client_addr=phone.accept() #等电话 (链接,客户的的ip和端口组成的元组) 12 print('-------->',conn,client_addr) 13 #收,发消息 14 while True:#通信循环 15 try: 16 cmd=conn.recv(1024) 17 if not cmd:break #针对linux 18 #执行cmd命令,拿到cmd的结果,结果应该是bytes类型 19 #。。。。 20 res = subprocess.Popen(cmd.decode('utf-8'), shell=True, 21 stdout=subprocess.PIPE, 22 stderr=subprocess.PIPE) 23 stdout=res.stdout.read() 24 stderr=res.stderr.read() 25 #先发报头(转成固定长度的bytes类型) 26 header = struct.pack('i',len(stdout)+len(stderr)) 27 conn.send(header) 28 #再发送命令的结果5 29 conn.send(stdout) 30 conn.send(stderr) 31 except Exception: 32 break 33 conn.close() #挂电话 34 phone.close() #关机
1 import socket 2 import struct 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 4 phone.connect(('127.0.0.1',8082)) #绑定手机卡 5 #发,收消息 6 while True: 7 cmd=input('>>: ').strip() 8 if not cmd:continue 9 phone.send(cmd.encode('utf-8')) 10 #先收报头 11 header_struct=phone.recv(4) 12 unpack_res = struct.unpack('i', header_struct) 13 total_size=unpack_res[0] 14 #再收数据 15 recv_size=0 #10241=10240+1 16 total_data=b'' 17 while recv_size < total_size: 18 recv_data=phone.recv(1024) 19 recv_size+=len(recv_data) 20 total_data+=recv_data 21 print(total_data.decode('gbk')) 22 phone.close()
五、完美解决tcp协议粘包问题
1 import socket 2 import subprocess 3 import struct 4 import json 5 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 6 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加 7 phone.bind(('127.0.0.1',8082)) #绑定手机卡 8 phone.listen(5) #开机 9 10 print('starting...') 11 while True: #链接循环 12 conn,client_addr=phone.accept() #等电话 (链接,客户的的ip和端口组成的元组) 13 print('-------->',conn,client_addr) 14 15 #收,发消息 16 while True:#通信循环 17 try: 18 cmd=conn.recv(1024) 19 if not cmd:break #针对linux 20 #执行cmd命令,拿到cmd的结果,结果应该是bytes类型 21 #。。。。 22 res = subprocess.Popen(cmd.decode('utf-8'), shell=True, 23 stdout=subprocess.PIPE, 24 stderr=subprocess.PIPE) 25 stdout=res.stdout.read() 26 stderr=res.stderr.read() 27 #制作报头 28 header_dic = { 29 'total_size': len(stdout)+len(stderr), 30 'filename': None, 31 'md5': None} 32 33 header_json = json.dumps(header_dic) 34 header_bytes = header_json.encode('utf-8') 35 #发送阶段 36 #先发报头长度 37 conn.send(struct.pack('i',len(header_bytes))) 38 #再发报头 39 conn.send(header_bytes) 40 41 #最后发送命令的结果 42 conn.send(stdout) 43 conn.send(stderr) 44 except Exception: 45 break 46 conn.close() #挂电话 47 phone.close() #关机
1 import socket 2 import struct 3 import json 4 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 5 phone.connect(('127.0.0.1',8082)) #绑定手机卡 6 7 #发,收消息 8 while True: 9 cmd=input('>>: ').strip() 10 if not cmd:continue 11 12 phone.send(cmd.encode('utf-8')) 13 #先收报头的长度 14 header_len=struct.unpack('i',phone.recv(4))[0] 15 16 #再收报头 17 header_bytes=phone.recv(header_len) 18 header_json=header_bytes.decode('utf-8') 19 header_dic=json.loads(header_json) 20 total_size=header_dic['total_size'] 21 22 #最后收数据 23 recv_size=0 #10241=10240+1 24 total_data=b'' 25 while recv_size < total_size: 26 recv_data=phone.recv(1024) 27 recv_size+=len(recv_data) 28 total_data+=recv_data 29 print(total_data.decode('gbk')) 30 phone.close()