博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python中网络编程中的黏包现象
阅读量:5140 次
发布时间:2019-06-13

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

黏包中的关键因素 : 缓冲区

缓冲区 :  将程序和网络解耦

缓冲区大致有输入缓冲区 和 输出缓冲区两种形式,这也是产生黏包的因素.大致的缓冲情况 如下图所示:

 

 

每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。这些I/O缓冲区特性可整理如下:1.I/O缓冲区在每个TCP套接字中单独存在;2.I/O缓冲区在创建套接字时自动生成;3.即使关闭套接字也会继续传送输出缓冲区中遗留的数据;4.关闭套接字将丢失输入缓冲区中的数据。输入输出缓冲区的默认大小一般都是 8K,可以通过 getsockopt() 函数获取:1.unsigned optVal;2.int optLen = sizeof(int);3.getsockopt(servSock, SOL_SOCKET, SO_SNDBUF,(char*)&optVal, &optLen);4.printf("Buffer length: %d\n", optVal);

 

两种黏包现象:   1.连续的小包可能会被优化算法给组合到一起进行发送   2.第一次如果发送的数据大小2000B接收端一次性接受大小为1024,这就导致剩下的内容会被下一次recv接收到,导致结果错乱   具体情况如图所示:

 

模拟黏包现象
1 #黏包现象服务端: 2 import socket 3 import subprocess 4 server = socket.socket() 5 ip_port = ('127.0.0.1',8001) 6  7 server.bind(ip_port) 8  9 server.listen()10 11 conn,addr = server.accept()12 13 while 1:14     from_client_cmd = conn.recv(1024)15 16     print(from_client_cmd.decode('utf-8'))17 18     sub_obj = subprocess.Popen(19         from_client_cmd.decode('utf-8'),20         shell=True,  #固定21         stdout=subprocess.PIPE,  #标准输出 PIPE:管道,保存着指令单额执行结果22         stderr=subprocess.PIPE   #标准错误输出23     )24 25     std_msg = sub_obj.stdout.read()26     print('指令的执行结果长度>>>>',len(std_msg))27 28     conn.send(std_msg)
#黏包客户端:import socketclient = socket.socket()client.connect(('127.0.0.1',8001))while 1:    cmd = input('请输入指令:')    client.send(cmd.encode('utf-8'))    server_cmd_result = client.recv(1024)    print(server_cmd_result.decode('gbk'))
黏包解决方案   黏包解决大致思路:问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,                 如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端发一个确认消息给发送端,                 然后发送端再发送过来后面的真实内容,接收端再来一个死循环接收完所有数据。
 
1 # 黏包解决服务端: 2 import socket 3 import subprocess 4 import struct 5 server = socket.socket() 6 ip_port = ('127.0.0.1',8001) 7  8 server.bind(ip_port) 9 10 server.listen()11 12 conn,addr = server.accept()13 14 while 1:15     from_client_cmd = conn.recv(1024)16 17     print(from_client_cmd.decode('utf-8'))18     #接收到客户端发送来的系统指令,服务端通过subprocess模块到服务端自己的系统里面执行这条指令19     sub_obj = subprocess.Popen(20         from_client_cmd.decode('utf-8'),21         shell=True,22         stdout=subprocess.PIPE,  #正确结果的存放位置23         stderr=subprocess.PIPE   #错误结果的存放位置24     )25     #从管道里面拿出结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果26     std_msg = sub_obj.stdout.read()27 28     #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据29     std_msg_len = len(std_msg)30 31     print('指令的执行结果长度>>>>',len(std_msg))32 33     msg_lenint_struct = struct.pack('i',std_msg_len)34 35     conn.send(msg_lenint_struct+std_msg)
 
1 #黏包解决方案客户端: 2 import socket 3 import struct 4 client = socket.socket() 5 client.connect(('127.0.0.1',8001)) 6  7 while 1: 8     cmd = input('请输入指令:') 9     #发送指令10     client.send(cmd.encode('utf-8'))11 12     #接收数据长度,首先接收4个字节长度的数据,因为这个4个字节是长度13     server_res_len = client.recv(4)14     msg_len = struct.unpack('i',server_res_len)[0]15 16     print('来自服务端的消息长度',msg_len)17     #通过解包出来的长度,来接收后面的真实数据18     server_cmd_result = client.recv(msg_len)19 20     print(server_cmd_result.decode('gbk'))

 今天的分享就到此暂告一个段落,有兴趣的小伙伴多多交流,有错误不足之处请提出见解,一起学习纠错

 

转载于:https://www.cnblogs.com/Godisgirl/p/10222866.html

你可能感兴趣的文章
过滤器(Filter)
查看>>
字符串的操作
查看>>
性能优化之Java(Android)代码优化
查看>>
springMVC相关—文件上传
查看>>
由Oracle 11g SYSAUX 和 SYSTEM 表空间回收引发的联想
查看>>
uva 1416 Warfare And Logistics
查看>>
欲则不达
查看>>
盒子游戏
查看>>
OpenJudgeP1.10.08:病人排队__(刷题)_水题
查看>>
观察者模式
查看>>
Hadoop分布式文件系统中架构和设计要点汇总
查看>>
cout和printf
查看>>
UVa 10088 - Trees on My Island (pick定理)
查看>>
#C++PrimerPlus# Chapter11_Exersice4_mytimeV4
查看>>
iOS8 针对开发者所拥有的新特性汇总如下
查看>>
Jmeter + Grafana搭建实时监控可视化
查看>>
uCGUI字符串显示过程分析和uCGUI字库的组建
查看>>
h5唤起app
查看>>
SQL Server 2008 /SQL Server 2008 R2 配置数据库邮件
查看>>
[转]vs2010编译金山代码
查看>>