远程执行命令的C/S架构软件,粘包问题解决
远程执行命令C/S架构的软件
什么是套接字:
套接字位于应用层与传输层之间,将传输层以下的协议都封装成接口
对于应用层来说只需要调节用套接字的接口,写出的程序自然是遵循tcp或者ubp等协议
C/S
server必须遵循:
1、稳定运行,对外一直提供服务
2、服务必须绑定ip和端口
TCP协议又称为流式协议,是像水一样多个数据一起流动,操作系统会将短时间,小数量的所要传的数据,会一次性打包一起传.而其过程形
成的情况称为粘包,可以用报头方式对数据进行准确划分
通过模块subprocess可以达成用户端控制服务器进行远程的命令控制,多个命令可以通过分号进行区分隔开多个命令.
subprocess执行好自动是bytes二进制模式,默认格式为"gbk"格式,用"gbk"进行转换
当发送的内容大于设置的最大字节数时,后面发送的指令回馈的内容在操作系统内存中会排在最后面,显示的时之前还未从操作系统中,
拿完的数据内容,可以通过struct模块与循环,解决此问题,在服务器与用户端传输中,服务器将反馈的数据用struct模块将数据的长度
进行数字转二进制类型
(struct.pack("i",内容))长度固定时4,作为报头传输给用户端,
用户端先读取长度为4的报头,再借用struct模块,将数据解包,解包后的内容是元组显示,然后利用索引取值.
header = client.recv(4)
total_size = struct.unpack("i",header)[0]
然后再循环接收真实数据,通过"gbk"解码将内容显示出.(其中有个BUG,就是内容过长的时候用struct模块来转化二进制会报错,所以需
要额外处理)
通用的报头内容为:
header_dic= {
"filename":"a.txt", #文件名
"total_size":len(stdout)+len(stderr)#真实数据长度
"hash":"xsdf3241" #hash值
}
字典内容的报头:字典用json模块将内容成为字符串可是,再用encode转为二进制,再用len计算出报头长度,最后用struct将藏毒转为二进
制发送给用户
用户接收后以反向推算获得想要的内容
from socket import * import subprocess import json import struct client = socket() client.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) client.bind(("127.0.0.1",8080)) client.listen(5) while True: conn,addr = client.accept() print("用户来访",addr) while True: try: cmd=conn.recv(1024) if len(cmd) == 0:break obj=subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout = obj.stdout.read() stderr = obj.stderr.read() #制作字典报头 header_dic={ "filename":"a.txt", "total_size":len(stdout)+len(stderr), "hash":"xsdf3241" } #json模块转化为字符串格式 header_json=json.dumps(header_dic) #将json转为二进制bytes header_bytes=header_json.encode("utf-8") #将二进制计算长度 header_len=len(header_bytes) #再用struct模块将长度转为二进制 header_struct=struct.pack("i",header_len) #发送给客户端 conn.send(header_struct) conn.send(header_bytes) conn.send(stdout) conn.send(stderr) except ConnectionResetError: break conn.close() client.close()
import struct from socket import * import json client=socket(AF_INET,SOCK_STREAM) client.connect(("127.0.0.1",8080)) while True: cmd = input(">>:").strip() if len(cmd) == 0:continue client.send(cmd.encode("utf-8")) #先接收4个字节长度的二进制 header_struct=client.recv(4) #将4个字节内的字节长度取出 header_len = struct.unpack("i",header_struct)[0] #接收报头 header_bytes=client.recv(header_len) #再将header_bytes二进制内容解码,成为json文件 header_json=header_bytes.decode('utf-8') #再将解码后的字符串进行json模块读取 header_dic=json.loads(header_json) #将字典内的需要内容真实数据长度取出 total_size=header_dic["total_size"] #接收真实数据 recv_size=0 res=b"" while recv_size<total_size: data=client.recv(1024) res+=data recv_size+=len(data) print(res.decode("gbk")) client.close()