一.半连接数:
三次握手没有完成 称之为半连接
原因1 恶意客户端没有返回第三次握手信息
原因2 服务器没空及时处理你的请求
socket中 listen(半连接最大数量)
二.粘包问题
TCP流式协议, 数据之间没有分界, 就像水 一杯水和一杯牛奶倒在一起了!
UDP 用户数据报协议
粘包 仅发生在TCP协议中
- 发送端 发送的数据量小 并且间隔短 会粘
- 接收端 一次性读取了两次数据的内容 会粘
- 接收端 没有接收完整 剩余的内容 和下次发送的粘在一起
无论是那种情况,其根本原因在于 接收端不知道数据到底有多少
解决方案就是 提前告知接收方 数据的长度
解决方案
先发长度给对方 再发真实数据
客户端 -----------
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.再收真实数据 真实可能很长 需要循环接收
发送端和接收端必须都处理粘包 才算真正的解决了