socket粘包现象
服务端: 客户端:
第一次 dir 数据418 < 1024 接收418数据
第二次 ipconfig 数据1517 > 1024 接收1024个字节
第三次 dir 数据418 < 1024 接收493个字节
黏包现象的根本原因:缓冲区
系统缓冲区
如果你的网络出现短暂的异常或者波动,接受数据就会出现短暂的中断,影响你的下载或者上传的效率.
但是 缓冲区也是双刃剑,缓冲区解决了上传下载的传输效率的问题,但是带来了粘包的问题
什么情况下产生粘包
1.recv会产生粘包(如果revc的接受数据量小于发送的数据量,第一次只能接受规定的数据量,第二次接受剩余的数据量)
2.send也可能造成粘包 (连续send少量的数据发到输出缓冲区,油鱼缓冲区的机制,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络)
解决粘包的方案(revc的工作原理)
# 错误实例
1.扩大recv:
可以扩大recv的上线 revc(10240000000000000000)
不是解决这个问题的根本原因 可能会撑爆内存
2.故意延长recv的时间
time.sleep 这样会非常影响效率
# 如何解决
解决粘包现象的思路分析:
1.当我第二次给服务器发送命令之前,我应该循环recv直至将所有的数据全部取完.
recv的次数与循环次数相关 3000>>3次 5000>>5次 30000>>30次
如何限制循环次数?
当你发送的总bytes个数与接收的总bytes个数相等时,循环结束.
如何获取发送的总bytes个数: len()
所以: send两次 总个数 + 总数据
总个数是什么类型? int() 3400,send需要发送 bytes类型.
send(总个数)
将int 转化成bytes 即可. b'3400'
方案一:
str(3400) -- > '3400' -----> bytes('3400') -----> b'3400' ---> 几个字节? 4个字节
send(总数据)
1563337531069
你现在继续解决的问题!!!!!
无论总字节个数是多多少? 129 3400 10000 30000 转化成固定长度的bytes.
将不固定长度的int类型,转化成固定长度bytes类型.方便获取头部信息.
recv的工作原理
recv从系统缓存区取出固定recv()字节的数据
解决沾包
server
import socket
import subprocess
import struct
import json
phone = socket.socket()
phone.bind(('127.0.0.1', 8888))
phone.listen(5)
# 4.接收连接
print('start')
conn, addr = phone.accept()
while 1:
try:
cmd = conn.recv(1024)
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
result = obj.stdout.read() + obj.stderr.read()
result = result.decode('gbk').encode('utf-8')
# 1.制作表头
head_dict = {
'MD5':'896a74sf9647a12we',
'file_name':'xxx视频',
'file_size':len(result),
}
# 2.将报头字典转化成json字符串
head_dict_json = json.dumps(head_dict)
# 3.将json字符串 转化成bytes
head_dict_json_bytes = head_dict_json.encode('utf-8')
# 4.获取报头的长度
head_len = len(head_dict_json_bytes)
# 5.将长度转化成固定的4个字节
head_len_bytes = struct.pack('i', head_len)
# 6.发送固定的4个字节
conn.send(head_len_bytes)
# 7.发送报头
conn.send(head_dict_json_bytes)
# 8.发送原数据
conn.send(result)
except ConnectionResetError:
break
conn.close()
phone.close()
client
import socket
import struct
phone = socket.socket()
phone.connect(('127.0.0.1', 8888))
# 发消息
while 1:
cmd = input('>>>').strip()
phone.send(cmd.encode('utf-8'))
# 1.接收报头
head_bytes = phone.recv(4)
# 2.将报头反解回int类型
total_size = struct.unpack('i', head_bytes)[0]
# 3.循环接收原数据
total_data = b''
while len(total_data) < total_size:
total_data += phone.recv(1024)
print(total_data.decode('gbk'))
phone.close()