Python学习【第17篇】:网络编程之粘包

news/2024/7/4 1:01:35

一、什么是粘包

须知:只有TCP有粘包现象,UDP永远不会粘包

粘包不一定会发生

如果发生了:1.可能是在客户端已经粘了

      2.客户端没有粘,可能是在服务端粘了

首先需要掌握一个socket收发消息的原理

应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。(因为TCP是流式协议,不知道啥时候开始,啥时候结束)。而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。怎样定义消息呢?可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方send一条信息的时候,无论底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈现在内核缓冲区。

所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

二、发生粘包的两种情况

发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会当做一个包发出去,产生粘包)

复制代码
 1 from socket import *
 2 phone = socket(AF_INET,SOCK_STREAM)
 3 phone.setsockopt(SOL_SOCKET,SOCK_STREAM,1)
 4 phone.bind(('127.0.0.1',8080))
 5 phone.listen(5)
 6 print('start running...')
 7 
 8 coon,addr = phone.accept() #等待连接
 9 
10 data1 = coon.recv(10) 11 data2 = coon.recv(10) 12 13 print('------------>',data1.decode('utf-8')) 14 print('------------>',data2.decode('utf-8')) 15 coon.close() 16 phone.close()
复制代码
复制代码
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('hello'.encode('utf-8'))
7 phone.send('helloworld'.encode('utf-8'))
8 phone.close()
复制代码

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

复制代码
 1 from socket import *
 2 phone = socket(AF_INET,SOCK_STREAM)
 3 phone.setsockopt(SOL_SOCKET,SOCK_STREAM,1)
 4 phone.bind(('127.0.0.1',8080))
 5 phone.listen(5)
 6 print('start running...')
 7 
 8 coon,addr = phone.accept() #等待连接
 9 
10 data1 = coon.recv(2) #一次没有接收完整
11 data2 = coon.recv(10)  #下一次接收的时候会先取旧的数据,然后取新的
12 # data3 = coon.recv(1024)  #接收等5秒后的信息
13 print('------------>',data1.decode('utf-8')) 14 print('------------>',data2.decode('utf-8')) 15 # print('------------>',data3.decode('utf-8')) 16 coon.close() 17 phone.close()
复制代码
复制代码
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('hello'.encode('utf-8'))
7 time.sleep(5)
8 phone.send('haiyan'.encode('utf-8')) 9 phone.close()
复制代码

三、解决粘包的方法

问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据

复制代码
 1 import socket
 2 import subprocess
 3 import struct
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
 5 phone.bind(('127.0.0.1',8080)) #绑定手机卡
 6 phone.listen(5) #阻塞的最大数
 7 print('start runing.....')
 8 while True: #链接循环
 9     coon,addr = phone.accept()# 等待接电话
10     print(coon,addr)
11     while True: #通信循环
12         # 收发消息
13         cmd = coon.recv(1024) #接收的最大数
14         print('接收的是:%s'%cmd.decode('utf-8')) 15 #处理过程 16 res = subprocess.Popen(cmd.decode('utf-8'),shell = True, 17 stdout=subprocess.PIPE, #标准输出 18 stderr=subprocess.PIPE #标准错误 19  ) 20 stdout = res.stdout.read() 21 stderr = res.stderr.read() 22 #先发报头(转成固定长度的bytes类型,那么怎么转呢?就用到了struct模块) 23 #len(stdout) + len(stderr)#统计数据的长度 24 header = struct.pack('i',len(stdout)+len(stderr))#制作报头 25  coon.send(header) 26 #再发命令的结果 27  coon.send(stdout) 28  coon.send(stderr) 29  coon.close() 30 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',8080)) #连接服
 5 while True:
 6     # 发收消息
 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 16 total_data=b'' 17 while recv_size<total_size: #循环的收 18 recv_data = phone.recv(1024) #1024只是一个最大的限制 19 recv_size+=len(recv_data) # 20 total_data+=recv_data # 21 print('返回的消息:%s'%total_data.decode('gbk')) 22 phone.close()
复制代码

四、解决粘包问题升级版:完整的解决了

复制代码
 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)
 7 phone.bind(('127.0.0.1',8080)) #绑定手机卡
 8 phone.listen(5) #阻塞的最大数
 9 print('start runing.....') 10 while True: #链接循环 11 coon,addr = phone.accept()# 等待接电话 12 print(coon,addr) 13 while True: #通信循环 14 # 收发消息 15 cmd = coon.recv(1024) #接收的最大数 16 print('接收的是:%s'%cmd.decode('utf-8')) 17 #处理过程 18 res = subprocess.Popen(cmd.decode('utf-8'),shell = True, 19 stdout=subprocess.PIPE, #标准输出 20 stderr=subprocess.PIPE #标准错误 21  ) 22 stdout = res.stdout.read() 23 stderr = res.stderr.read() 24 # 制作报头 25 header_dic = { 26 'total_size': len(stdout)+len(stderr), # 总共的大小 27 'filename': None, 28 'md5': None 29  } 30 header_json = json.dumps(header_dic) #字符串类型 31 header_bytes = header_json.encode('utf-8') #转成bytes类型(但是长度是可变的) 32 #先发报头的长度 33 coon.send(struct.pack('i',len(header_bytes))) #发送固定长度的报头 34 #再发报头 35  coon.send(header_bytes) 36 #最后发命令的结果 37  coon.send(stdout) 38  coon.send(stderr) 39  coon.close() 40 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',8080)) #连接服务器
 6 while True:
 7     # 发收消息
 8     cmd = input('请你输入命令>>:').strip() 9 if not cmd:continue 10 phone.send(cmd.encode('utf-8')) #发送 11 #先收报头的长度 12 header_len = struct.unpack('i',phone.recv(4))[0] #吧bytes类型的反解 13 #在收报头 14 header_bytes = phone.recv(header_len) #收过来的也是bytes类型 15 header_json = header_bytes.decode('utf-8') #拿到json格式的字典 16 header_dic = json.loads(header_json) #反序列化拿到字典了 17 total_size = header_dic['total_size'] #就拿到数据的总长度了 18 #最后收数据 19 recv_size = 0 20 total_data=b'' 21 while recv_size<total_size: #循环的收 22 recv_data = phone.recv(1024) #1024只是一个最大的限制 23 recv_size+=len(recv_data) #有可能接收的不是1024个字节,或许比1024多呢, 24 # 那么接收的时候就接收不全,所以还要加上接收的那个长度 25 total_data+=recv_data #最终的结果 26 print('返回的消息:%s'%total_data.decode('gbk')) 27 phone.close()
复制代码

五、struct模块

复制代码
 1 #该模块可以把一个类型,如数字,转成固定长度的bytes类型
 2 import struct
 3 res = struct.pack('i',12345)
 4 print(res,len(res),type(res))  #长度是4
 5 
 6 res2 = struct.pack('i',12345111)
 7 print(res,len(res),type(res2))  #长度也是4
 8 
 9 unpack_res =struct.unpack('i',res2)
10 print(unpack_res)  #(12345111,)
11 print(unpack_res[0]) #12345111
复制代码

 

转载于:https://www.cnblogs.com/kcwxx/p/10145249.html


http://www.niftyadmin.cn/n/4002365.html

相关文章

小作文―描写文

小作文―描写文 曲线图 数据典型&#xff0c;语言典型简洁 第一段&#xff1a;2句话&#xff0c;考官就知道图表全貌&#xff0c;考官手里没有图表 第一句&#xff1a;介绍图表&#xff0c;改写原文&#xff0c;4处&#xff0c;变成自己的话 1. below去掉; 2. 动词shows换…

Github开源安全工具集合

2019独角兽企业重金招聘Python工程师标准>>> 本仓库收集的初衷是为向各类企业安全从业人员提供在企业信息安全防护体系建设过程中可以参考的开源安全扫描工具&#xff0c;以期望企业能够利用这些扫描器对自身业务进行自检&#xff0c;从而提高自身业务安全性。集合g…

在linux服务器安装windows,在Windows操作系统中安装Linux系统

很多人对我写下面的东西已不屑一看了&#xff0c;但我写这个帖子的原因是&#xff1a;很多在Windows下想安装Linux的人不知如何开始&#xff0c;如何下手安装。等进入到安装界面又出现了很问题。不得不中途放弃。好了&#xff0c;从我做起&#xff0c;开始新手上路!我想要从win…

大作文 第一段

2011年7月9日 There are many extinct animal species in the world nowadays. Some people say we should protect these animals from dying out, while others say we should concentrate more on problems of human beings. Discuss both opinions and give your personal…

口语题目

Part 1考题总结 考题总结&#xff1a; Self-introduction and personal information What’s your name? What does it mean to you? What names are popular in your culture? What name would you like to give to your own child in future? Are you a student or …

推动新能源汽车,传统车企、科技公司、媒体成三辆马车头?

“贾老板”的话题贯穿了2018年整年&#xff0c;最近又与恒大“分手”&#xff0c;甚至还闹上了法庭。但相关报道称&#xff0c;11月29日&#xff0c;香港国际仲裁中心驳回了贾跃亭要求剥夺恒大资产抵押权的申请&#xff0c;“贾老板”的造车梦又耽搁下来了。 大洋那头的新能源汽…

linux下配置织梦系统环境,linux服务器WEB环境一键安装包及教程

lanmp/lamp/lnmp/lnamp一键安装包,快速安装包,linux服务器WEB环境一键安装包lanmp一键安装包是wdlinux官网2010年底开始推出的web应用环境的快速简易安装包.执行一个脚本&#xff0c;整个环境就安装完成就可使用&#xff0c;快速,方便易用,安全稳定lanmp一键安装包是用shell脚本…

作文顿悟

科技&#xff1a;Secondly, science and technology have long been proved as the most effective way to solve environmental problems. As is known to all, people equipped with professional knowledge can have a better understanding of the tough situation we are …