模拟远程SSH执行命令的编解码说明
模拟一个SSH“远程”执行命令并获取命令结果的一个程序:
1、在C/S架构下,当客户端与服务器建立连接(这里以TCP为例)后,二者可以不断的进行数据交互。SSH远程可以实现的效果是客户端输入命令可以在服务器中执行并且可以将结果返回给客户端。但是需要注意的一点事:客户端的“命令”在计算机看来仅仅是“字符串”而已,而真正需要执行的“命令”必须是操作系统能够识别的!也就是说,真正“执行命令”与“返回结果”的地方仍然是服务器端。在客户端我们只是“显示”出了一个这样的知执行假象而已。
那么,这样的一个SSH远程执行程序的具体流程是怎样的呢?
下图是这样的一个简单过程:
简单的过程说明:
对于客户端来讲,用户首先输入了str类型的命令command,然后程序将这个str类型的字符串encode成能够在网络中传输的bytes类型的数据并发送给服务器;服务器接收到以后将其重新解码为str,然后在本段“执行”这段代码生成str类型的结果,接着再进行编码传给客户端,客户端接收到以后解码为人能识别的str类型最终输出到屏幕上。
2、这个过程有两个大问题:一个是服务器端是如何进行“代码执行”的,另一个就是客户端与服务器端str与bytes格式数据的编解码问题。
2.1、对于第一个问题,我们利用subprocess模块下的Popen方法可以将str类型的“虚假命令”转换为操作系统能够识别的“真是命令”:
import subprocess cmd = input('>>>:') obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) print(obj.stdout.read().decode('gbk')) print(obj.stderr.read().decode('gbk'))
这里有几点需要说明:
(1)可以把obj看成是一个管道,它一次性的、毫无保留将所有执行结果都拿到,再一次取值时里面没有了数据,这就像一个“管道”一样,里面的数据全部取完后就“无所保留”了。
(2)关于输出结果解码的问题:subprocess模块下的Popen方法在不同的操作系统下结果的编码方式不同:linux下为utf-8模式,windows下为gbk模式。由于本例是在windows操作系统下进行的,所以我们在输出是要进行gbk的方式解码才能看到结果。
(3)最终的结果包含正确的信息stdout与错误的结果stderr(当用户输入一个不存在的命令时产生)。
2.2、第二个编解码问题可以看下图具体的描述:
这里需要注意的一点是:本例是在windows操作系统下执行的,所以result的编码方式要以gbk模式进行,这样才不会在客户端中产生乱码。
程序的代码以及运行结果为:
import socket import subprocess server_whw = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server_whw.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) server_whw.bind(('127.0.0.1',8008)) server_whw.listen(5) print('Starting......') conn,client_addr = server_whw.accept() while 1: try: cmd = conn.recv(8096) #将从客户端传来的命令信息进行处理 obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) #将结果返回给客户端 stdout = obj.stdout.read().decode('gbk') stderr = obj.stderr.read().decode('gbk') std1 = stdout.encode('gbk') std2 = stderr.encode('gbk') conn.send(std1) conn.send(std2) except ConnectionResetError: print('连接断开') break conn.close() server_whw.close()
import socket client_whw = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client_whw.connect(('127.0.0.1',8008)) while 1: cmd = input('请输入命令:') if not cmd: continue client_whw.send(cmd.encode('utf-8')) data = client_whw.recv(1024) print('命令结果:\n',data.decode('gbk')) # client_whw.close()
程序演示:
当然,最终的结果出现了粘包问题~需要进行进一步的处理🤗