使用网络编程编写一个类似ssh的应用
客户端
#!/usr/bin/python3
import socket
import struct
import sys
import time
import argparse
# 创建解析对象
parser = argparse.ArgumentParser()
# 添加命令行参数、选项
parser.add_argument(
"-c",
"--client",
default="127.0.0.1",
help="run in client mode, connecting to <host>",
)
parser.add_argument(
"-p",
"--port",
default=8888,
help="server port to listen on/connect to",
)
# 进行解析
args = parser.parse_args()
IP_ADDRESS = (args.client, args.port)
BUF_SIZE = 1024
try:
client =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect_ex(IP_ADDRESS)
while True:
cmd = input("tcp>").strip()
if not cmd:
continue
client.send(cmd.encode("utf-8"))
res_header = client.recv(4)
total_size = struct.unpack("i", res_header)[0]
recv_size = 0
while recv_size < total_size:
res = client.recv(BUF_SIZE)
recv_size += len(res)
sys.stdout.write(res.decode("utf-8"))
sys.stdout.write("\n")
except KeyboardInterrupt:
print("\n断开连接\n")
except Exception as e:
print(e)
服务端
单客户端连接
#!/usr/bin/python3
import sys
import struct
import socket
import subprocess
import argparse
# 创建解析对象
parser = argparse.ArgumentParser()
# 添加命令行参数、选项
parser.add_argument(
"-s",
"--server",
default="",
help="run in server mode",
)
parser.add_argument(
"-p",
"--port",
default=8888,
help="server port to listen on/connect to",
)
# 进行解析
args = parser.parse_args()
IP_ADDRESS = (args.server, args.port)
BUF_SIZE = 1024
try:
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(IP_ADDRESS)
server.listen(5)
while True:
conn, addr = server.accept()
while True:
try:
cmd = conn.recv(BUF_SIZE)
if not cmd:
break
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
res_stdout = obj.stdout.read()
res_stderr = obj.stderr.read()
res_len = len(res_stdout) + len(res_stderr)
# 制作TCP头,利用struct的模块的pack将返回内容的长度打包成4个byte发送
res_header = struct.pack('i', res_len)
# 利用TCP流式传输的特性
conn.send(res_header)
conn.send(res_stdout)
conn.send(res_stderr)
except Exception as e:
sys.stderr.write(f'error message:{e}')
conn.close()
except KeyboardInterrupt:
print()
多客户端连接(多线程)
#!/usr/bin/python3
import sys
import struct
import socketserver
import subprocess
import argparse
# 创建解析对象
parser = argparse.ArgumentParser()
# 添加命令行参数、选项
parser.add_argument(
"-s",
"--server",
default="",
help="run in server mode",
)
parser.add_argument(
"-p",
"--port",
default=8888,
help="server port to listen on/connect to",
)
# 进行解析
args = parser.parse_args()
IP_ADDRESS = (args.server, args.port)
BUF_SIZE = 1024
class MyRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
"""
TCP中的conn-->self.request
TCP中的addr-->self.client_address
"""
while 1:
try:
cmd = self.request.recv(BUF_SIZE)
if not cmd: break
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
res_stdout = obj.stdout.read()
res_stderr = obj.stderr.read()
# 将报头的长度进行打包
res_header = struct.pack('i', len(res_stdout) + len(res_stderr))
# 利用TCP流式传输特性
self.request.send(res_header)
self.request.send(res_stdout)
self.request.send(res_stderr)
except Exception as e:
sys.stderr.write(f'error message:{e}')
self.request.close()
try:
s = socketserver.ThreadingTCPServer(IP_ADDRESS, MyRequestHandler)
s.serve_forever()
except KeyboardInterrupt:
s.server_close()
print()
多客户端连接(协程)
#!/usr/bin/python3
from gevent import monkey, spawn
monkey.patch_all()
import socket
import sys
import struct
import subprocess
import argparse
# 创建解析对象
parser = argparse.ArgumentParser()
# 添加命令行参数、选项
parser.add_argument(
"-s",
"--server",
default="",
help="run in server mode",
)
parser.add_argument(
"-p",
"--port",
default=8888,
help="server port to listen on/connect to",
)
# 进行解析
args = parser.parse_args()
IP_ADDRESS = (args.server, args.port)
BUF_SIZE = 1024
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
server.bind(IP_ADDRESS)
server.listen(5)
def Server(conn):
while True:
try:
cmd = conn.recv(BUF_SIZE)
if not cmd: break
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
res_stdout = obj.stdout.read()
res_stderr = obj.stderr.read()
res_len = len(res_stdout) + len(res_stderr)
# 制作TCP头,利用struct的模块的pack将返回内容的长度打包成4个byte发送
res_header = struct.pack('i', res_len)
# 利用TCP流式传输的特性
conn.send(res_header)
conn.send(res_stdout)
conn.send(res_stderr)
except Exception as e:
sys.stderr.write(f'error message:{e}')
conn.close()
try:
while True:
conn, addr = server.accept()
spawn(Server, conn)
except KeyboardInterrupt:
server.close()