1. 程式人生 > 程式設計 >python基於socket模擬實現ssh遠端執行命令

python基於socket模擬實現ssh遠端執行命令

一、subprocess.Popen()

subprocess模組定義了一個類: 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)

我們只需要關注其中幾個引數:

  • args:

args引數。可以是一個字串,可以是一個包含程式引數的列表。要執行的程式一般就是這個列表的第一項,或者是字串本身。

  • shell=True:

在Linux下,當shell=True時,如果arg是個字串,就使用shell來解釋執行這個字串。如果args是個列表,則第一項被視為命令,其餘的都視為是給shell本身的引數。也就是說,等效於:
subprocess.Popen(['/bin/sh','-c',args[0],args[1],...])

  • stdin stdout和stderr:

stdin stdout和stderr,分別表示子程式的標準輸入、標準輸出和標準錯誤。可選的值有PIPE或者一個有效的檔案描述符(其實是個正整數)或者一個檔案物件,還有None。如果是PIPE,則表示需要建立一個新的管道,如果是None,不會做任何重定向工作,子程序的檔案描述符會繼承父程序的。另外,stderr的值還可以是STDOUT,表示子程序的標準錯誤也輸出到標準輸出。

二、粘包現象

所謂粘包問題主要還是因為接收方不知道訊息之間的界限,還有系統快取區的問題,時間差的原因,不知道一次性提取多少位元組的資料所造成的。

須知:只有TCP有粘包現象,UDP永遠不會粘包

粘包不一定會發生,如果發生了:1.可能是在客戶端已經粘了;2.客戶端沒有粘,可能是在服務端粘了

緩衝區的作用:儲存少量資料

如果你的網路出現短暫的異常或者波動,接收資料就會出現短暫的中斷,影響你的下載或者上傳的效率。但是,緩

衝區解決了上傳下載的傳輸效率的問題,帶來了黏包問題。

收發的本質:不一定是一收一發

三、為什麼出現粘包?

1,接收方沒有及時接收緩衝區的包,造成多個包接收(客戶端傳送了一段資料,服務端只收了一小部分,服務端下次再收的時候還是從緩衝區拿上次遺留的資料,產生粘包)recv會產生黏包(如果recv接受的資料量(1024)小於傳送的資料量,第一次只能接收規定的資料量1024,第二次接收剩餘的資料量)

2,傳送端需要等緩衝區滿才傳送出去,造成粘包(傳送資料時間間隔很短,資料也很小,會合到一起,產生粘包)send 也可能發生粘包現象。(連續send少量的資料發到輸出緩衝區,由於緩衝區的機制,也可能在緩衝區中不斷積壓,多次寫入的資料被一次性發送到網路)

出現粘包現象的程式碼例項

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))

# 監聽
phone.listen(5)

# 通訊迴圈
while True:
  # 接收客戶端連線請求
  conn,client_addr = phone.accept()
  while True:
    # 接收客戶端資料/命令
    cmd = conn.recv(1024)
    if not cmd:
      break
    # 建立管道
    obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    stdout = obj.stdout.read()
    stderr = obj.stderr.read()
    # 向客戶端傳送資料
    conn.send(stdout)
    conn.send(stderr)
  # 結束連線
  conn.close()

# 關閉套接字
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
  # 給服務端傳送資料/命令
  phone.send(cmd.encode('utf-8'))
  # 接收服務端資料/命令
  data = phone.recv(1024)
  print(data.decode('utf-8'))

# 關閉套接字
phone.close()

粘包現象執行結果

python基於socket模擬實現ssh遠端執行命令

python基於socket模擬實現ssh遠端執行命令

python基於socket模擬實現ssh遠端執行命令

可以觀察到執行兩次ls命令後,服務端返回的仍然是ifconfig命令的結果,最後一次ls命令的末尾才出現ls命令返回的部分結果

四、解決粘包問題的程式碼例項
server. py

import socket
import subprocess
import json
import struct

# 建立
phone = socket.socket(socket.AF_INET,client_addr = phone.accept()
  while True:
    # 接收客戶端資料/命令
    cmd = conn.recv(1024)
    if not cmd:
      continue
    # 建立資料流管道
    obj = subprocess.Popen(cmd.decode('utf-8'),stderr=subprocess.PIPE)
    stdout = obj.stdout.read()
    stderr = obj.stderr.read()
    # 向客戶端傳送資料

    # 解決粘包問題
    # 1.製作固定長度的報頭
    header_dic = {
      'filename': 'a.txt','total_size': len(stdout)+len(stderr)
    }
    # 序列化報頭
    header_json = json.dumps(header_dic) # 序列化為byte位元組流型別
    header_bytes = header_json.encode('utf-8') # 編碼為utf-8(Mac系統)
    # 2.先發送報頭的長度
    # 2.1 將byte型別的長度打包成4位int
    conn.send(struct.pack('i',len(header_bytes)))
    # 2.2 再發報頭
    conn.send(header_bytes)
    # 2.3 再發真實資料
    conn.send(stdout)
    conn.send(stderr)
  # 結束連線
  conn.close()

# 關閉套接字
phone.close()

client. py

import socket
import struct
import json

# 建立
phone = socket.socket(socket.AF_INET,8081))
while True:
  cmd = input('>>> ').strip()
  if not cmd:
    continue
  if cmd == 'quit':
    break
  # 給服務端傳送命令
  phone.send(cmd.encode('utf-8'))
  # 接收服務端資料

  # 1.先收報頭長度
  obj = phone.recv(4)
  header_size = struct.unpack('i',obj)[0]
  # 2.收報頭
  header_bytes = phone.recv(header_size)
  # 3.從報頭中解析出資料的真實資訊(報頭字典)
  header_json = header_bytes.decode('utf-8')
  header_dic = json.loads(header_json)
  total_size = header_dic['total_size']
  # 4.接受真實資料
  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'))

# 關閉套接字
phone.close()

以上就是python基於socket模擬實現ssh遠端執行命令的詳細內容,更多關於python基於socket實現ssh遠端執行命令的資料請關注我們其它相關文章!

以上就是python基於socket模擬實現ssh遠端執行命令的詳細內容,更多關於python socket的資料請關注我們其它相關文章!