day27_粘包问题与struct模块
1、粘包问题
问题描述:
当客户端设定的最大接收字节数(bytes size)小于服务端发来的数据时,多余的部分数据,会在下一次的客户端接收时,一起传过来,先接收完之前没接收到的数据,才会继续接收后面的数据。这个特性跟tcp的协议特性有关,因为tcp协议是一个可靠的传输协议,
演示:
# 客户端.py
import socket
client = socket.socket()
client.connect(('127.0.0.1',10086))
while True:
cmd = input('cmd>>:').encode('utf8')
client.send(cmd)
result = client.recv(1024).decode('gbk') # 指定接收数据的最大字节数1024
print(f'>>{result}')
if result == 'q':
break
# 服务器.py
import socket
import subprocess
server = socket.socket()
server.bind(('127.0.0.1', 10086))
server.listen(5)
while True:
conn, addr = server.accept() # 链接客户端
print(f'{addr}:')
while True:
try:
cmd = conn.recv(1024).decode('utf8')
print(f'from client:{cmd}')
if cmd == 'q':
conn.send(cmd.encode('utf8'))
break
# 1 subprocess.Popen()
obj = subprocess.Popen(
cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
restult = obj.stdout.read() + obj.stderr.read()
print(restult.decode('gbk'))
conn.send(restult)
print('to client success')
except Exception as e:
print(f'e:{e}')
break
conn.close() # 执行命令出错或者与该客户端的链接已断开
先执行命令tasklist,再执行命令dir
实际执行的结果:
('127.0.0.1', 59338):
from client:tasklist
映像名称 PID 会话名 会话# 内存使用
========================= ======== ================ =========== ============
System Idle Process 0 Services 0 8 K
System 4 Services 0 27,848 K
Registry 96 Services 0 41,200 K
smss.exe 360 Services 0 924 K
csrss.exe 600 Services 0 5,272 K
wininit.exe 700 Services 0 6,332 K
csrss.exe 728 1 4,888 K
services.exe 776 Services 0 8,612 K
lsass.exe 848 Services 0 21,060 K
svchost.exe 984 Services 0 3,596 K
fontdrvhost.exe 1008 Services 0 3,048 K
svchost.exe 560 Services 0 31,540 K
svchost.exe 556 Services 0 14,772 K
svchost.exe 868 Services 0 7,908 K
svchost.exe 1204 Services 0 10,228 K
svchost.exe 1292 Services 0 11,932 K
svchost.exe 1408 Services 0 15,228 K
svchost.exe 1420 Services 0 14,044 K
svchost.exe 1448 Services 0 6,972 K
svchost.exe 1480 Services 0 5,564 K
svchost.exe 1540 Services 0 10,392 K
svchost.exe 1676 Services 0 9,800 K
svchost.exe 1732 Services 0 10,412 K
svchost.exe 1744 Services 0 7,556 K
svchost.exe 1832 Services 0 6,088 K
svchost.exe 1876 Services 0 7,380 K
svchost.exe 1980 Services 0 11,536 K
svchost.exe 2016 Services 0 9,724 K
svchost.exe 2024 Services 0 8,128 K
svchost.exe 1324 Services 0 15,104 K
svchost.exe 2072 Services 0 8,828 K
svchost.exe 2312 Services 0 74,900 K
svchost.exe 2320 Services 0 5,548 K
svchost.exe 2328 Services 0 6,796 K
Memory Compression 2376 Services 0 177,844 K
svchost.exe 2432 Services 0 8,372 K
igfxCUIService.exe 2456 Services 0 8,080 K
svchost.exe 2488 Services 0 7,336 K
svchost.exe 2504 Services 0 6,944 K
svchost.exe 2528 Services 0 12,032 K
svchost.exe 2596 Services 0 15,732 K
svchost.exe 2796 Services 0 12,952 K
svchost.exe 3008 Services 0 12,456 K
svchost.exe 3016 Services 0 6,312 K
svchost.exe 3160 Services 0 14,784 K
svchost.exe 3340 Services 0 12,744 K
svchost.exe 3436 Services 0 15,116 K
spoolsv.exe 3684 Services 0 13,680 K
svchost.exe 3752 Services 0 7,060 K
svchost.exe 3836 Services 0 14,512 K
svchost.exe 4020 Services 0 18,512 K
svchost.exe 4052 Services 0 7,924 K
svchost.exe 4144 Services 0 5,932 K
svchost.exe 4440 Services 0 26,732 K
svchost.exe 4448 Services 0 15,712 K
NVDisplay.Container.exe 4496 Services 0 8,676 K
svchost.exe 4516 Services 0 12,176 K
QQProtect.exe 4524 Services 0 18,996 K
svchost.exe 4532 Services 0 11,836 K
svchost.exe 4548 Services 0 6,024 K
svchost.exe 4556 Services 0 10,664 K
svchost.exe 4564 Services 0 10,396 K
svchost.exe 4580 Services 0 7,404 K
svchost.exe 4600 Services 0 42,908 K
GATESRV.exe 4620 Services 0 6,112 K
svchost.exe 4716 Services 0 5,304 K
svchost.exe 4732 Services 0 8,200 K
svchost.exe 4756 Services 0 21,012 K
svchost.exe 5084 Services 0 5,108 K
svchost.exe 5576 Services 0 8,456 K
svchost.exe 5640 Services 0 10,528 K
svchost.exe 2180 Services 0 7,480 K
svchost.exe 4432 Services 0 8,772 K
svchost.exe 6980 Services 0 12,688 K
svchost.exe 4672 Services 0 15,708 K
dllhost.exe 5772 Services 0 10,272 K
svchost.exe 9144 Services 0 29,256 K
SecurityHealthService.exe 8528 Services 0 15,140 K
svchost.exe 1464 Services 0 10,796 K
svchost.exe 9384 Services 0 7,320 K
svchost.exe 8208 Services 0 17,708 K
svchost.exe 1336 Services 0 13,000 K
svchost.exe 4724 Services 0 10,008 K
svchost.exe 4376 Services 0 22,892 K
SgrmBroker.exe 7020 Services 0 5,620 K
svchost.exe 9472 Services 0 8,920 K
svchost.exe 5484 Services 0 8,060 K
svchost.exe 10108 Services 0 7,052 K
csrss.exe 8356 2 5,052 K
svchost.exe 7412 Services 0 6,528 K
svchost.exe 1456 Services 0 7,124 K
svchost.exe 9368 Services 0 9,164 K
csrss.exe 9124 Console 3 5,320 K
winlogon.exe 13408 Console 3 8,928 K
fontdrvhost.exe 10280 Console 3 16,588 K
dwm.exe 4296 Console 3 88,868 K
nvxdsync.exe 12712 Console 3 19,060 K
MasterHelper.exe 7184 Console 3 9,572 K
ProcHelper64.exe 3524 Console 3 5,904 K
sihost.exe 9328 Console 3 29,792 K
svchost.exe 13808 Console 3 24,296 K
svchost.exe 7432 Console 3 27,012 K
taskhostw.exe 12168 Console 3 17,956 K
igfxEM.exe 9972 Console 3 10,632 K
igfxHK.exe 7480 Console 3 8,012 K
igfxTray.exe 4816 Console 3 9,072 K
explorer.exe 12068 Console 3 139,020 K
ctfmon.exe 10464 Console 3 39,232 K
svchost.exe 2632 Services 0 7,232 K
svchost.exe 13724 Console 3 18,416 K
ChsIME.exe 14256 Console 3 53,544 K
ShellExperienceHost.exe 11296 Console 3 61,556 K
SearchUI.exe 6852 Console 3 99,068 K
RuntimeBroker.exe 4028 Console 3 42,096 K
RuntimeBroker.exe 6360 Console 3 20,324 K
MicrosoftEdge.exe 13212 Console 3 104,060 K
ApplicationFrameHost.exe 11024 Console 3 34,516 K
YourPhone.exe 1148 Console 3 35,692 K
browser_broker.exe 1712 Console 3 15,636 K
dllhost.exe 2812 Console 3 13,928 K
dllhost.exe 11352 Console 3 5,928 K
RuntimeBroker.exe 3424 Console 3 14,736 K
RuntimeBroker.exe 10328 Console 3 34,520 K
MicrosoftEdgeCP.exe 7380 Console 3 134,968 K
MicrosoftEdgeSH.exe 6896 Console 3 15,388 K
SettingSyncHost.exe 12512 Console 3 4,428 K
WindowsInternal.Composabl 14172 Console 3 41,328 K
svchost.exe 4744 Services 0 5,000 K
RuntimeBroker.exe 4360 Console 3 9,768 K
StudentMain.exe 10956 Console 3 40,304 K
RAVCpl64.exe 11952 Console 3 11,476 K
RAVBg64.exe 5556 Console 3 10,520 K
WeChat.exe 6880 Console 3 96,068 K
WeChatWeb.exe 9584 Console 3 16,996 K
SystemSettings.exe 1192 Console 3 324 K
svchost.exe 2420 Console 3 25,104 K
RuntimeBroker.exe 11132 Console 3 24,920 K
WinStore.App.exe 14320 Console 3 324 K
FeiQ.exe 12912 Console 3 15,604 K
Microsoft.Photos.exe 4004 Console 3 39,640 K
RuntimeBroker.exe 3504 Console 3 30,476 K
mcool.exe 1044 Console 3 28,652 K
audiodg.exe 5612 Services 0 18,492 K
FMAPP.exe 7660 Console 3 7,892 K
pycharm64.exe 10456 Console 3 864,072 K
fsnotifier64.exe 13792 Console 3 1,692 K
conhost.exe 11980 Console 3 4,912 K
OfficeClickToRun.exe 12488 Services 0 47,996 K
AppVShNotify.exe 8992 Services 0 6,072 K
AppVShNotify.exe 13048 Console 3 6,680 K
SearchIndexer.exe 6804 Services 0 53,196 K
Typora.exe 3880 Console 3 112,520 K
Typora.exe 9268 Console 3 83,152 K
Typora.exe 8636 Console 3 69,700 K
Typora.exe 14132 Console 3 138,072 K
Notepad++.exe 1196 Console 3 45,280 K
smartscreen.exe 592 Console 3 28,508 K
svchost.exe 10016 Services 0 7,472 K
Windows.WARP.JITService.e 13884 Services 0 5,248 K
svchost.exe 6592 Services 0 13,464 K
MicrosoftEdgeCP.exe 8012 Console 3 64,236 K
MicrosoftEdgeCP.exe 9224 Console 3 98,264 K
MicrosoftEdgeCP.exe 8264 Console 3 117,192 K
MicrosoftEdgeCP.exe 6940 Console 3 27,632 K
MicrosoftEdgeCP.exe 13372 Console 3 27,436 K
SearchProtocolHost.exe 8164 Services 0 10,056 K
SearchProtocolHost.exe 9220 Console 3 7,688 K
SearchFilterHost.exe 8964 Services 0 6,248 K
python.exe 9808 Console 3 31,436 K
conhost.exe 11172 Console 3 11,040 K
python.exe 10020 Console 3 31,536 K
conhost.exe 1232 Console 3 11,044 K
cmd.exe 6468 Console 3 3,944 K
tasklist.exe 13424 Console 3 7,652 K
WmiPrvSE.exe 13964 Services 0 8,536 K
to client success
from client:dir
驱动器 D 中的卷是 DATA
卷的序列号是 AE05-5DDC
D:\Python\python学习—上海老男孩\PycharmProjects\网络编程 的目录
2019/10/18 16:47 <DIR> .
2019/10/18 16:47 <DIR> ..
2019/10/18 17:00 <DIR> .idea
2019/10/18 15:45 186 test.py
2019/10/18 16:31 325 客户端.py
2019/10/18 16:47 998 服务端.py
3 个文件 1,509 字节
3 个目录 217,707,405,312 可用字节
to client success
客户端接收到的结果:
cmd>>:tasklist
>>
映像名称 PID 会话名 会话# 内存使用
========================= ======== ================ =========== ============
System Idle Process 0 Services 0 8 K
System 4 Services 0 27,848 K
Registry 96 Services 0 41,200 K
smss.exe 360 Services 0 924 K
csrss.exe 600 Services 0 5,272 K
wininit.exe 700 Services 0 6,332 K
csrss.exe 728 1 4,888 K
services.exe 776 Services 0 8,612 K
lsass.exe 848 Services 0 21,060 K
svchost.exe 984 Services 0 3,596 K
fontdrvhost.exe 1008 Services 0 3,048 K
svchost.
cmd>>:dir
>>exe 560 Services 0 31,540 K
svchost.exe 556 Services 0 14,772 K
svchost.exe 868 Services 0 7,908 K
svchost.exe 1204 Services 0 10,228 K
svchost.exe 1292 Services 0 11,932 K
svchost.exe 1408 Services 0 15,228 K
svchost.exe 1420 Services 0 14,044 K
svchost.exe 1448 Services 0 6,972 K
svchost.exe 1480 Services 0 5,564 K
svchost.exe 1540 Services 0 10,392 K
svchost.exe 1676 Services 0 9,800 K
svchost.exe 1732 Services 0 10,412 K
svchost.exe 1744 Services 0 7,556 K
svchost.exe
cmd>>:
2、struct模块
2.1 介绍
headers = struct.pack('i',length)
参数为i
的时候,将一个整形数字length
通过某个规则转化为一个4字节长度的bytes数据,应用在数据传输时,可以将数据流的长度传入,转化为header
struct.unpack('i',headers)
参数为i
的时候,将一个struct.pack('i',length)
得到的4个字节长度的bytes类型数据headers
,还原为header被压缩之前的整形数值length
,是一个元组(length,)
演示:
import struct
headers_by_pack = struct.pack('i',100)
print(f'headers_by_pack:{headers_by_pack}')
headers_by_unpack = struct.unpack('i',headers_by_pack)
print(f'headers_by_unpack:{headers_by_unpack}')
'''
headers_by_pack:b'd\x00\x00\x00'
headers_by_unpack:(100,)'''
2.2 利用struct模块解决粘包问题
# 客户端.py
import socket
import struct
client = socket.socket()
client.connect(('127.0.0.1',10086))
while True:
cmd = input('cmd>>:').encode('utf8')
client.send(cmd)
headers = client.recv(4) # 接收报头headers,包头信息固定为4个字节,所以用4个字节就能接收到全部的报头数据
result_len = struct.unpack('i',headers)[0] # 获取报头内容,里面包含了将要接收的数据真正的数据流长度
result = client.recv(result_len).decode('gbk') # 设定最大接收数据流长度为报头中得到的数据,即真正的将要接收的数据流长度,这样就能接收到全部的数据了
print(f'>>{result}')
if result == 'q':
break
# 服务端.py
# 服务器.py
import socket
import subprocess
import struct
server = socket.socket()
server.bind(('127.0.0.1', 10086))
server.listen(5)
while True:
conn, addr = server.accept() # 链接客户端
print(f'{addr}:')
while True:
try:
cmd = conn.recv(1024).decode('utf8')
print(f'from client:{cmd}')
if cmd == 'q':
conn.send(cmd.encode('utf8'))
break
# 1 subprocess.Popen()
obj = subprocess.Popen(
cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
restult = obj.stdout.read() + obj.stderr.read() # 真正的bytes类型执行结果
print(restult.decode('gbk'))
len_data = len(restult) # 真正的bytes类型数据字节数,即长度
headers = struct.pack('i',len_data) # 将真正的bytes数据字节长度按格式'i'压缩成4个字节,即得到报头
conn.send(headers) # 先将报头发送给客户端,告知将要发送的数据的信息,其中包含数据流长度,即bytes数据字节数
conn.send(restult) # 发送真正的bytes数据流
print('to client success')
except Exception as e:
print(f'e:{e}')
break
conn.close() # 执行命令出错或者与该客户端的链接已断开
2.2 实例:上传大文件
# 服务端.py
import socket
import json
import struct
server = socket.socket()
server.bind(('127.0.0.1', 10086))
server.listen()
while True:
conn, addr = server.accept()
print(f'客户端:{addr}')
info_headers = conn.recv(4)
len_of_info_headers = struct.unpack('i', info_headers)[0]
file_info_json = conn.recv(len_of_info_headers).decode('utf-8')
file_info = json.loads(file_info_json)
file_name, file_length = file_info.values() # 字典解压缩时,取值顺序?
file_data = conn.recv(file_length) # 视频文件不需解码
print('接收完成')
# 存储文件
with open(file_name, 'wb') as fw:
fw.write(file_data)
print(f'存储成功')
# 客户端.py
import socket
import json
import struct
import os
client = socket.socket()
client.connect(('127.0.0.1', 10086))
while True:
file_path = r'C:\Users\Administrator\Desktop\day27code\3 上传大文件\jason真实写真集.mp4'
file_name = os.path.basename(file_path)
print(f'将要上传文件{file_name}')
choice = input('''
1 开始上传
q 退出''')
if choice == 'q':
break
if choice != '1':
print('请选择1或q')
continue
# 打开文件,获取文件大小
with open(file_path, 'rb') as fr:
movie_bytes = fr.read()
# 先将带有文件信息的字典json串发送到服务端,告诉服务端将要接收的数据的信息
file_info = {'file_name': file_name, 'file_size': len(movie_bytes)}
file_info_json = json.dumps(file_info)
bytes_info = file_info_json.encode('utf-8')
headers1 = struct.pack('i', len(bytes_info))
client.send(headers1)
client.send(bytes_info)
# 发送真正的数据movie_bytes
# client.send(movie_bytes)
# print('上传成功')
# 也可以采用多次发送,读文件时,读取一部分就上传一部分
size = 0
num = 1
with open(file_path, 'rb') as fr:
while size < len(movie_bytes):
data = fr.read(1024)
client.send(data)
print(num, f'已上传{len(data)}bytes')
size += len(data)
num += 1
print(size,len(movie_bytes)) # 为何读取完成之后,size才只有movie_bytes的一半
print('上传成功')