Python进阶-网络编程之TCP粘包

一、什么是TCP粘包

  C/S架构下,接收方不知道每个消息的发送间隙、也不知道每次应该提取多少个字节的数据,与此同时,TCP是面向连接的,面向流的,收发两端都要有,因此发送端为了将多个发往接收端的数据包更高效的发给对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个发送给接收端。此时接收端无法分辨出来,必须提供合理的拆包机制,即面向流的通信是无消息保护边界的。
  除此之外,因为TCP是基于流的,所以收发的消息不能为空,需要发送、接收端添加空消息处理机制,防止程序卡住。

二、处理思路

   粘包现象主要是因为发送端没有确切的发送间隔以及发送数据包的大小,而接收端更不知道发送算发送了多少个数据包以及大小,只能来多少接收多少。
   解决方法:
    自定义报头,发送端发送数据之前,先将自定义的报头(数据包大小等信息)发送给接收端,接收端明确每个数据包的大小进行依次接收。

三、实现方式

方法一:

 1 # 客户端
 2 import socket
 3 import struct
 4 
 5 IP = '127.0.0.1'
 6 PORT = 8080
 7 bufsize = 1024
 8 
 9 client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
10 client_socket.connect((IP,PORT))
11 
12 while True:
13     cmd = input('cmd>>>').strip()
14     if len(cmd) == 0:continue
15     elif cmd == 'q':break
16 
17     client_socket.send(cmd.encode('utf-8'))
18 
19     # 1.接收固定报头
20     header = client_socket.recv(4)
21 
22     # 2.解析报头
23     total_size = struct.unpack('i',header)[0]
24     print(total_size)
25 
26     # 3.根据包头接收真实数据
27     recv_size = 0
28     # 保存接收的数据(接收到的是byte类型)
29     res_data = b''
30     while recv_size < total_size:
31         recv_data = client_socket.recv(1024)
32         res_data += recv_data
33         recv_size += len(recv_data)
34 
35     print(recv_data.decode('gbk'))
36 
37 client_socket.close()
38 
39 # 服务端
40 import socket
41 import subprocess
42 import struct
43 
44 IP = '127.0.0.1'
45 PORT = 8080
46 bufsize = 1024
47 
48 tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
49 tcp_socket.bind((IP,PORT),)
50 tcp_socket.listen(5)
51 
52 while True:
53     conn,addr = tcp_socket.accept()
54     print('客户端:',addr)
55 
56     while True:
57         try:
58             cmd = conn.recv(bufsize)
59             res = subprocess.Popen(cmd.decode('utf-8'),shell=True
60                                    ,stdin=subprocess.PIPE
61                                    ,stdout=subprocess.PIPE
62                                    ,stderr=subprocess.PIPE)
63             stderr = res.stderr.read()
64             stdout = res.stdout.read()
65 
66 
67             # 1.制作固定长度的报头
68             total_size = len(stdout) + len(stderr)
69             header = struct.pack('i',total_size)
70 
71             # 2.发送报头
72             conn.send(header)
73 
74             # 3.发送真实数据
75             conn.send(stderr)
76             conn.send(stdout)
77         except ConnectionResetError:
78             break
79     conn.close()
80 tcp_socket.close()
解决粘包问题

方法二:

 1 # 客户端
 2 import socket
 3 import struct
 4 import json
 5 
 6 IP = '127.0.0.1'
 7 PORT = 8080
 8 bufsize = 1024
 9 
10 client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
11 client_socket.connect((IP,PORT))
12 
13 while True:
14     cmd = input('cmd>>>').strip()
15     if len(cmd) == 0:continue
16     elif cmd == 'q':break
17 
18     client_socket.send(cmd.encode('utf-8'))
19 
20     # 1.接收包头长度
21     header_size = struct.unpack('i',client_socket.recv(4))[0]
22 
23     # 2.接收报头
24     header_bytes = client_socket.recv(header_size)
25 
26     # 3.解析报头
27     header_json = header_bytes.decode('utf-8')
28     header_dic = json.loads(header_json)
29     # print(header_dic)
30 
31     total_size = header_dic['total_size']
32 
33     # 3.根据包头接收真实数据
34     recv_size = 0
35     # 保存接收的数据(接收到的是byte类型)
36     res_data = b''
37     while recv_size < total_size:
38         recv_data = client_socket.recv(1024)
39         res_data += recv_data
40         recv_size += len(recv_data)
41 
42     print(recv_data.decode('gbk'))
43 
44 client_socket.close()
45 
46 
47 
48 # 服务端
49 import socket
50 import subprocess
51 import struct
52 import json
53 
54 IP = '127.0.0.1'
55 PORT = 8080
56 bufsize = 1024
57 
58 tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
59 tcp_socket.bind((IP,PORT),)
60 tcp_socket.listen(5)
61 
62 while True:
63     conn,addr = tcp_socket.accept()
64     print('客户端:',addr)
65 
66     while True:
67         try:
68             cmd = conn.recv(bufsize)
69             res = subprocess.Popen(cmd.decode('utf-8'),shell=True
70                                    ,stdin=subprocess.PIPE
71                                    ,stdout=subprocess.PIPE
72                                    ,stderr=subprocess.PIPE)
73             stderr = res.stderr.read()
74             stdout = res.stdout.read()
75 
76             # 1.制作固定长度的报头
77             header_dic = {
78                 'total_size':len(stdout) + len(stderr),
79                 'md5':'123sssss222',
80                 'filename':'120.txt'}
81 
82             header_json = json.dumps(header_dic)
83             header_bytes = header_json.encode('utf-8')
84 
85             # 2.发送报头的长度
86             total_size = len(header_bytes)
87             conn.send(struct.pack('i',total_size))
88 
89             # 发送报头
90             conn.send(header_bytes)
91 
92             # 3.发送真实数据
93             conn.send(stderr)
94             conn.send(stdout)
95         except ConnectionResetError:
96             break
97     conn.close()
98 tcp_socket.close()
解决粘包问题
原文地址:https://www.cnblogs.com/mdzzbojo/p/8982746.html