我们使用tcp协议的时候有时会出现一些问题,
就比如我同时发送了3次数据,但是在另外一边缺只收到了一次,它把三次数据都和在了一起,
服务端(接收)
import socket server = socket.socket() server.bind(('127.0.0.1',18080)) # 绑定ip和端口 server.listen(2) # 半连接池 conn,addr = server.accept() # 等别人来 data = conn.recv(1024) # 如果conn 没了,就会报错 print('第一次接收--') print(data.decode('utf-8')) data = conn.recv(1024) # 如果conn 没了,就会报错 print('第二次接收---') print(data.decode('utf-8')) data = conn.recv(1024) # 如果conn 没了,就会报错 print('第三次接收---') print(data.decode('utf-8'))
客户端(发送)
import socket client = socket.socket() client.connect(('127.0.0.1',18080)) # 连接 client.send(b'11') # 发送第一次 client.send(b'22') # 第二次发送 client.send(b'33') # 第三次发送
输出结果:
>>>
第一次接收-- 112233 第二次接收--- 第三次接收---
这个就是tcp粘包问题
解决这个问题的关键就是设置recv的长度,recv就是获取数据的长度,所以我们需要得到被发送数据的长度,但是我们又有可能不知道数据长度的长度,除非我们能固定这个数据的长度。这里我们可以引入一个模块:他就能固定数据的长度
struct 模块
struct 模块 # 对数据进行打包处理,被打包后的数据是固定长度,,我们只需要接收这个固定长度就好,然后把这个包解开,就能得到真正的数据的长度 pack # 打包数据 unpack # 解包数据这时我们就不止能发送数据了,还可以把数据的数据信息(名称,数据长度,大小),都打包到一个字典当中,我们直接把字典序列化,然后把字典的报头(数据长度),和字典一起传过去就好了。
服务端(发送) 1:生成一个字典 2:制作字典的报头 JSON序列化 编码 统计长度 3:发送字典的报头 4:发送字典 5:发送真实的数据 客户端(接收): 1:先接受固定长度,4 个字节的报头(几个字节取决于struct打包的报头长度) 2:解析获取的字典数据长度 unpack(...)[0] 3:接受字典数据 解码 反序列化 4:接收真实数据
这样一来我们就成功解决了粘包问题
代码
客户端(接收)
import socket import struct import json client = socket.socket() client.connect(('127.0.0.1',8080)) while True: msg = input('>>>:').encode('utf-8') if len(msg) == 0: continue client.send(msg) # 1.先接受字典报头 header_dict = client.recv(4) # 2.解析报头 获取字典的长度 dict_size = struct.unpack('i',header_dict)[0] # 解包的时候一定要加上索引0 # 3.接收字典数据 dict_bytes = client.recv(dict_size) dict_json = json.loads(dict_bytes.decode('utf-8')) # 4.从字典中获取信息 print(dict_json) # 这个是字典 recv_size = 0 # 这个是设置下载总量的初始值,下载多少就加多少 real_data = b'' # 这个是下载的东西,下载了什么,就把他加进去 # 如果一直加这个数据的花,这个数据如果很大,会不会把内存弄爆炸? while recv_size < dict_json.get('file_size'): # 如果已经接受的数据总量不大于被接受数据的总量时,跳出循环(接收循环) # while recv_size < dict_json.['file_size']: # 不推荐用这个方法,最好用get,这个可能会报错 data = client.recv(1024) # 一次性接收多大 real_data += data # 把数据加上去 recv_size += len(data) # 把已经下载的数据长度加在下载数据长度上面 print(real_data.decode('gbk')) # 打印已经下载好的数据
服务端(发送)
import socket import subprocess import struct import json server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) while True: conn, addr = server.accept() while True: try: cmd = conn.recv(1024) if len(cmd) == 0: break cmd = cmd.decode('utf-8') obj = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) res = obj.stdout.read() + obj.stderr.read() d = {'file_size': len(res)} # 这个是文件大小 # d = {'name':'jason','file_size':len(res),'info':'asdhjkshasdad'} json_d = json.dumps(d) # 格式化 # 1.先制作一个字典的报头 header = struct.pack('i', len(json_d)) print(header,'---',len(header)) # 2.发送字典报头 conn.send(header) # 发送字典的文件大小 # 3.发送字典 conn.send(json_d.encode('utf-8')) # 发送字典 # 4.再发真实数据 conn.send(res) # conn.send(obj.stdout.read()) # conn.send(obj.stderr.read()) except ConnectionResetError: break conn.close()