1. struct 模块
可以把一种数据类型 比如int 转化为固定长度的bytes类型~
import struct ret=struct.pack("i",4096) # i 表示即将把整数转化为固定长度的bytes类型 print(ret) # 会返回把整数转为bytes后的结果(int类型 整数 一般是转为4个字节) num=struct.unpack('i',ret) # 把刚才使用struct.pack()把整数转成的bytes类型,再使用struct.unpack()转为整数 只不过得到的是一个元组而已 print(num)
运行结果:
2. 使用struct模块解决黏包问题----仍然是server端下达命令,客户端远程执行命令的交互功能实现
# server.py import socket import struct sk=socket.socket() sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 为了防止重启服务时报错 sk.bind(('127.0.0.1',8080)) # 服务端绑定一个IP地址和一个端口号 sk.listen() # 服务端监听 conn,addr=sk.accept() # 开始连接 while True: cmd=input(">>>") # 用户输入,作为服务端给客户端下达的需要客户端执行的命令 cmd=conn.send(bytes(cmd.encode("utf-8"))) # 客户端收到命令时 也需要按照utf_8解码才可以拿到cmd num_bytes=conn.recv(4) # 可以直接拿到执行结果对应的字节数 借助struct.pack转为bytes类型之后的结果 # 之所以写 4 是因为整数(客户端发来的执行结果的字节数这个整数 使用struct模块转为bytes类型 占四个字节)拿到的是客户端发来的执行完命令的结果有多少个字节,服务端需要接收这些字节,才会对应该条命令的全部执行结果 num=struct.unpack("i",num_bytes)[0] # 把刚才字节数(len(std_out)+len(std_err))对应的bytes再转化为数字(也就是cmd执行完成之后的结果 需要多少个字节接收,代表的字节数) # (客户端使用struct.pack()把一个int类型转化为bytes类型 进行传输) ret=conn.recv(num).decode("gbk") # 服务端直接按照这些字节去接收就可以一次性把客户端执行命令的结果全部拿到,客户端即使连续发三个send(第一个对应的是执行结果所需的字节数,小数据包,第二三次分别是stdout stderr)即使三次send的结果合并到一起发送 # 但是由于服务器这边知道你告诉我的执行结果的字节数 这个数字 占有几个字节,我直接接收这个数字对应的字节数 就会直接拿到第一个send的结果,然后再把这个数字表示的执行命令的结果 有多少个字节直接recv一次性接收 就可以,并不会出现黏包现象 print(ret) conn.close() sk.close()
# client.py import socket import subprocess import struct sk=socket.socket() sk.connect(('127.0.0.1',8080)) while True: cmd=sk.recv(1024).decode("utf-8") # 客户端首先需要接收服务端发来的命令 print(cmd) res=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) # 客户端接收到服务端发来的命令后 借助subprocess模块执行 std_out=res.stdout.read() # read()方法返回的是字节类型 std_err=res.stderr.read() # stderr执行结果的字节数 num=len(std_out)+len(std_err) # 客户端借助subprocess模块执行结果所需要的字节数----int类型 num_bytes=struct.pack("i",num) # 把整数num(代表执行结果所需要的字节数)借助struct模块转为固定长度的bytes类型,进行传输, # 接收端只需要接收4个字节 就可以拿到执行结果对应的字节数这个整数了(因为struct模块对整数转化为bytes类型对应的就是4个字节)就可以拿到执行结果的字节数了 sk.send(num_bytes) # 客户端把执行命令的结果所对应的字节数发给服务端,告诉服务端在接收执行结果时需要按照这个字节数来接受,才能全部拿到执行命令的结果 # 但是服务端在接收这个num_bytes(执行命令结果所对应的字节数)时recv(4)因为整数借助struct.pack()转为bytes只占四个字节,服务端recv(4)就只会拿到这个数字 sk.send(std_out) # 把执行结果发送给服务器,服务端接受时 只需要接收num_of_bytes个字节数就可以啦 sk.send(std_err) sk.close()
运行结果: