【Python之旅】第五篇(四):基於Python Sockct多線程的簡版SSH程序

    仍是繼續延續篇五中前三節的例子,經過對代碼的修修補補,把它改爲一個能夠在鏈接後就能在Client端執行Server端命令的程序,因此就有點相似於SSH鏈接程序了。
python

    至於仍是用前面的例子來改嘛,是由於上課也一直這麼幹,並且老師也講得很是不錯,本身吸取後也做爲一個學習的記錄吧,由於確實是很是不錯的!
shell

    之因此能對前面的例子如這樣的修改,應當有這樣的思想:前面的例子中,Server端可以返回Client端輸入的字符串,那麼若是Client端輸入的是Linux的shell命令,Server端是否能夠執行這些命令而後返回執行的結果?併發

    因此是基於這樣的思想來進行對前面例子的改進的,達到這樣的效果,須要其它一些模塊的支持,固然也須要在細節上作一些改進,下面給出的代碼中都會有說比較詳細的說明。
ssh

    就看看這個簡版的SSH程序是個什麼樣的東東吧。
socket


Server端程序代碼:ide

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端運行服務端程序spa

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鏈接軟件。

相關文章
相關標籤/搜索