TCP协议------------黏包现象

 1 1.服务端连续执行三次recv
 2 2.客户端连续执行三次send
 3 问题:服务端一次性接收到了客户端三次的消息 该现象称为"黏包现象"
 4 --------------------------------------
 5 黏包现象产生的原因:
 6     1.收消息的时候,不知道每次接收的数据到底有多大!!!!!!
 7             recv()括号里面规定了每次收消息的字节数,所以如果客户端连续发几次消息,但是总字节数小于括号里面规定收消息的字节数
 8     2.TCP也称为流式协议:数据像水流一样,没有间隔。
 9 TCP里面有一个算法会针对数据量较小且发送间隔较短的多条数据一次性合并打包发送,服务端就会一次性全都接收到!!!
10 ---------------
11 避免黏包现象的核心思路\关键点:
12     如何明确即将接收的数据具体的字节数有多大!!!!!!
13 ---------------
14 ps:如何将长度变化的数据全部制作成固定长度的数据??????

解决粘包问题办法一:

 1 服务端:
 2 
 3 import subprocess
 4 from socket import *
 5 import struct
 6 
 7 server = socket(AF_INET, SOCK_STREAM)
 8 server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
 9 server.bind(('127.0.0.1', 8891))
10 server.listen(5)
11 
12 # 服务端应该做两件事:
13 # 1.循环的从半链接池中取出链接请求与其建立双向链接,拿到链接对象
14 while True:
15     conn, client_addr = server.accept()
16     print('Connected by', client_addr)
17     # 2.拿到链接对象,与其进行通信循环
18     while True:
19         try:
20             res = conn.recv(1024)
21             if len(res) == 0: break
22             obj = subprocess.Popen(res.decode('utf-8')
23                                    , shell=True,
24                                    stdout=subprocess.PIPE,
25                                    stderr=subprocess.PIPE)
26             stdout_res = obj.stdout.read()
27             stderr_res = obj.stderr.read()
28             total_size = len(stdout_res) + len(stderr_res)
29             # print(len(stderr_res)+len(stdout_res))
30             #  把执行结果返回给客户端(错误和正确都返回)
31 
32             # 1.先发头信息(固定长度的bytes):对数据进行描述
33             # int--->固定长度的bytes(使用import struct模块),组包
34             header = struct.pack('i', total_size)
35             conn.send(header)
36 
37             # 2.再发真实的数据
38             conn.send(stdout_res)
39             conn.send(stderr_res)
40             # with open("1.mp4",mode='rb') as f:
41             #     for lin in f:
42             #         conn.send(lin)
43         except Exception:
44             break
45 
46     conn.close()
47 
48 
49 ==============================================
50 客户端
51 
52 from socket import *
53 import struct
54 
55 #  创建socket对象
56 client = socket(AF_INET, SOCK_STREAM)
57 client.connect(('127.0.0.1', 8891))
58 
59 while True:
60     msg = input('>>:').strip()
61     if len(msg) == 0: continue
62     client.send(msg.encode('utf-8'))
63     # 解决粘包问题的思路:
64     # 1.先收固定长度的头,解析出数据的描述信息,包括数据的总大小,total_size
65     header = client.recv(4)
66     #  解析头信息,获取数据的总大小struct.unpack
67     total_size = struct.unpack('i', header)[0]
68     print('total_size:', total_size)
69     # 2.根据解析出的描述信息,接收真实的数据:recv_size=0,循环接收,每接收一次,recv_size+=接收的长度
70     # 3.当recv_size==total_size时,说明接收完毕
71     recv_size = 0
72     while recv_size < total_size:
73         recv_data = client.recv(1024)
74         recv_size += len(recv_data)
75         print(recv_data.decode('gbk'), end='')
76     else:
77         print()

 

代码实现终极解决粘包问题

 1 服务端
 2 
 3 import subprocess
 4 from socket import *
 5 import struct
 6 import json
 7 
 8 server = socket(AF_INET, SOCK_STREAM)
 9 server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
10 server.bind(('127.0.0.1', 8891))
11 server.listen(5)
12 
13 # 服务端应该做两件事:
14 # 1.循环的从半链接池中取出链接请求与其建立双向链接,拿到链接对象
15 while True:
16     conn, client_addr = server.accept()
17     print('Connected by', client_addr)
18     # 2.拿到链接对象,与其进行通信循环
19     while True:
20         try:
21             res = conn.recv(1024)
22             if len(res) == 0: break
23             obj = subprocess.Popen(res.decode('utf-8')
24                                    , shell=True,
25                                    stdout=subprocess.PIPE,
26                                    stderr=subprocess.PIPE)
27             stdout_res = obj.stdout.read()
28             stderr_res = obj.stderr.read()
29             total_size = len(stdout_res) + len(stderr_res)
30             # print(len(stderr_res)+len(stdout_res))
31             # 1.文件真实存在:制作头(头信息)
32             header_dic = {
33                 'filename': 'a.txt',
34                 'total_size': total_size,
35                 'md5': '123456'
36             }
37             # conn.send(json.dumps(header_dic).encode('utf-8'))
38             json_str = json.dumps(header_dic)
39             json_str_bytes = json_str.encode('utf-8')
40 
41             # 2.先发头的长度信息(固定长度的bytes):对数据进行描述
42             header_size = struct.pack('i', len(json_str_bytes))
43             conn.send(header_size)
44             # 3.发头信息
45             conn.send(json_str_bytes)
46 
47             # 4.再发真实的数据
48             conn.send(stdout_res)
49             conn.send(stderr_res)
50 
51 
52         except Exception:
53             break
54 
55     conn.close()
56 
57 
58 ================================================
59 客户端
60 
61 from socket import *
62 import struct
63 import json
64 
65 #  创建socket对象
66 client = socket(AF_INET, SOCK_STREAM)
67 client.connect(('127.0.0.1', 8891))
68 
69 while True:
70     msg = input('>>:').strip()
71     if len(msg) == 0: continue
72     client.send(msg.encode('utf-8'))
73     # 接收端
74     # 1.先收4个字节,从中提取接下来要收的头的长度
75     header_size = client.recv(4)
76     header_len = struct.unpack('i', header_size)[0]
77     # 2.接收头,并解析
78     json_str_bytes = client.recv(header_len)
79     json_str = json_str_bytes.decode('utf-8')
80     header_dic = json.loads(json_str)
81     print(header_dic)
82     total_size = header_dic['total_size']
83     # 提取信息:total_size=header_dic['total_size'],filename,md5
84     # 3.接收真实的数据
85     recv_size = 0
86     while recv_size < total_size:
87         recv_data = client.recv(1024)
88         recv_size += len(recv_data)
89         print(recv_data.decode('gbk'), end='')
90     else:
91         print()

 

PS补充:

 

【socketserver模块实现并发】

(基于tcp)

 1 服务端
 2 
 3 import socketserver
 4 
 5 
 6 class MyRequestHandler(socketserver.BaseRequestHandler):  # 继承BaseRequestHandler类
 7     def handle(self):
 8         # print(self.request)  # 如果tcp协议,self.request==>conn
 9         print(self.client_address)
10 
11         while True:
12             try:
13                 msg = self.request.recv(1024)
14                 if len(msg) == 0: break
15                 self.request.send(msg.upper())
16             except Exception:
17                 break
18 
19         self.request.close()
20 
21 
22 # 1.创建一个服务端对象,绑定端口,启动服务
23 s = socketserver.ThreadingTCPServer(('127.0.0.1', 8081), MyRequestHandler)
24 s.serve_forever()
25 # 等同于while True:
26 # conn, client_addr = server.accept()
27 # 启动一个线程(conn,client_addr)
28 
29 # 2.拿到链接对象,与其进行通信循环====》handle
30 
31 
32 =================================================
33 客户端
34 
35 from socket import *
36 
37 client = socket(AF_INET, SOCK_STREAM)
38 client.connect(('127.0.0.1', 8081))
39 
40 while True:
41     msg = input('>>:').strip()
42     if len(msg) == 0: continue
43     client.send(msg.encode('utf-8'))
44     res = client.recv(1024)
45     print(res.decode('utf-8'))

 

(基于udp)

 1 import socketserver
 2 
 3 
 4 class MyRequestHandler(socketserver.BaseRequestHandler):
 5     def handle(self):
 6         client_data = self.request[0]
 7         server = self.request[1]
 8         client_address = self.client_address
 9         print('客户端发来的数据%s' % client_data)
10         server.sendto(client_data.upper(), client_address)
11 
12 
13 s = socketserver.ThreadingUDPServer(('127.0.0.1', 8089), MyRequestHandler)
14 s.serve_forever()
15 
16 # 相当于:只负责循环的收
17 # while True:
18 #     data,client_addr=server.recvfrom(1024)
19 #       启动一个线程处理后续的事情
20 #     server.sendto(data.upper(),client_addr)
21 
22 
23 ==============================================
24 客户端
25 
26 import socket
27 
28 client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
29 while True:
30     msg = input('>>:').strip()
31     client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8089))
32     data, server_addr = client.recvfrom(1024)
33     print('Receive from server:', data.decode('utf-8'))
34 
35 client.close()

 

 

posted on 2024-06-22 19:53  认真的六六  阅读(5)  评论(0编辑  收藏  举报