7、實現遠程執行命令

TCP遠程執行命令

利用TCP協議的socket實現xshell的基本功能

服務端

#!/usr/bin/env python

# -*- coding: utf-8 -*-

# @Time : 2018/3/2 15:12

# @Author : CaiChangEn

# @Software: PyCharm

from socket import *

import subprocess

IP_PORT=('127.0.0.1',8080)

BUFFER_SIZE=65535

BACK_LOG=5

TCP_SERVER=socket(AF_INET,SOCK_STREAM)

TCP_SERVER.bind(IP_PORT)

TCP_SERVER.listen(BACK_LOG)

while True: # 循環接受新的客戶端連接

conn,addr=TCP_SERVER.accept() # 程序啟動將阻塞到此處,等待客戶端連接

print(addr) # 打印新客戶端的連接信息

while True: # 循環接受客戶端發送的消息

try: # 這個try是當客戶端和服務端建立連接之後,也沒有正常向服務器發送關閉連接的請求就直接關閉了客戶端,那麼此時就會報錯,所以加上try來捕獲異常,如果出現這個錯誤,就break

cmd=conn.recv(BUFFER_SIZE).decode('utf-8') # 接受客戶端發送的命令

res=subprocess.Popen(cmd,shell=True, # 執行客戶端發送來的命令

stdout=subprocess.PIPE, # 將正確輸出放入標準輸出管道

stdin=subprocess.PIPE, # 將輸入放入標準輸入管道

stderr=subprocess.PIPE) # 將錯誤輸出放入標準錯誤輸出管道

stderr=res.stderr.read() # 錯誤的結果賦值給stderr

stdout=res.stdout.read() # 正確的結果賦值給stdout

if stderr: # 如果標準錯誤輸出管道為True(不為空)表示這條命令執行有問題,所以管道里面有信息,即將這個結果返回給客戶端

res=stderr

elif stdout: # 如果標準輸出管道為True(不為空)表示這條命令執行成功,所以管道里面有信息,即將這個結果返回給客戶端

res=stdout

else: # 處理特殊情況,比如mkdir rmdir都不會有標準錯誤輸出和標準正確輸出的結果,那麼此時就說明命令執行成功,但是沒有返回結果,即返回執行成功字眼

res='執行成功'.encode('gbk') # windows默認是gbk編碼

conn.send(res) # 返回最終的命令執行結果

except Exception:

break

conn.close() # 關閉客戶端建立的TCP連接

客戶端

#!/usr/bin/env python

# -*- coding: utf-8 -*-

# @Time : 2018/3/2 15:12

# @Author : CaiChangEn

# @Software: PyCharm

from socket import *

IP_PORT=('127.0.0.1',8080)

BUFFER_SIZE=65535

BACK_LOG=5

TCP_CLIENT=socket(AF_INET,SOCK_STREAM)

TCP_CLIENT.connect(IP_PORT)

while True:

cmd=input('請輸入要執行的命令(Q/q退出):').strip() # 接受用戶輸入的命令

if not cmd:continue # 如果發送空字符就continue

if cmd == 'q' or cmd == 'Q':break

TCP_CLIENT.send(cmd.encode('utf-8')) # 將用戶輸入的命令發送給服務端

print(TCP_CLIENT.recv(BUFFER_SIZE).decode('gbk')) #接受客戶端的返回信息

TCP_CLIENT.close() #關閉TCP連接

執行結果

請輸入要執行的命令(Q/q退出):uname -a

Linux Linux 3.10.0-693.2.2.el7.x86_64 #1 SMP Tue Sep 12 22:26:13 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

請輸入要執行的命令(Q/q退出):netstat -tnlp

Active Internet connections (only servers)

Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name

tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 18060/nginx: worker

tcp 0 0 0.0.0.0:81 0.0.0.0:* LISTEN 1755/httpd

tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1380/sshd

tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 4044/python

tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 18060/nginx: worker

tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 18100/php-fpm: mast

tcp6 0 0 :::8080 :::* LISTEN 16194/docker-proxy

tcp6 0 0 :::5000 :::* LISTEN 2193/docker-proxy

tcp6 0 0 :::3306 :::* LISTEN 1179/mysqld

請輸入要執行的命令(Q/q退出):curl -I https://blog.doorta.com

% Total % Received % Xferd Average Speed Time Time Time Current

Dload Upload Total Spent Left Speed

0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0

請輸入要執行的命令(Q/q退出):

UDP遠程執行命令

利用UDP協議的socket實現xshell的基本功能

服務端

#!/usr/bin/env python

# -*- coding: utf-8 -*-

# @Time : 2018/3/2 15:12

# @Author : CaiChangEn

# @Software: PyCharm

from socket import *

import subprocess

IP_PORT=('127.0.0.1',8080)

BUFFER_SIZE=65535

BACK_LOG=5

UDP_SERVER=socket(AF_INET,SOCK_DGRAM)

UDP_SERVER.bind(IP_PORT)

while True:

cmd,addr=UDP_SERVER.recvfrom(BUFFER_SIZE)

res=subprocess.Popen(cmd.decode('utf-8'),shell=True,

stdout=subprocess.PIPE,

stdin=subprocess.PIPE,

stderr=subprocess.PIPE)

stderr=res.stderr.read()

stdout=res.stdout.read()

if stderr:

res=stderr

elif stdout:

res=stdout

else:

res='執行成功'.encode('gbk') # windows默認是gbk編碼

UDP_SERVER.sendto(res,addr) # 返回最終的命令執行結果

客戶端

#!/usr/bin/env python

# -*- coding: utf-8 -*-

# @Time : 2018/3/2 15:12

# @Author : CaiChangEn

# @Software: PyCharm

from socket import *

IP_PORT=('127.0.0.1',8080)

BUFFER_SIZE=65535

BACK_LOG=5

UDP_CLIENT=socket(AF_INET,SOCK_DGRAM)

while True:

cmd=input('請輸入要執行的命令(Q/q退出):').strip() # 接受用戶輸入的命令

if not cmd:continue # 如果發送空字符就continue

if cmd == 'q' or cmd == 'Q':break

UDP_CLIENT.sendto(cmd.encode('utf-8'),IP_PORT) # 將用戶輸入的命令發送給服務端

res,addr=UDP_CLIENT.recvfrom(BUFFER_SIZE)

print(res.decode('gbk')) #接受客戶端的返回信息

UDP_CLIENT.close() #關閉TCP連接

終極解決方案

#服務端

#!/usr/bin/env python

# -*- coding: utf-8 -*-

# @Time : 2018/3/9 16:31

# @Author : CaiChangEn

# @Software: PyCharm

from socket import *

import subprocess

import struct

IP_PORT=('0.0.0.0',5643)

BUFFER_SIZE=1024

BACK_LOG=5

TCP_SERVER=socket(AF_INET,SOCK_STREAM)

TCP_SERVER.bind(IP_PORT)

TCP_SERVER.listen(BACK_LOG)

while True:

conn,addr=TCP_SERVER.accept()

while True:

try:

recv_massage=conn.recv(BUFFER_SIZE).decode('utf-8')

cmd_res=subprocess.Popen(recv_massage,shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)

stdout=cmd_res.stdout.read()

stderr=cmd_res.stderr.read()

if stderr:

send_msg=stderr

elif stdout:

send_msg=stdout

elif not stderr or not stdout:

send_msg='execute successfully'.encode('gbk')

else:

send_msg='unknown operation'.encode('gbk')

struct_msg_length=struct.pack('i',len(send_msg)) # 這段內容就是將原數據的bytes長度做個統計

conn.send(struct_msg_length) # 將長度發送給客戶端

conn.send(send_msg) # 發送真實的數據

except Exception:

break

conn.close()

TCP_SERVER.close()

# 客戶端

#!/usr/bin/env python

# -*- coding: utf-8 -*-

# @Time : 2018/3/9 16:31

# @Author : CaiChangEn

# @Software: PyCharm

from socket import *

import struct

IP=input('請輸入服務端的公網IP:')

IP_PORT=(IP,5643)

BUFFER_SIZE=1024

BACK_LOG=5

TCP_CLIENT=socket(AF_INET,SOCK_STREAM)

TCP_CLIENT.connect(IP_PORT)

while True:

message=input('> ')

if not message:continue

if message == 'Q' or message == 'q':break

TCP_CLIENT.send(message.encode('utf-8'))

receive_buffer_size=struct.unpack('i',TCP_CLIENT.recv(4))[0] # 此處收到的是服務端要發送的真實數據的bytes的長度,0代表返回的元祖中的第一個值

receive_data=b'' # 用於拼接數據

receive_len=0 # 用戶統計已接受的數據的長度

while receive_len < receive_buffer_size: # 如果已接收的數據的等於總長度說明接收完成

receive_data+=TCP_CLIENT.recv(BUFFER_SIZE) # 每次已接受以自己的buffer_size為準的長度的數據,然後拼接數據

receive_len=len(receive_data) # 每次接收之後修改已接收數據的長度

try: # 判斷客戶端採用的編碼方式

print(receive_data.decode('utf-8'))

except Exception:

print(receive_data.decode('gbk'))

TCP_CLIENT.close()


分享到:


相關文章: