基于tcp协议传输数据的时候,如果接收端单次接收的数据长度小于发送的总长度,则会导致部分数据无法接收全,在第二次接受的时候会继续接受上次没接受完的数据,导致数据接收变混乱,这种现象就叫做黏包
应用程序通过操作系统来发送数据,接收方操作系统收到所有数据会缓存下来,然后发送给应用程序。如果应用程序一次没接受完,下次操作系统还会从缓存里继续发送剩下的数据。
黏包的解决办法就是,client客户端发送数据之前先发送一条数据,告诉服务端这次数据长度是多少,服务器接收到这个数据之后反馈成功,客户端在发送数据,服务端可以通过之前接受的数字来接受数据
#server 端
import socket
#基于tcp的socket
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
conn,addr=sk.accept() #建立链接
while 1:
cmd=input("输入doc命令>>>")
if cmd.lower()=="q":
conn.send(b'q')
break;
conn.send(cmd.encode("gbk"))
retl=conn.recv(1024).decode('gbk') #因为执行的是windows cmd命令,windows cmd默认是gbk的编码
#此处由于只接受1024(或任意指定的数字个字节),所以如果client端发来更多的信息,则无法一次接受完
#所以下次再调用recv方法的时候会继续接受上次未接受完的消息,这种现象叫做黏包
conn.send(b"ok")
ret=conn.recv(int(retl)).decode("gbk")
print(ret)
conn.close()
sk.close()
# client 端
import subprocess
import socket
# import
sk=socket.socket()
sk.connect(('127.0.0.1',8080))
while 1:
rec=sk.recv(1024).decode("gbk")
print("要执行的命令是",rec)
if rec=='q':break;
res=subprocess.Popen(rec,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) #此模块的Popen跟os 模块的popen函数区别是,这个正确和错误信息都能单独返回
#subprocess.PIPE是管道,可以临时存信息,信息只能取一次,取完之后就不能再取了
#为了解决黏包问题,那么在发送之前要先告诉服务器,下面要发送的数据有多大,服务器知道之后再发送包,服务器根绝包大小来动态接受,即可不再黏包
stdout=res.stdout.read() #管道信息只能取一次,所以要先缓存上,方便多次调用
stderr=res.stderr.read()
strlen=str(len(stdout)+len(stderr)).encode('gbk')
sk.send(strlen)
sk.recv(1024)
sk.send(stdout)#找到管道之后,读(read方法),即可获取管道里的消息
sk.send(stderr)#连续发送两次会合并成一次来发送
sk.close()
struct模块(解决黏包现象标准模块)
#struct模块解决黏包问题(解决黏包最标准的模块)
#把字符串转换成固定长度的bytes 转成固定长度之后可以接受固定 (4位)长度即可知道后边数据有多长,这样节省了一次交互就能防止黏包
# import struct
# ret=struct.pack('i',2048) #i代表 int ,意思要把数字转换成固定长度的bytes pack打包
# ret1=struct.pack('i',123)
# ret2=struct.pack('i',1)
# ret2=struct.pack('i',36)
# ret2=struct.pack('l',4096)
#
# # import sys
# # print(sys.version)
#
# print(ret,ret1,ret2)
# res=struct.unpack('i',ret2) #解包
# print(res[0])