博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python3.6入门到高阶(全栈) day28----黏包处理
阅读量:7024 次
发布时间:2019-06-28

本文共 4485 字,大约阅读时间需要 14 分钟。

1、缓冲区和subprocess模块

  1.1  缓冲区( 当send()内容超过输入缓冲区大小或recv()接收内容超过输出缓冲区大小时旧版本(py3.5以前)是会直接报错的, py3.5以后如果出错内部机制会直接处理错误, 处理方式类似于sendall()的方式循环发送去缓存区. )

    

 

 

  每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。

  write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。

  TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。

  read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。

  2.2  解释器调用系统指令----> subprocess

1 import subprocess      # 解释器操作cmd的模块 2 recv_msg = input("输入cmd命令: ") 3 cmd_obj = subprocess.Popen( 4     recv_msg,       # 输入的cmd指令,"dir", "ipconfig" ...... 5     shell=True,     # shell = True, 即相当于使用cmd窗口 6     stdout=subprocess.PIPE,      # 标准输出信息都在PIPE管道中 7     stderr=subprocess.PIPE,      # 出错信息也在PIPE管道中 8 ) 9 cmd_msg = cmd_obj.stdout.read().decode("gbk")        # 对象调用里面的stdout属性, 并读取出来.(和计算机交互一般都以GBK编码解码)   10 print(cmd_msg)
subprocess模块

2、黏包

  产生黏包的原因? 主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

  现象1: 连续send两个比较小点的数据时, 在网速各方面都很流畅情况下是不会产生黏包现象的(用time.sleep模拟), 当网速或其他原因造成上传速度变慢, TCP内部的Nagle算法和延迟ACK机制, 会减少大量小包的连续发送, 所以会将比较小的连续发送的数据合并成一个比较大的包, 这是第一种黏包;

  现象2: send()给缓冲区扔了太多数据, 超过缓冲区的大小就会报错, 每个IP包一般最大为1500b, 如果超过这个值就会被拆包发送, 第一次剩下的数据有可能和第二次发送的数据一起连在一起被接收, 这就是第二种黏包;

3、如何解决黏包现象?

  3.1 了解一下struct模块 ----> 其实就是定义一种结构,里面包含不同类型的数据(int,char,bool等等),方便对某一结构对象进行处理。该模块的主要作用就是对python基本类型值与一个四位二进制数类型间的转化.

  struct模块支持打包哪些数据类型?

  struct.pack()  可以将struct支持的数据类型打包成一个对应长度二进制的数, 如果给 "int" 类型的数字打包的话, 结果就是一个4位二进制的数. 

  struct.unpack()  将打包成的二进制数解包回来, 结果是一个元组. 

1 import struct2 numb = 1528569003 numb_bytes = struct.pack("i",numb)      # 把int 类型的numb打包成一个4位二进制的数(不管多大多小的数转化后的结果都是4位),4                                         # "i"表示是一个整数  b'Di\x1c\t'5 print(numb_bytes)6 7 numb = struct.unpack("i",numb_bytes)    # 结果是一个元组形式. (152856900,)8 print(numb)
struct模块

如何解决黏包现象?   ---->  让接收方知道自己要接收的字节流总长度, 然后按这个长度接收就行.

    第一种方案: 发送方先把字节流的总长度发给接收方, 接收方在接到总长度后给发送方传达一个确认信息, 发送方开始传数据, 接收方循环接收数据, 直到接受到的字节总长度等于发送方发来的字节流总长度.

1 import socket 2 import subprocess 3  4  5 # 服务端(发送方) 6 server = socket.socket() 7 ip_port = ("192.168.12.42",8520) 8 server.bind(ip_port) 9 server.listen(4)10 conn,addr = server.accept()11 12 while 1:13     recv_msg = str(conn.recv(1024),"utf8")14     cmd_obj = subprocess.Popen(15         recv_msg,16         shell=True,17         stdout=subprocess.PIPE,18         stderr=subprocess.PIPE,19     )20     cmd_msg = cmd_obj.stdout.read() * 2021     cmd_msg_len = bytes(str(len(cmd_msg)),"utf8")22     conn.send(cmd_msg_len)              # 先发送一个总的字节长度23     has_send = 024     while has_send < len(cmd_msg):      # 发送方循环发送25         every_send = cmd_msg[has_send:has_send + 1024]26         conn.send(every_send)27         has_send += len(every_send)28 29 30 # 客户端(接收方)31 client = socket.socket()32 ip_port = ("192.168.12.42",8520)33 client.connect(ip_port)34 35 while 1:36     cmd_msg = bytes(input("请输入cmd指令: "),"utf8")37     client.send(cmd_msg)38     recv_msg_len = int(str(client.recv(1024),"utf8"))39     print(recv_msg_len)40     has_recv = bytes()              # 接收到总的字节流长度41     while len(has_recv) < recv_msg_len:         # 循环接收,直到字节流的长度相等42         has_recv += client.recv(1024)43     print(str(has_recv,"gbk"))44     print(len(has_recv))
循环接收方案

第二种方案: 通过struck模块将需要发送的内容的长度进行打包,打包成一个4字节长度的数据发送到对端,对端只要取出前4个字节,然后对这四个字节的数据进行解包,拿到你要发送的内容的长度,然后通过这个长度来继续接收我们实际要发送的内容(不好理解看代码, 简单)。

1 import socket 2 import subprocess 3 import struct 4  5  6 # 客户端(发送方) 7 server = socket.socket() 8 ip_port = ("192.168.12.42",8520) 9 server.bind(ip_port)10 server.listen(4)11 conn,addr = server.accept()12 13 while 1:14     recv_msg = str(conn.recv(1024),"utf8")15     cmd_obj = subprocess.Popen(16         recv_msg,17         shell=True,18         stdout=subprocess.PIPE,19         stderr=subprocess.PIPE,20     )21     cmd_msg = cmd_obj.stdout.read()22     numb_b = struct.pack("i",len(cmd_msg))23     conn.send(numb_b + cmd_msg)        # 把   总字节流长度 + 字节内容  发给接收方,接收方接收前4个字节拿到总字节长度, 24                                        #  然后按照总字节流长度来接收.25 26 27 # 客户端(接收方)28 import socket29 import struct30 31 client = socket.socket()32 ip_port = ("192.168.12.42",8520)33 client.connect(ip_port)34 35 while 1:36     cmd_msg = bytes(input("请输入cmd指令: "),"utf8")37     client.send(cmd_msg)38     numb_b = client.recv(4)39     numb_len = struct.unpack("i",numb_b)[0]40     msg = client.recv(numb_len)41     print(str(msg,"gbk"))
struct方案

 

转载于:https://www.cnblogs.com/wanxiangai/p/10045836.html

你可能感兴趣的文章
docker安装
查看>>
mybatis由浅入深day01_4入门程序_4.6根据用户id(主键)查询用户信息
查看>>
HPC Linux
查看>>
JDBC 插入时间字段的值
查看>>
计蒜客 疑似病毒 (AC自动机 + 可达矩阵)
查看>>
QoS优先级详解
查看>>
【ACE】恩墨《访客》第一期嘉宾-刘盛-中国Oracle数据库领域首位ACEA
查看>>
oracle等待事件1分别用表和索引上数据的访问来产生db file scattered read等待事件...
查看>>
腾讯区块链+医疗,一场值得期待的卫生行业创新探索
查看>>
CentOS 7之Postfix部署系列 (二) CentOS网络设置
查看>>
30K 月薪运维工程师面试考什么?滴滴17年春招笔试题
查看>>
给力!新书面市:软考45分采分点梳理与难点突破——系统集成项目管理工程师...
查看>>
0成本日涨粉1000+,新媒体小白也能实操的引流方法
查看>>
权威公布:百度搜索网页标题规范
查看>>
《微软Azure云计算开发实战(2):Azure部署ASP.NET MVC 网站
查看>>
linux系统被×××后处理经历
查看>>
思科路由器开机进入 miniIOS 的原因分析
查看>>
分享ISTQB培训体验
查看>>
Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(3)...
查看>>
自动化部署工具PUPPET介绍
查看>>