远程执行命令的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()
客户端

 

posted @ 2018-07-09 18:52  指尖市场  阅读(167)  评论(0编辑  收藏  举报