第六章 - 网络编程 - 粘包

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()

原文地址:https://www.cnblogs.com/mumupa0824/p/9016266.html

时间: 2024-07-31 23:53:49

第六章 - 网络编程 - 粘包的相关文章

第六章|网络编程-socket开发

1.计算机基础 作为应用开发程序员,我们开发的软件都是应用软件,而应用软件必须运行于操作系统之上,操作系统则运行于硬件之上,应用软件是无法直接操作硬件的,应用软件对硬件的操作必须调用操作系统的接口,由操作系统操控硬件. 比如客户端软件想要基于网络发送一条消息给服务端软件,流程是: 1.客户端软件产生数据,存放于客户端软件的内存中,然后调用接口将自己内存中的数据发送/拷贝给操作系统内存 2.客户端操作系统收到数据后,按照客户端软件指定的规则(即协议).调用网卡发送数据 3.网络传输数据 4.服务端

第六章 网络编程-SOCKET开发——续2

6.5--粘包现象与解决方案 简单远程执行命令程序开发 是时候用户socket干点正事呀,我们来写一个远程执行命令的程序,写一个socket client端在windows端发送指令,一个socket server在Linux端执行命令并返回结果给客户端 执行命令的话,肯定是用我们学过的subprocess模块啦,但注意注意注意: res=subprocess.Popen(cmd.deocde('utf-8'),shell=subprocess.PIPE,stdout=subprocess.PI

python之网络编程粘包

一.粘包 粘包现象 # 服务端 import socket import subprocess phone = socket.socket() phone.bind(('127.0.0.1',8888)) phone.listen(5) while 1: conn,addr = phone.accept() while 1: cmd = conn.recv(1024) ret = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=s

网络编程粘包问题

首先说为什么会粘包,在py2上运行时,服务器把两次发送的操作强制的合成一次发送给客户端,所以 粘在一起了,因为python3的版本是可以的,但是有的时候python3也会出现粘包现象. 解决粘包的问题有两种方法: 1  可以先sleep一下,这个样子就可以使缓冲区超时,就不在等下一次的了,这样就可以和下一条命令隔离开了 2  我在服务端来一个等待客户端确认,就ok了,这个确认不需要我们用户输入,而是客户端自动的给你来这个响应,就是说,客户端自动的写好代码,自动的给服务器一个响应,只要收到服务端的

20 网络编程 粘包现象与解决方案

1.https://www.cnblogs.com/guobaoyuan/p/6809447.html 发送数据包前 对包的长度进行计算 1. 比较low的方法是 len( package) 然后直接发送给接收端.这样会出现一个问题,就是接收端不知道你的这个 len(package)是几个字节,就也有可能会出现粘包问题. 2. 利用struct对包的长度打包为固定4个字节或8个字节. 3. struct.pack format参数为"i" 时只能打包长度为10的数字,那么还可以先将 长

六、网络编程(socket编程)

一.客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网处处是C/S架构 比如百度网站是服务端,浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务端提供微信服务,需要下载微信安装包安装使用才可以去聊微信. C/S架构与socket的关系: 用socket就是为了完成C/S架构的开发 server端(必须遵守的): 1.位置必须固定死,绑定一个固定的地址 2.对外一直提供服务,稳定运行 3.支持并发(让多个客户端感觉是同时被服务着) 二.OSI七层 1.引子 须

[CSAPP笔记][第十一章网络编程]

第十一章 网络编程 我们需要理解基本的客户端-服务端编程模型,以及如何编写使用因特网提供的服务的客户端-服务端程序. 最后,我们将把所有这些概念结合起来,开发一个小的但功能齐全的Web服务器,能够为真实的Web浏览器提供静态的和动态的文本和图形内容. 11.1 客户端 - 服务器编程模型 每个网络应用程序都是基于客户端 - 服务器模型的 采用这种模型,一个应用是由一个服务器进程 和一个或多个客户端进程组成. 服务器管理某种资源,并且通过操作这种资源为它的客户端提供某种服务. WEB服务器,代表客

第13章 网络编程

1 /***************** 2 ***第13章 网络编程 3 *******知识点: 4 **************1.基本概念 5 ******************1.1 网络OSI模型 6 ******************1.2 IP地址 7 ******************1.3 端口地址 8 ******************1.4 通讯协议 9 **************2.Java网络相关类 10 ******************2.1 URLDe

java语言的科学与艺术 第六章 课后编程

欢迎大家转载,为保留作者成果,转载请注明出处,http://blog.csdn.net/netluoriver,有些文件在资源中也可以下载!如果你没有积分,可以联系我索要! 1. package SixthCharter; /* * File: Poker.java * --------------------------- * 这是第6章的第一题 * Author luoriver */ import acm.program.*; import acm.util.*; public class