day_32网络编程之socket粘包问题

一.半连接数:

三次握手没有完成 称之为半连接

原因1 恶意客户端没有返回第三次握手信息

原因2 服务器没空及时处理你的请求

socket中 listen(半连接最大数量)

二.粘包问题

TCP流式协议, 数据之间没有分界, 就像水 一杯水和一杯牛奶倒在一起了!

UDP 用户数据报协议

粘包 仅发生在TCP协议中

  1. 发送端 发送的数据量小 并且间隔短 会粘

  2. 接收端 一次性读取了两次数据的内容 会粘

  3. 接收端 没有接收完整 剩余的内容 和下次发送的粘在一起

无论是那种情况,其根本原因在于 接收端不知道数据到底有多少

解决方案就是 提前告知接收方 数据的长度

解决方案

先发长度给对方 再发真实数据

 

客户端 -----------
import
socket import struct client = socket.socket() client.connect(('192.168.13.72',666)) while True: try: # 首先客户端输入指令 cmd = input('请输入系统指令>>:').encode('utf-8') # 用struct的pack函数将收到的cmd指令的长度转成固定格式的字节 cmd_size_bytes = struct.pack('i',len(cmd)) # 然后先将这个固定格式的字节发过去. client.send(cmd_size_bytes) # 然后在发送cmd指令 这样就算后面跟着传别的东西发生了粘包,也不会取到后面的东西了 client.send(cmd) # 然后服务器将执行结果返回 ,同样先取四个字节.用struct的unpack函数 将数据的长度转回来 注意返回的是元组 res_bytes = client.recv(4) res_size = struct.unpack('i',res_bytes)[0] temp = b'' while True: # 然后 如果数据量大的话 一次直接接收内存会溢出,所以循环收取.循环收到的数据可以进行保存或者其他的各种操作 # 首先设置一个空二进制字符串 temp # 然后判断总的大小 减掉已接收字符串temp的长度如果大于等于1024 就直接接收1024字节 # 然后 if res_size-len(temp) >= 1024: temp += client.recv(1024) else: # else:的话 就代表数据已经快收完了 就接收总长度减掉已接受temp的长度 # 然后循环结束 temp += client.recv(res_size-len(temp)) break # 最后打印temp的结果即可 print(temp.decode('gbk')) except Exception as E: print(E) client.close() break
# 服务器-----------------
import
socket import json import struct import subprocess server = socket.socket() server.bind(('192.168.13.72',666)) server.listen() while True: client, addr = server.accept() print('IP%s,端口%s已连接'%addr) while True: try: # 服务器首先接收四个字节的cmd命令的长度信息 size_bytes = client.recv(4) # 然后将这个信息 用unpack反序列化为整型的数字信息.同样返回的元组.取0号索引即可 data_size = struct.unpack('i',size_bytes)[0] # 然后用recv 读数据 将cmd的长度放进括号里 data = client.recv(data_size) # 然后用 subprocess执行系统命令 p_obj = subprocess.Popen(data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 将输出管道和错误输出管道信息读出. out_info = p_obj.stdout.read() err_info = p_obj.stderr .read() # 然后把他俩加起来的长度加起来,两个字符串 因为只有一个有值另一个为空,直接不用判断了 info_size = len(out_info)+len(err_info) # 先讲长度用struct转为固定长度的字节 info_size_bytes = struct.pack('i',info_size) # 然后先把固定长度的字节发过去 client.send(info_size_bytes) # 再将两次的执行结果发过去 #这样客户端就可以直接接受多少了 client.send(out_info) client.send(err_info) except Exception as E: print('IP%s,端口%s已挂'%addr) client.close() break

#发送端

1.使用struct 将真实数据的长度转为固定的字节数据

2.发送长度数据

3.发送真实数据

接收端

1.先收长度数据 字节数固定

2.再收真实数据 真实可能很长 需要循环接收

发送端和接收端必须都处理粘包 才算真正的解决了

猜你喜欢

转载自www.cnblogs.com/jixu/p/10946859.html