第六章-网络编程-粘包(代码片段)

mumupa0824 mumupa0824     2022-11-11     506

关键词:

1.粘包:
多个包 多个命令的结果 粘到一起了 因为recv 1024限制了 导致的结果
参考:http://www.cnblogs.com/linhaifeng/articles/6129246.html

粘包底层原理分析:
1.运行一个软件 和 哪几个硬件 有关
硬盘 内存 cpu
2.启动程序:
硬盘程序 加载到 内存 启一个软件就占一个内存空间
os 就有一个内存空间
os的内存空间 和 软件的内存空间 彼此互相隔离

须知:只有TCP有粘包现象,UDP永远不会粘包。
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。


3.send recv 底层原理
send 应用程序的代码 把自己的数据 发出去 存放到自己的内存空间里
发到os的内存里调网卡发数据
程序的内存和os的内存两个内存互相隔离
数据copy给os的内存
send 发给了 自己的os的内存 os会照着tcp协议去发
recv 通知os 去调网卡 收数据
1.send发到数据到服务端os的内存 # 慢
2.os的内存 copy 给程序 # 快
站在应用程序角度上:
send: 1.数据发给本地的os # 耗时短一些
recv:1.通知os收数据 2.os内存数据copy到应用程序内存中 # 耗时长一些

4.send recv 总结:
1:不管是recv还是send都不是直接接收对方的数据,而是操作自己的操作系统内存--->不是一个send对应一个recv # 一发可以对应多收 一收对应多发
2.:recv:
wait data 耗时非常长
copy data
send:
copy data
3.数据量比较小 时间间隔比较短 就合并成一个包 再发
使用了优化方法(Nagle算法)

5.recv 有关部门建议的不要超过 8192,再大反而会出现影响收发速度和不稳定的情况

服务端
 1 \'\'\'
 2 解决粘包的办法:
 3     明确知道对方给我发的包的长度
 4 \'\'\'
 5 import time
 6 import socket
 7 server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 8 server.bind((\'127.0.0.1\',8080))
 9 server.listen(5)
10 print(\'strating...\')
11 
12 conn,addr=server.accept()
13 
14 # 第一次接收5
15 # res1 = conn.recv(2)
16 # res2 = conn.recv(2)
17 res1 = conn.recv(5)
18 print(\'第一次:\',res1)
19 time.sleep(6)
20 #第二次接收
21 res3 = conn.recv(6).decode(\'utf-8\')
22 print(\'第二次:\',res3)
23 
24 \'\'\'
25 第一次: b\'helloworld\'
26 第二次: b\'\'
27 \'\'\'
28 
29 \'\'\'
30 第一次: b\'hello\'
31 第二次: b\'world\'
32 \'\'\'
33 
34 \'\'\'
35 第一次: b\'h\'
36 第二次: b\'ello\' 
37 \'\'\'
38 
39 \'\'\'
40 第一次: b\'h\'
41 第二次: b\'elloworld\'  # 客户端粘了 服务端粘了
42 \'\'\'

客户端
 1 \'\'\'
 2 粘包不一定会发生:
 3     数据量比较小 时间比较短 才会发生粘包
 4 
 5 粘包是tcp协议底层优化算法决定的!(Nagle算法)
 6 \'\'\'
 7 import socket
 8 import time
 9 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
10 client.connect((\'127.0.0.1\',8080))
11 
12 client.send(\'hello\'.encode(\'utf-8\'))
13 time.sleep(5) # 解决了粘包 low
14 client.send(\'我们\'.encode(\'utf-8\'))
15 # 两个粘成一个包
2.简单版 - 解决粘包

strcut 模块的使用
 1 import struct
 2 import json
 3 res = struct.pack(\'i\',429496)
 4 print(res,type(res),len(res))  # 数字转成了 bytes 型
 5 # b\'\\xb8\\x8d\\x06\\x00\' <class \'bytes\'> 4
 6 
 7 # client.recv(4)
 8 obj = struct.unpack(\'i\',res)
 9 print(obj)
10 
11 # res = struct.pack(\'i\',1231213123123)  # 数据 不合理  若是发一个文件的话 就有可能 很大
12 
13 # q Q d 是8位  i l L 是4位
14 res = struct.pack(\'d\',120000223232123123123121231)
15 print(res,type(res),len(res))
16 # b\'\\x00\\x00\\x00\' <class \'bytes\'> 4
参考: http://blog.csdn.net/w83761456/article/details/21171085
http://www.cnblogs.com/linhaifeng/articles/6129246.html

 1 header_dic = 
 2     \'filename\': \'a.txt\',
 3     \'md5\': \'xxxxxxx\',
 4     \'total_size\': 1231231321321232132131232321321321321323221231312123123213213
 5 
 6 header_json = json.dumps(header_dic)
 7 print(header_json,type(header_json))
 8 header_bytes = header_json.encode(\'utf-8\')
 9 print(header_bytes,type(header_bytes))
10 print(len(header_bytes))  # 会变
11 
12 res = struct.pack(\'i\',len(header_bytes))  # 固定长度
13 print(res,len(res))
14 data = struct.unpack(\'i\',res)
15 print(data)

服务端
 1 import subprocess
 2 import socket
 3 import struct
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 重用ip和端口 任然存在4次挥手的状态 解决办法
 6 phone.bind((\'127.0.0.1\',8080))
 7 phone.listen(0)
 8 print(\'strating...\')
 9 while True:
10     conn,client_addr = phone.accept()
11     print(client_addr)
12 
13     while True:
14         try:
15             # 1.收命令
16             cmd = conn.recv(8096) # 8096 一次接收完  # 系统规定不能超过8096个
17             if not cmd:break
18             print(\'客户端数据:\',cmd)
19 
20             # 2.执行命令,拿到结果
21             obj = subprocess.Popen(cmd.decode(\'utf-8\'), shell=True, # 客户端用 utf-8发的
22                                    stdout=subprocess.PIPE,
23                                    stderr=subprocess.PIPE)
24             stdout = obj.stdout.read()
25             stderr = obj.stderr.read()
26 
27             # 3.把命令的结果返回给客户端
28             # 第一步:制作固定长度的报头
29             print(len(stdout) + len(stderr))
30             total_size = len(stdout) + len(stderr)
31             header = struct.pack(\'i\',total_size)
32             # 第二部:把报头发给客户端
33             conn.send(header)
34             # 第三步:再发真实的数据
35             # conn.send(stdout+stderr) # 有效率问题的 这里 之后 可以优化
36             conn.send(stdout)
37             conn.send(stderr)  # 这样会自动粘包 比 + 号的效率高
38         except ConnectionResetError:
39             break
40     conn.close()
41 
42 phone.close()

客户端
 1 \'\'\'
 2 粘包现象 解决了 仔细想想 是有问题的 报头里面 应该 包含对真实数据的描述 就不能这样了
 3 \'\'\'
 4 import socket
 5 import struct
 6 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 7 phone.connect((\'127.0.0.1\',8080))
 8 while True:
 9     # 1.发命令
10     cmd = input(\'msg>>>:\').strip()  # dir ls
11     if not cmd:continue
12     phone.send(cmd.encode(\'utf-8\'))
13 
14     # 2.拿到命令的结果,并打印  8096 再大 就没 意义了
15     # 第一步 先收报头 收到有用的信息
16     obj = phone.recv(4)
17     # total_size = 10241
18     # 第二步:从报头中解析出对真实数据的描述 数据的长度
19     total_size = struct.unpack(\'i\', obj)[0]  # i 表示 整数
20     # data = phone.recv(1024)   # 这里是个坑 有可能会大于1024 接收数据量的最大限制
21     # 第三步:接收真实的数据
22     # data = phone.recv(526)   # 这里是个坑 从自己的os的内存里 拿数据 不可能无限大 所以 那个数字不可能无限大
23     # data=phone.recv(526)
24     # data=phone.recv(526)
25     recv_size = 0
26     recv_data = b\'\'
27     while recv_size < total_size:
28         res = phone.recv(526)
29         recv_data += res
30         recv_size+=len(res)
31 
32     print(recv_data.decode(\'gbk\'))
33 
34     # print(data.decode(\'gbk\'))   # linux:utf-8  windows:gbk
35 
36 phone.close()

3.终极版 - 解决粘包
服务端
 1 import subprocess
 2 import socket
 3 import struct
 4 import json
 5 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 6 phone.bind((\'127.0.0.1\',8080))
 7 phone.listen(5)
 8 print(\'strating...\')
 9 while True:
10     conn,client_addr = phone.accept()
11     print(client_addr)
12 
13     while True:
14         try:
15             # 1.收命令
16             cmd = conn.recv(8096) # 8096 一次接收完
17             if not cmd:break
18             print(\'客户端数据:\',cmd)
19 
20             # 2.执行命令,拿到结果
21             obj = subprocess.Popen(cmd.decode(\'utf-8\'), shell=True, # 客户端用 utf-8发的
22                                    stdout=subprocess.PIPE,
23                                    stderr=subprocess.PIPE)
24             stdout = obj.stdout.read()
25             stderr = obj.stderr.read()
26 
27             # 3.把命令的结果返回给客户端
28 
29             # 第一步:制作固定长度的报头  # 将字典转成 str 转成 bytes  用到了序列化
30             header_dic = 
31                 \'filename\':\'a.txt\',
32                 \'md5\':\'xxxxxxx\',
33                 \'total_size\': len(stdout)+len(stderr)
34             
35             header_json = json.dumps(header_dic)
36             header_bytes = header_json.encode(\'utf-8\')   # 这里不知道 多长 会粘包!!
37 
38             # 第二步先发送报头的长度
39             conn.send(struct.pack(\'i\',len(header_bytes)))
40 
41             # 第三步:再发报头
42             conn.send(header_bytes)
43 
44             # 第四部:在发真实的数据
45             conn.send(stdout)
46             conn.send(stderr)  # 这样会自动粘包 比 + 号的效率高
47         except ConnectionResetError:
48             break
49     conn.close()
50 
51 phone.close()

客户端
 1 \'\'\'
 2 思路:
 3     1.处理报头 准备发送的字典 先发报头的长度 再收报头 得到数据的长度 在收数据
 4     做字典 能容纳 很多信息量
 5     解决了:1.报头信息量少 2.i 格式有限的 解决了
 6 \'\'\'
 7 import json
 8 import socket
 9 import struct
10 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
11 phone.connect((\'127.0.0.1\',8080))
12 while True:
13     # 1.发命令
14     cmd = input(\'msg>>>:\').strip()  # dir ls
15     if not cmd:continue
16     phone.send(cmd.encode(\'utf-8\'))
17 
18     # 2.拿到命令的结果,并打印
19     # 第一步 先收报头的长度
20     obj = phone.recv(4)
21     header_size = struct.unpack(\'i\',obj)[0]
22 
23     # 第二步:在收报头
24     header_bytes = phone.recv(header_size)
25 
26     # 第三步:从报头中解析出对真实数据的描述
27     header_json = header_bytes.decode(\'utf-8\')
28     header_dic = json.loads(header_json)
29     print(header_dic)
30     total_size = header_dic[\'total_size\']
31 
32     # 第四步:接收真实的数据
33     recv_size = 0
34     recv_data = b\'\'
35     while recv_size < total_size:
36         res = phone.recv(526)
37         recv_data += res
38         recv_size+=len(res)
39 
40     print(recv_data.decode(\'gbk\'))  # linux:utf-8  windows:gbk
41 
42 
43 phone.close()

 

第六章网络编程-socket开发(代码片段)

6.1C/S架构介绍6.2TCP/IP各层详解6.3Socket介绍6.4Socket代码实例6.5粘包现象与解决方案6.6通过socket发送文件6.7本章总结6.1C/S架构介绍什么是C/S架构C指的是client(客户端软件),S指的是Server(服务端软件),本章的重点就是教大家写一... 查看详情

cprimerplus(第六版)第六章编程练习答案(代码片段)

前言:由于不是太复杂,第六章的程序我就全部集成在一个程序了,仅供参考,新手勿喷。(绷不住了从这章开始没有全部打在同一个里面了,第一次上传没有优化内容(后续优化,毕竟我都懒得上传wp~~~CH06... 查看详情

第六章|网络编程-socket开发(代码片段)

1、计算机基础作为应用开发程序员,我们开发的软件都是应用软件,而应用软件必须运行于操作系统之上,操作系统则运行于硬件之上,应用软件是无法直接操作硬件的,应用软件对硬件的操作必须调用操作系统的接口,由操... 查看详情

learningspark中文版--第六章--spark高级编程(代码片段)

WorkingonaPer-PartitionBasis(基于分区的操作)以每个分区为基础处理数据使我们可以避免为每个数据项重做配置工作。如打开数据库连接或者创建随机数生成器这样的操作,我们希望避免为每个元素重做配置工作。Spark有分区版本的... 查看详情

计算机网络第六章:应用层(代码片段)

§6.1网络应用模型一、应用层概述1.应用层的目的:  应用层对应用程序的通信提供服务。2.应用层协议的定义:  ①应用进程交换的报文类型,请求还是响应?  ②各种报文类型的语法,如报文中的各个字段... 查看详情

《网络安全技术原理与实践》第六章缓冲区溢出攻击-课本实验(代码片段)

...;垃圾水文,笑一下就好《网络安全技术原理与实践》第六章缓冲区溢出攻击-课本实验案例代码先来看书上的代码#include<stdio.h>#include<string.h>#include<windows.h>#defineTEMP_BUFF_LEN8i 查看详情

第六章第二十四题(显示当前日期和时间)(displaycurrentdateandtime)-编程练习题答案(代码片段)

**6.24(显示当前日期和时间)程序清单2-7显示当前时间。改进这个例子,显示当前的日期和时间。程序清单6-12中日历例子可以提供一些如何求年、月和日的思路。**6.24(Displaycurrentdateandtime)Listing2.7,ShowCurrentTime.java,displaysthecurrenttime.... 查看详情

docker|第六章:构建私有仓库(代码片段)

前言上一章节,讲解了利用Dockerfile和commit进行自定义镜像的构建。大部分时候,公司运维或者实施部门在构建了符合公司业务的镜像环境后,一般上不会上传到公共资源库的。这就需要自己搭建一个私有仓库,来存放自己的镜... 查看详情

《集体智慧编程》代码勘误:第六章

一:勘误classifier类中:    deffprob(self,f,cat): ifself.catcount(cat)==0: return0 #notice:remberchangeinttodoubleorfloat #+0.0or*1.0isok,otherwise,mayget0. returnself.fcount(f,cat)*1.0/ 查看详情

第六章学习小结(代码片段)

一、本章内容小结  本章节主要讲解的是图这种数据结构,其中包括图的定义、存储结构,也有图的遍历方法和图的应用。相对于前面的知识而言,我觉得这一章是最难上手的一个章节。  图是由顶点和连接顶点的边构成的... 查看详情

第六章上机2(代码片段)

usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespaceCarRunpublicclassVehicle//带参构造函数publicVehicle(stringplace,stringtype)this.Place=pl 查看详情

计算机网络实验(华为ensp模拟器)——第六章密码模式和aaa模式(代码片段)

目录一、用户级别三、用户页面四、用户页面的命令1、Console用户界面2、虚拟类型终端VTY用户界面(远程登陆)五、用户界面的用户认证1、password模式(1)进入Console用户界面(2)设置模式(3)配... 查看详情

第六章函数和宏定义实验(代码片段)

C程序设计实验报告实验项目:6.4.2、模块化程序设计利用复化梯形公式计算定积分计算Ackerman函数6.4.3函数的递归调用编程计算x的y次幂的递归函数getpower(intx,inty),并在主程序中实现输入输出编写计算学生年龄的递归函数编写递... 查看详情

第六章(代码片段)

 阅读目录面向过程vs面向对象初识面向对象  类的相关知识  对象的相关知识  对象之间的交互  类命名空间与对象、实例的命名空间  类的组合用法  初识面向对象小结面向对象的三大特性  继承  多态... 查看详情

数据结构第六章学习总结(代码片段)

一、第六章内容小结本章内容思维导图 1.邻接矩阵储存1#defineMVNum100//最大顶点数2typedefcharVerTexType;//假设顶点的数据类型为字符型3typedefintArcType;//假设边的权值类型为整型45typedefstruct67VerTexTypevexs[MVNum];//顶点表8ArcTypearcs[MVNum][MV... 查看详情

第六章—字典(代码片段)

6-1人:使用一个字典来存储一个熟人的信息,包括名、姓、年龄和居住的城市。该字典应包含键first_name、last_name、age和city。将存储在该字典中的每项信息都打印出来。 1messages=‘first_name‘:‘peter‘,‘last_name‘:‘Smith‘,‘age... 查看详情

第六章第二十四题(显示当前日期和时间)(displaycurrentdateandtime)-编程练习题答案(代码片段)

**6.24(显示当前日期和时间)程序清单2-7显示当前时间。改进这个例子,显示当前的日期和时间。程序清单6-12中日历例子可以提供一些如何求年、月和日的思路。**6.24(Displaycurrentdateandtime)Listing2.7,ShowCurrentTime.java,displaysthecurrenttime.... 查看详情

第六章django实例(代码片段)

1.出版社管理1.1展示1.创建数据库#打开cmd窗口,在命令行,mysql中createdatabasedj_bookmanager2.settings.pyBASE_DIR:项目根目录debug=True(开发)/False(上线)INSTALL_APPS:注册appMIDDLEWARW:注释掉csrf校验TEMPLATES:模版文件目录DATABASES:配置mysql数据... 查看详情