黏包及解决方法

!!!!!!只有TCP有粘包现象,UDP永远不会粘包

什么是黏包?

  接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

黏包问题的产生原因:

      接收方 不知道对方发了多少数据
      而TCP 会把所有收到的数据 拼接到一起 放到系统缓存中
      UDP 不会黏包 因其实基于数据包发送数据
黏包问题的解决方法:

   发送方 先发送数据长度  接收方先接收长度  长度必须固定字节
      自定义报头  不仅可以传输 数据长度 还能添加任意额外信息(报头格式建议使用json 可以跨平台)

 1 import  socket
 2 import subprocess
 3 import struct
 4 server = socket.socket()
 5 server.bind(("127.0.0.1",9090))
 6 server.listen()
 7 
 8 while True:
 9     client,addr = server.accept()
10     while True:
11         try:
12             # 接收命令
13             cmd = client.recv(1024).decode("utf-8")
14             p = subprocess.Popen(cmd,shell=True,stdout=-1,stderr=-1)
15             # data与err_data 都是采用的系统编码 windows是GBK
16             data = p.stdout.read()
17             err_data = p.stderr.read()
18 
19             print("数据长度:%s" % (len(data)   + len(err_data)))
20 
21             # 先发送长度给客户端
22             length = len(data)   + len(err_data)
23 
24             len_data = struct.pack("i",length)
25             # 先发送长度 在发真实数据 有可能 长度数据和真实数据黏在一起 而接收方不知道长度数据的字节数 导致黏包
26             # 解决的方案就是 长度信息占的字节数固定死  整数 转成一个固定长度字节
27 
28             client.send(len_data)
29 
30             client.send(data)
31             client.send(err_data)
32         except ConnectionResetError:
33             client.close()
34             print("连接中断......")
35             break
黏包问题的基础解决方案(服务端)
 1 import socket
 2 import struct
 3 
 4 c = socket.socket()
 5 c.connect(("127.0.0.1",9090))
 6 while True:
 7     cmd = input(">>:").strip()
 8 
 9     c.send(cmd.encode("utf-8"))
10 
11     # 先接收长度  长度固定为4个字节
12     length = c.recv(4)
13     len_data = struct.unpack("i",length)[0] # 转换为整型
14     print("数据长度为%s" % len_data)
15 
16     all_data = b"" # 存储已接收数据
17     rcv_size = 0 # 已接收长度
18     # 接收真实数据
19     # 循环接收 直到 接收到的长度等于总长度
20     while  rcv_size < len_data:
21         data = c.recv(1024)
22         rcv_size += len(data)
23         all_data += data
24 
25     print("接收长度%s" % rcv_size)
26     print(all_data.decode("gbk"))
黏包问题的基础解决方案(客户端)

struct 模块的使用

  struct 可以将python中的数据类型转换为bytes类型

 1 num = 1024
 2 
 3 import struct
 4 
 5 # 该函数 将一个python的数据转成bytes   
 6 # 第一个参数通常是i 其能转换的数据范围是c语言的int范围
 7 # 如果int不够 那就用q  表示long long 型(具体可参照官方文档,一般情况下i已经足够使用)
 8 res = struct.pack("i",num)
 9 print(res)
10 print(len(res))
struct数据转换
1 num = 1024
2 
3 import struct
4 
5 res = struct.pack("i",num)
6 # 从字节转回整型
7 res2 = struct.unpack("i",res)
8 print(res2[0])
struct数据类型回转

黏包问题高级解决方案

 1 import socket
 2 import subprocess
 3 import datetime
 4 import json
 5 import struct
 6 
 7 # 创建对象
 8 soc = socket.socket()
 9 
10 # 绑定ip 和 端口
11 soc.bind(('127.0.0.1', 8888))
12 
13 # 监听
14 soc.listen()
15 
16 while True:
17     client, addr = soc.accept()
18     while True:
19         try:
20             # 接收命令
21             cmd = client.recv(1024).decode('utf-8')
22             p = subprocess.Popen(
23                                 cmd,
24                                 shell=True,
25                                 stdout=subprocess.PIPE,
26                                 stderr=subprocess.PIPE
27             )
28             # data 与err_data 都是采用系统编码 Windows 采用'GBK'
29             data = p.stdout.read()
30             err_data = p.stderr.read()
31 
32             # 计算真实数据长度
33             length = len(data) + len(err_data)
34 
35             t = {}
36             t['time'] = str(datetime.datetime.now())
37             t['size'] = length
38 
39             # 得到json格式字符串
40             t_json = json.dumps(t)
41             # 将json转换为字节
42             t_data = t_json.encode('utf-8')
43             t_length = struct.pack('i', len(t_data))
44 
45             # 发送额外信息长度
46             client.send(t_length)
47             # 发送额外信息
48             client.send(t_data)
49 
50             # 发送真实数据
51             client.send(data)
52             client.send(err_data)
53 
54         except ConnectionRefusedError:
55             client.close()
56             print('链接中断')
57             break
服务端
 1 import socket
 2 import struct
 3 import json
 4 
 5 client = socket.socket()
 6 
 7 client.connect(('127.0.0.1', 8888))
 8 
 9 while True:
10     cmd = input('>>>:').strip().encode('utf-8')
11     if not cmd:
12         print('cmd命令不能为空')
13         continue
14     client.send(cmd)
15 
16     # 接受额外信息长度
17     length = client.recv(4)
18     len_data = struct.unpack("i", length)[0]
19     #接受额外信息
20     t_json = client.recv(len_data)
21     json_dic = json.loads(t_json.decode('utf-8'))
22     print('执行时间为%s'%json_dic['time'])
23     data_size = json_dic['size']
24 
25     #接收真实数据
26     all_data = b''
27     rcv_size = 0
28 
29     while rcv_size < data_size:
30         data = client.recv(1024)
31         rcv_size += len(data)
32         all_data += data
33 
34     print('接收长度为%s' % rcv_size)
35     print(all_data.decode('gbk'))
客户端

 

转载于:https://www.cnblogs.com/yangyufeng/p/10179940.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值