python3-基于socket实现ssh,简单命令执行

服务端

import socket
import subprocess
import struct

IP = '127.0.0.1'
PORT = 8080
ADD = (IP, PORT)

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADD)
server.listen(5)
while True:
    conn, addr = server.accept()
    while True:
        try:  # 针对win系统
            #接受客户端指令
            date = conn.recv(1024)
            if not date: break  # 针对linux系统
            date = date.decode('utf-8')
            if date == 'q':
                conn.send('已退出'.encode('gbk'))
                break
           
            #系统执行命令,并保存显示用于返回客户端
            obj = subprocess.Popen(date, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, )
            print(obj)
            #命令显示
            out = obj.stdout.read()
            #错误显示
            err = obj.stderr.read()

            msg = err + out
            msg_size = len(out) + len(err)
            #固定长度(4)打包作为报头传输
            size = struct.pack('i', msg_size)
            conn.send(size + msg)
            # conn.send(msg)  # 以gbk 的bytes进行发送
            # conn.send(err)
        except ConnectionResetError:
            break
    conn.close()

客户端

import socket
import struct

IP = '127.0.0.1'
PORT = 8080
ADD = (IP, PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADD)
while True:
    msg = input('请输入命令(q退出)>>>>').strip()
    if not msg:continue #判断输入是否为空,确保不存在空传输等待
    client.send(msg.encode('utf-8'))
  
    # 以固定长度获取报头,即获取命令显示长度,以元组返回
    header = client.recv(4)
    msg_size = struct.unpack('i',header)[0]
    
    #循环在客户端上打印服务器命令显示
    res = b''
    recv_size = 0
    while recv_size < msg_size:
        data = client.recv(1024)
        recv_size += len(data)
        res += data
    print(res.decode('gbk'))

    # date = client.recv(1024)
    # print(date.decode('gbk'))
    
    if msg == 'q':
        break
client.close()

注:

1)粘包问题:连续的send发送,则会出现粘包,即多类数据组合成一个数据块。例:1,‘aaa’,9.9  ——> 1aaa9.9

    原因:TCP(transport control protocol,传输控制协议)是面向连接的,面向的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。

   解决:struct模块,将数据长度封装为一个统一长度的报头,首先进行发送。另一端进行recv时,先对报头进行解码,再以解码后的数字,对剩下的数据进行解码。

2)空传输:当输入空值返回,则会进行无限期等待,造成程序卡死

  原因:tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住。:而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头。

      解决:在传输前进行空值判断

3)ConnectionResetError(连接重置错误):一端强制关闭

  解决:win-异常捕捉,liunx-判断传输空值

4)使用循环取数据:防止数据过大而报错

猜你喜欢

转载自blog.csdn.net/qq_33961117/article/details/82350296