发布时间:2019-09-02 07:46:23编辑:auto阅读(1407)
还是继续延续篇五中前三节的例子,通过对代码的修修补补,把它改成一个可以在连接后就能在Client端执行Server端命令的程序,所以就有点类似于SSH连接程序了。
至于还是用前面的例子来改嘛,是因为上课也一直这么干,而且老师也讲得非常不错,自己吸收后也作为一个学习的记录吧,因为确实是非常不错的!
之所以能对前面的例子如这样的修改,应当有这样的思想:前面的例子中,Server端能够返回Client端输入的字符串,那么如果Client端输入的是Linux的shell命令,Server端是否可以执行这些命令然后返回执行的结果?
所以是基于这样的思想来进行对前面例子的改进的,达到这样的效果,需要其它一些模块的支持,当然也需要在细节上做一些改进,下面给出的代码中都会有说比较详细的说明。
就看看这个简版的SSH程序是个什么样的东东吧。
Server端程序代码:
import SocketServer import commands #使用其中的getstatusoutput()函数,让Server端可以识别Client端发送过来的命令并执行 import time #主要使用其中的time.sleep()函数,用来解决Server端发送数据的“连块”问题 class MySockServer(SocketServer.BaseRequestHandler): def handle(self): print 'Got a new connection from', self.client_address while True: cmd = self.request.recv(1024) if not cmd: print 'Last connection with:',self.client_address break cmd_result = commands.getstatusoutput(cmd) #获取Client端的指令并执行,返回结果是一个存储两个元素的元组,第一个元素为0表示成功执行,第二个元素则是命令的执行结果 self.request.send(str(len(cmd_result[1]))) #发送命令执行结果的大小长度,Client端要想接收任意大小的执行结果,就需要根据命令执行结果的大小来选择策略,这里需要注意的是,发送的数据是字符串,所以需要作类型转换 time.sleep(0.2) #睡眠0.2s,是为了解决“连块”的问题 self.request.sendall(cmd_result[1]) #发送命令执行结果 if __name__ == '__main__': HOST = '' PORT = 50007 s = SocketServer.ThreadingTCPServer((HOST, PORT), MySockServer) s.serve_forever()
Client端程序代码:
import socket HOST = '192.168.1.13' PORT = 50007 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) def data_all(obj, lenth_size): data = '' #用来存储每一次循环时socket接收的数据,解决socket大概两万多字节的缓冲瓶颈限制 while lenth_size != 0: #如果接收的数据长度不为0,开始执行接收数据处理策略 if lenth_size <= 4096: #这里以4096为单位块,作为每次的数据处理量大小 data_recv = obj.recv(lenth_size) #通过recv()接收数据 lenth_size = 0 #通过这一步的处理,数据全部接收完毕,置lenth_size为0,结束循环,完成数据的接收处理工作 else: data_recv = obj.recv(4096) #以4096为单位块,一次接收4096的数据量大小 lenth_size -= 4096 #处理完一次4096字节的数据后,将lenth_size减去4096 data += data_recv #判断外层,用本地的data来存储接收到的数据,因为本地的data大小没有限制,所以不存在data饱和无法继续存储数据的问题,但前面socket的recv()函数一次最多只能接收的数据量大小是有限制的,这取决于socket的缓冲区大小,因此data_all函数的作用就是通过使用多次recv()函数,并且每次接收一定量的数据后就进行本地存储,直到把所有的数据都接收完毕 return data while True: user_input = raw_input('cmd to send:').strip() if len(user_input) == 0:continue s.sendall(user_input) data_size = int(s.recv(1024)) #得到命令执行结果的大小长度,因为发送过来的数据是字符串,所以这里要作类型转换 print '\033[32;1mdata size:\033[0m',data_size #打印命令执行结果的大小 result = data_all(s, data_size) #通过data_all函数来执行相应的数据接收处理策略 print result #打印命令执行结果 s.close() #关闭套接字
演示:
步骤1:Server端运行服务端程序
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python ssh_server5.py ===>光标在此处处于等待状态
步骤2:Client端运行客户端程序并观察返回结果
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python ssh_client5.py cmd to send:df data size: 502 ===>命令执行结果的大小 df: "/var/lib/lightdm/.gvfs": 权限不够 ===>命令的执行结果 文件系统 1K-块 已用 可用 已用% 挂载点 /dev/sda3 8781832 3458300 4877428 42% / udev 493784 4 493780 1% /dev tmpfs 201040 784 200256 1% /run none 5120 0 5120 0% /run/lock none 502592 144 502448 1% /run/shm /dev/sda1 93207 30139 58256 35% /boot .host:/ 162256468 152391980 9864488 94% /mnt/hgfs cmd to send:pwd ===>执行pwd命令 data size: 21 /mnt/hgfs/Python/day5 cmd to send:ls data size: 357 [1]sec_4_ver1(单线程,非交互式) [2]sec_4_ver2(单线程,交互式,阻塞模式一般演示) [3]sec_4_ver3(单线程,交互式,阻塞模式进阶演示) [4]sec_4_ver3(单线程,交互式,多并发) client4.py duotiao_jian_biao3.py jian_biao2.py my_conn1.py server4.py ssh_client5.py ssh_server5.py Thread_socket_server4.py cmd to send:top -bn 3 ===>注意该命令的执行结果(大小)已经超出了socket的缓冲区大小,因此上面Client端中的程序代码主要就是为了解决该问题,这也是Client端的关键所在 data size: 34378 ===>该命令的执行结果大小为三万多字节,超出了socket缓冲区,socket的recv()函数是无法一次接收那么多数据的 …… 省略输出结果 cmd to send:ls ===>继续执行命令,返回结果正常 data size: 357 [1]sec_4_ver1(单线程,非交互式) [2]sec_4_ver2(单线程,交互式,阻塞模式一般演示) [3]sec_4_ver3(单线程,交互式,阻塞模式进阶演示) [4]sec_4_ver3(单线程,交互式,多并发) client4.py duotiao_jian_biao3.py jian_biao2.py my_conn1.py server4.py ssh_client5.py ssh_server5.py Thread_socket_server4.py
可以看到上面两个程序已经比较好的实现了命令执行的功能了,问题主要是集中在:
1.Server端发送数据的“连块”问题,即发送两次数据时,如果发送间隔比较短,socket会把两次发送的数据放在一起来发送,这里通过time.sleep()函数来解决。
2.socket的缓冲区大小问题,即当执行top -bn 3这样执行结果长度大的命令时,socket缓冲区一次是无法存储这么多数据的,所以只能分多次来接收数据,这样就会在Client端带来一定的问题,比如命令执行的不同步等,解决的方法是通过用循环接收的方法来进行本地存储Server端发送的数据。
当然,如果要执行man等查询方面的命令,上面的程序也是无法做到的,所以这里说,这只是一个简版的SSH程序,作为对Python socket的学习就好了,真要用SSH的话,那还不如直接下个ssh连接软件。
上一篇: c++ python交互之boost.p
下一篇: Windows 8.1之系统镜像备份功能
47867
46433
37321
34767
29336
25998
24951
19970
19568
18056
5810°
6436°
5952°
5978°
7083°
5928°
5969°
6461°
6425°
7803°