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()
閱讀更多 動漫資深愛好者和IT 的文章