python基于socket模擬實(shí)現(xiàn)ssh遠(yuǎn)程執(zhí)行命令
一、subprocess.Popen()
subprocess模塊定義了一個(gè)類: Popen
類原型:
class subprocess.Popen( args, bufsize = 0, executable = None, stdin = None, stdout = None, stderr = None, preexec_fn = None, close_fds = False, shell = False, cwd = None, env = None, universal_newlines = False, startupinfo = None, creationflags = 0)
我們只需要關(guān)注其中幾個(gè)參數(shù):
- args:
args參數(shù)。可以是一個(gè)字符串,可以是一個(gè)包含程序參數(shù)的列表。要執(zhí)行的程序一般就是這個(gè)列表的第一項(xiàng),或者是字符串本身。
- shell=True:
在Linux下,當(dāng)shell=True時(shí),如果arg是個(gè)字符串,就使用shell來解釋執(zhí)行這個(gè)字符串。如果args是個(gè)列表,則第一項(xiàng)被視為命令,其余的都視為是給shell本身的參數(shù)。也就是說,等效于:
subprocess.Popen(['/bin/sh', '-c', args[0], args[1], ...])
- stdin stdout和stderr:
stdin stdout和stderr,分別表示子程序的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤。可選的值有PIPE或者一個(gè)有效的文件描述符(其實(shí)是個(gè)正整數(shù))或者一個(gè)文件對(duì)象,還有None。如果是PIPE,則表示需要?jiǎng)?chuàng)建一個(gè)新的管道,如果是None,不會(huì)做任何重定向工作,子進(jìn)程的文件描述符會(huì)繼承父進(jìn)程的。另外,stderr的值還可以是STDOUT,表示子進(jìn)程的標(biāo)準(zhǔn)錯(cuò)誤也輸出到標(biāo)準(zhǔn)輸出。
二、粘包現(xiàn)象
所謂粘包問題主要還是因?yàn)榻邮辗讲恢老⒅g的界限,還有系統(tǒng)緩存區(qū)的問題,時(shí)間差的原因,不知道一次性提取多少字節(jié)的數(shù)據(jù)所造成的。
須知:只有TCP有粘包現(xiàn)象,UDP永遠(yuǎn)不會(huì)粘包
粘包不一定會(huì)發(fā)生,如果發(fā)生了:1.可能是在客戶端已經(jīng)粘了;2.客戶端沒有粘,可能是在服務(wù)端粘了
緩沖區(qū)的作用:存儲(chǔ)少量數(shù)據(jù)
如果你的網(wǎng)絡(luò)出現(xiàn)短暫的異?;蛘卟▌?dòng),接收數(shù)據(jù)就會(huì)出現(xiàn)短暫的中斷,影響你的下載或者上傳的效率。但是,緩
沖區(qū)解決了上傳下載的傳輸效率的問題,帶來了黏包問題。
收發(fā)的本質(zhì):不一定是一收一發(fā)
三、為什么出現(xiàn)粘包?
1,接收方?jīng)]有及時(shí)接收緩沖區(qū)的包,造成多個(gè)包接收(客戶端發(fā)送了一段數(shù)據(jù),服務(wù)端只收了一小部分,服務(wù)端下次再收的時(shí)候還是從緩沖區(qū)拿上次遺留的數(shù)據(jù),產(chǎn)生粘包)recv會(huì)產(chǎn)生黏包(如果recv接受的數(shù)據(jù)量(1024)小于發(fā)送的數(shù)據(jù)量,第一次只能接收規(guī)定的數(shù)據(jù)量1024,第二次接收剩余的數(shù)據(jù)量)
2,發(fā)送端需要等緩沖區(qū)滿才發(fā)送出去,造成粘包(發(fā)送數(shù)據(jù)時(shí)間間隔很短,數(shù)據(jù)也很小,會(huì)合到一起,產(chǎn)生粘包)send 也可能發(fā)生粘包現(xiàn)象。(連續(xù)send少量的數(shù)據(jù)發(fā)到輸出緩沖區(qū),由于緩沖區(qū)的機(jī)制,也可能在緩沖區(qū)中不斷積壓,多次寫入的數(shù)據(jù)被一次性發(fā)送到網(wǎng)絡(luò))
出現(xiàn)粘包現(xiàn)象的代碼實(shí)例
server. py
import socket
import subprocess
# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 綁定
phone.bind(('127.0.0.1', 8081))
# 監(jiān)聽
phone.listen(5)
# 通信循環(huán)
while True:
# 接收客戶端連接請(qǐng)求
conn, client_addr = phone.accept()
while True:
# 接收客戶端數(shù)據(jù)/命令
cmd = conn.recv(1024)
if not cmd:
break
# 創(chuàng)建管道
obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
# 向客戶端發(fā)送數(shù)據(jù)
conn.send(stdout)
conn.send(stderr)
# 結(jié)束連接
conn.close()
# 關(guān)閉套接字
phone.close()
client. py
import socket
# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 連接
phone.connect(('127.0.0.1', 8081))
while True:
cmd = input('>>> ').strip()
if not cmd:
continue
if cmd == 'quit':
break
# 給服務(wù)端發(fā)送數(shù)據(jù)/命令
phone.send(cmd.encode('utf-8'))
# 接收服務(wù)端數(shù)據(jù)/命令
data = phone.recv(1024)
print(data.decode('utf-8'))
# 關(guān)閉套接字
phone.close()
粘包現(xiàn)象運(yùn)行結(jié)果



可以觀察到執(zhí)行兩次ls命令后,服務(wù)端返回的仍然是ifconfig命令的結(jié)果,最后一次ls命令的末尾才出現(xiàn)ls命令返回的部分結(jié)果
四、解決粘包問題的代碼實(shí)例
server. py
import socket
import subprocess
import json
import struct
# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 綁定
phone.bind(('127.0.0.1', 8081))
# 監(jiān)聽
phone.listen(5)
# 通信循環(huán)
while True:
# 接收客戶端連接請(qǐng)求
conn, client_addr = phone.accept()
while True:
# 接收客戶端數(shù)據(jù)/命令
cmd = conn.recv(1024)
if not cmd:
continue
# 創(chuàng)建數(shù)據(jù)流管道
obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
# 向客戶端發(fā)送數(shù)據(jù)
# 解決粘包問題
# 1.制作固定長(zhǎng)度的報(bào)頭
header_dic = {
'filename': 'a.txt',
'total_size': len(stdout)+len(stderr)
}
# 序列化報(bào)頭
header_json = json.dumps(header_dic) # 序列化為byte字節(jié)流類型
header_bytes = header_json.encode('utf-8') # 編碼為utf-8(Mac系統(tǒng))
# 2.先發(fā)送報(bào)頭的長(zhǎng)度
# 2.1 將byte類型的長(zhǎng)度打包成4位int
conn.send(struct.pack('i', len(header_bytes)))
# 2.2 再發(fā)報(bào)頭
conn.send(header_bytes)
# 2.3 再發(fā)真實(shí)數(shù)據(jù)
conn.send(stdout)
conn.send(stderr)
# 結(jié)束連接
conn.close()
# 關(guān)閉套接字
phone.close()
client. py
import socket
import struct
import json
# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 連接
phone.connect(('127.0.0.1', 8081))
while True:
cmd = input('>>> ').strip()
if not cmd:
continue
if cmd == 'quit':
break
# 給服務(wù)端發(fā)送命令
phone.send(cmd.encode('utf-8'))
# 接收服務(wù)端數(shù)據(jù)
# 1.先收?qǐng)?bào)頭長(zhǎng)度
obj = phone.recv(4)
header_size = struct.unpack('i', obj)[0]
# 2.收?qǐng)?bào)頭
header_bytes = phone.recv(header_size)
# 3.從報(bào)頭中解析出數(shù)據(jù)的真實(shí)信息(報(bào)頭字典)
header_json = header_bytes.decode('utf-8')
header_dic = json.loads(header_json)
total_size = header_dic['total_size']
# 4.接受真實(shí)數(shù)據(jù)
recv_size = 0
recv_data = b''
while recv_size < total_size:
res = phone.recv(1024)
recv_data += res
recv_size += len(res)
print(recv_data.decode('utf-8'))
# 關(guān)閉套接字
phone.close()
以上就是python基于socket模擬實(shí)現(xiàn)ssh遠(yuǎn)程執(zhí)行命令的詳細(xì)內(nèi)容,更多關(guān)于python基于socket實(shí)現(xiàn)ssh遠(yuǎn)程執(zhí)行命令的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
以上就是python基于socket模擬實(shí)現(xiàn)ssh遠(yuǎn)程執(zhí)行命令的詳細(xì)內(nèi)容,更多關(guān)于python socket的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Python 聊聊socket中的listen()參數(shù)(數(shù)字)到底代表什么
- Python Socket多線程并發(fā)原理及實(shí)現(xiàn)
- Python基于Socket實(shí)現(xiàn)簡(jiǎn)易多人聊天室的示例代碼
- Python WebSocket長(zhǎng)連接心跳與短連接的示例
- 用Python進(jìn)行websocket接口測(cè)試
- Python使用socket模塊實(shí)現(xiàn)簡(jiǎn)單tcp通信
- python Socket網(wǎng)絡(luò)編程實(shí)現(xiàn)C/S模式和P2P
- Python基礎(chǔ)之Socket通信原理
相關(guān)文章
Python入門教程(二十)Python的Lambda表達(dá)式
這篇文章主要介紹了Python入門教程(二十)Python的Lambda表達(dá)式,lambda表達(dá)式是一行的函數(shù)。它們?cè)谄渌Z言中也被稱為匿名函數(shù),lambda表達(dá)式非常有用,可以讓代碼簡(jiǎn)單,簡(jiǎn)潔,需要的朋友可以參考下2023-04-04
解決win64 Python下安裝PIL出錯(cuò)問題(圖解)
這篇文章主要介紹了解決win64 Python下安裝PIL出錯(cuò)問題,文中的解決方法也很簡(jiǎn)單,需要的朋友參考下吧2018-09-09

