看了林海峰老师的视频后的总结:
粘包是TCP协议经常出现的问题,如果不解决好的话,会将数据粘在一起,带来很多烦恼和麻烦
首先为大家展示解决粘包问题前实现本章的功能代码
# 客户端
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('请输入命令>>>>').strip()
if len(msg)==0:
continue
client.send((msg.encode('utf-8')))
cmd_res=client.recv(1024)# 当收到的数据超过1024时,剩下的数据会继续在客户端的缓存中,新的数据也会在客户端的缓存中,等剩下的数据接收完了再接收新的数据
print(cmd_res.decode('gbk'))# windows默认是gbk格式,解码应为gbk格式
# 服务端
from socket import *
import subprocess
'''
服务端应满足特点:
1、一直提供服务
2、并发提供服务
'''
server=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)
# 服务端应该做两件事
# 1、第一件事:循环的从链接池中取出链接请求与其建立双向链接,拿到链接对象
while True:
conn,client_addr=server.accept()
# 2、拿到链接对象,与其进行通信循环
while True:
try:
cmd=conn.recv(1024)
if len(cmd)==0:
break
obj=subprocess.Popen(cmd.decode('utf-8'),# pycharm默认格式为utf-8
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout_res=obj.stdout.read()
stderr_res=obj.stderr.read()
conn.send(stdout_res+stderr_res)
except Exception:
break
conn.close
一样要先启动服务端,再启动客户端,先为大家基本展示该代码的功能(基于windows)
# 运行结果:
请输入命令>>>>dir D:\新建文件夹\pythonProject1# window终端的命令语句dir查看文件夹下的目录
驱动器 D 中的卷是 所有文件软件丢到这里来
卷的序列号是 C0F9-5D0A
D:\新建文件夹\pythonProject1 的目录
2021/11/15 12:04 <DIR> .
2021/11/15 12:04 <DIR> ..
2021/11/23 20:47 <DIR> .idea
2021/04/28 16:38 912,447 detail.csv
2021/04/21 09:19 <DIR> Include
2021/03/22 21:19 <DIR> Lib
2021/03/22 21:19 88 pyvenv.cfg
2021/08/17 19:24 <DIR> Scripts
2021/11/23 00:15 <DIR> shujialianxi
2021/10/20 22:46 8,223 text4.py
2021/11/15 12:03 <DIR> __pycache__
3 个文件 920,758 字节
8 个目录 37,793,673,216 可用字节
请输入命令>>>>
这样想必大家看不出什么问题,前提是客户端接收到的结果没有超过指定的1024bytes
# 运行结果:
请输入命令>>>>tasklist # 查看电脑正在运行的进程(返回结果远超1024bytes)
映像名称 PID 会话名 会话# 内存使用
========================= ======== ================ =========== ============
System Idle Process 0 Services 0 8 K
System 4 Services 0 60 K
Registry 124 Services 0 75,908 K
smss.exe 520 Services 0 860 K
csrss.exe 720 Services 0 4,916 K
wininit.exe 804 Services 0 5,432 K
csrss.exe 816 Console 1 6,796 K
services.exe 880 Services 0 9,968 K
lsass.exe 900 Services 0 23,804 K
svchost.exe 1020 Services 0 33,160 K
fontdrvhost.exe 528 Services 0 2,124 K
svchost.
请输入命令>>>>ipconfig #查看电脑ip地址(但是明显下面的结果依旧是tasklist的返回结果)
exe 724 Services 0 15,584 K
svchost.exe 1044 Services 0 9,760 K
WUDFHost.exe 1164 Services 0 14,740 K
WUDFHost.exe 1216 Services 0 6,004 K
winlogon.exe 1296 Console 1 12,480 K
fontdrvhost.exe 1352 Console 1 9,080 K
WUDFHost.exe 1404 Services 0 4,348 K
dwm.exe 1464 Console 1 68,488 K
svchost.exe 1548 Services 0 10,128 K
svchost.exe 1568 Services 0 4,112 K
svchost.exe 1672 Services 0 9,908 K
svchost.exe 1680 Services 0 11,044 K
svchost.exe 1712 Services 0 13,684 K
svchost.exe
请输入命令>>>>
tcp是流式协议,就像流水一样,服务端返回的结果像流水一样流入客户端的缓存内,而客户端一次只能取1024bytes,当客户端新发命令后,服务端会把返回结果继续像流水一样流入客户端的内存底部,而此时客户端会继续向内存取剩下的1024bytes,但基于上次没有取完,所以取的依旧是上次的没有取完剩下的返回结果
# 粘包问题出现的原因
# 1、tcp是流式协议,数据像水流一样粘在一起,没有任何边界区分
# 2、收数据没收干净,有残留,就会下一次结果混淆在一起
# 解决的核心法门就是:每次都收干净,不用有任何残留
# UDP不存在粘包问题(收不干净就丢失)
# 解决粘包问题:
# 一、先收固定长度的头:解析出数据的描述信息,包括拿数据的总大小total_size
# 二、根据解析出的描述信息,接收真实的数据
# 2、recv_size=0,循环接收,每接收一次,recv_size+=接收的长度
# 3、直到recv_size==total_size,结束循环
下面为大家展示解决粘包问题之后的代码:
# 客户端:
from socket import *
import struct
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('请输入命令>>>>').strip()
if len(msg)==0:
continue
client.send((msg.encode('utf-8')))
# 解决粘包问题:
# 一、先收固定长度的头:解析出数据的描述信息,包括拿数据的总大小total_size
header=client.recv(4)
total_size=struct.unpack('i',header)[0]# 将bytes转成int类型(解包)
# 二、根据解析出的描述信息,接收真实的数据
# 2、recv_size=0,循环接收,每接收一次,recv_size+=接收的长度
# 3、直到recv_size==total_size,结束循环
recv_size=0
while recv_size<total_size:
recv_data=client.recv(1024)# 当收到的数据超过1024时,剩下的数据会继续在客户端的缓存中,新的数据也会在客户端的缓存中,等剩下的数据接收完了再接收新的数据
recv_size+=len(recv_data)
print(recv_data.decode('gbk'),end='')# windows默认是gbk格式,解码应为gbk格式
else:
print()
# 粘包问题出现的原因
# 1、tcp是流式协议,数据像水流一样粘在一起,没有任何边界区分
# 2、收数据没收干净,有残留,就会下一次结果混淆在一起
# 解决的核心法门就是:每次都收干净,不用有任何残留
# UDP不存在粘包问题(收不干净就丢失)
# 服务端:
from socket import *
import subprocess
import struct
'''
服务端应满足特点:
1、一直提供服务
2、并发提供服务
'''
server=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)
# 服务端应该做两件事
# 1、第一件事:循环的从链接池中取出链接请求与其建立双向链接,拿到链接对象
while True:
conn,client_addr=server.accept()
# 2、拿到链接对象,与其进行通信循环
while True:
try:
cmd=conn.recv(1024)
if len(cmd)==0:
break
obj=subprocess.Popen(cmd.decode('utf-8'),# pycharm默认格式为utf-8
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout_res=obj.stdout.read()
stderr_res=obj.stderr.read()
total_size=len(stdout_res)+len(stderr_res)
# 1、先发头信息(固定长度的bytes):对数据描述信息
header=struct.pack('i',total_size)# 将整形转换为bytes类型
conn.send(header)
# 2、再发真实的数据
conn.send(stdout_res+stderr_res)
except Exception:
break
conn.close
# 输出结果:
请输入命令>>>>tasklist
映像名称 PID 会话名 会话# 内存使用
========================= ======== ================ =========== ============
System Idle Process 0 Services 0 8 K
System 4 Services 0 60 K
Registry 124 Services 0 76,524 K
smss.exe 520 Services 0 860 K
csrss.exe 720 Services 0 4,672 K
wininit.exe 804 Services 0 5,432 K
csrss.exe 816 Console 1 6,740 K
services.exe 880 Services 0 9,708 K
lsass.exe 900 Services 0 23,332 K
svchost.exe 1020 Services 0 32,588 K
fontdrvhost.exe 528 Services 0 2,124 K
svchost.exe 724 Services 0 15,528 K
svchost.exe 1044 Services 0 9,776 K
WUDFHost.exe 1164 Services 0 14,392 K
。。。。。。。。。。。。
python.exe 12168 Console 1 35,292 K
conhost.exe 6412 Console 1 11,964 K
HxTsr.exe 4140 Console 1 34,228 K
RuntimeBroker.exe 3600 Console 1 12,696 K
cmd.exe 17256 Console 1 5,120 K
tasklist.exe 13656 Console 1 9,672 K
请输入命令>>>>dir
驱动器 D 中的卷是 所有文件软件丢到这里来
卷的序列号是 C0F9-5D0A
D:\新建文件夹\pythonProject1\shujialianxi\基于·TCP协议的套接字\基于TCP实现远程执行命令 的目录
2021/11/23 21:12 <DIR> .
2021/11/23 21:12 <DIR> ..
2021/11/23 21:12 1,475 客户端4.py
2021/11/23 20:41 1,408 服务端4.py
2021/11/23 20:51 549 解决粘包问题前客户端.py
2021/11/23 20:51 1,105 解决粘包问题前服务端4.py
4 个文件 4,537 字节
2 个目录 37,792,854,016 可用字节