【Python之旅】第五篇(四):基于Python Sockct多线程的简版SSH程序

还是继续延续篇五中前三节的例子,通过对代码的修修补补,把它改成一个可以在连接后就能在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端运行服务端程序

[email protected]:/mnt/hgfs/Python/day5$ python ssh_server5.py 
===>光标在此处处于等待状态

步骤2:Client端运行客户端程序并观察返回结果

[email protected]:/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连接软件。

时间: 2024-08-01 10:44:39

【Python之旅】第五篇(四):基于Python Sockct多线程的简版SSH程序的相关文章

Python开发【第五篇】:Python基础之迭代器、生成器

迭代器 一.迭代的概念 迭代器即迭代的工具,那什么是迭代呢?迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值 优点: 提供一种统一的.不依赖于索引的迭代方式 惰性计算,节省内存缺点: 无法获取长度(只有在next完毕才知道到底有几个值) 一次性的,只能往后走,不能往前退 1.为何要有迭代器? 对于序列类型:字符串.列表.元组,我们可以使用索引的方式迭代取出其包含的元素.但对于字典.集合.文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索

Python自动化 【第五篇】:Python基础-常用模块

目录 模块介绍 time和datetime模块 random os sys shutil json和pickle shelve xml处理 yaml处理 configparser hashlib re正则表达式 1.      模块介绍 1.1    定义 能够实现某个功能的代码集合(本质是py文件)  test.p的模块名是test包的定义:用来从逻辑上组织模块,本质就是一个目录(必须带有一个__init__.py文件) 1.2    导入方法 a) Import module b) Impo

Python开发【第五篇】:Python基础之杂货铺

字符串格式化 Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3101] This PEP proposes a new system for built-in string formatting operations, intended as a replacement for the existing '%' string formatting operator

Python开发【第五篇】:Python基础之2

字符串格式化 Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3101] This PEP proposes a new system for built-in string formatting operations, intended as a replacement for the existing '%' string formatting operator

python学习记录第五篇--遍历目录

#coding=utf-8'''@author: 简单遍历目录删除文件的小程序'''import os#查找文件操作def findFile(path): fileList=[] for rootPath,subRoot,fileName in os.walk(path): for sub in fileName: if os.path.isfile(os.path.join(rootPath,sub)): k=os.path.splitext(sub)[1].lower() if k in (

Python之路【第二篇】:Python基础(一)

Python之路[第二篇]:Python基础(一) 入门知识拾遗 一.作用域 对于变量的作用域,执行声明并在内存中存在,该变量就可以在下面的代码中使用. 1 2 3 if 1==1:     name = 'wupeiqi' print  name 下面的结论对吗? 外层变量,可以被内层变量使用 内层变量,无法被外层变量使用 二.三元运算 1 result = 值1 if 条件 else 值2 如果条件为真:result = 值1如果条件为假:result = 值2 三.进制 二进制,01 八进

Python之路【第九篇】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

Python之路[第九篇]:Python操作 RabbitMQ.Redis.Memcache.SQLAlchemy Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached基于一个存储键/值对的hashmap.其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信. Memc

Python之路【第九篇】:Python基础(26)——socket server

socketserver Python之路[第九篇]:Python基础(25)socket模块是单进程的,只能接受一个客户端的连接和请求,只有当该客户端断开的之后才能再接受来自其他客户端的连接和请求.当然我 们也可以通过python的多线程等模块自己写一个可以同时接收多个客户端连接和请求的socket.但是这完全没有必要,因为python标准库已经为 我们内置了一个多线程的socket模块socketserver,我们直接调用就可以了,完全没有必要重复造轮子. 我们只需简单改造一下之前的sock

Python之路【第一篇】:Python前世今生

Python简介 Python前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言的一种继承. 最新的TIOBE排行榜,Python赶超PHP占据第五!!! 由上图可见,Python整体呈上升趋势,反映出Python应用越来越广泛并且也逐渐得到业内的认可!!! Python可以应用于众多领域,如:数据分析.组件集成.网络服务.图像处理.数值计算和科学计算等众