python基础—socket套接字(4)基于tcp实现远程执行命令
1.python运行shell命令要用到一个模块subpross。模块允许你生成新的进程,连接它们的输入、输出、错误管道,并且获取它们的返回码
如:在cmd执行ipconfig命令
import subprocess # 创建子程序 subprocess_obj = subprocess.Popen('ipconfig', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) '''当shell=True时,表示在系统默认的shell环境中执行新的进程,此shell在windows表示为cmd.exe,在linux为/bin/sh subprocess.PIPE表示打开标准流的管道 stdout从子进程捕获到的标准输出. 一个字节序列, 或一个字符串 stderr捕获到的子进程的标准错误. 一个字节序列, 或者一个字符串, ''' stdout_res = subprocess_obj.stdout.read() # 读取输出结果。标准输出是byte类型 stderr_res = subprocess_obj.stderr.read() print(stdout_res.decode('gbk'))#windows默认编码为gbk print(stderr_res.decode('gbk'))

2.有了subpross,在上两次课的基础上修改即可。
#服务端 import socket import subprocess socket_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM) socket_obj.bind(('127.0.0.1', 8080)) socket_obj.listen(5) while True: fd, addr = socket_obj.accept() try: while True: cmd = fd.recv(1024) obj = subprocess.Popen( cmd.decode('utf8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout_res = obj.stdout.read() # 标准输出是byte格式,widows是gbk编码 stderr_res = obj.stderr.read() print(stdout_res.decode('gbk')) print(stderr_res.decode('gbk')) fd.send(stdout_res) fd.send(stderr_res) except ConnectionResetError: print('客户端关闭') fd.close()
#客户端 import socket client_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_obj.connect(('127.0.0.1', 8080)) while True: cmd = input('请输入cmd命令:').strip() if not cmd: continue client_obj.send(cmd.encode('utf8')) cmd_res = client_obj.recv(1024) print(cmd_res.decode('gbk'))
3.如果客户端运行的结果比较大,超过了recv的大小,客户端接收不干净,造成粘包了,怎么处理呢?
首先,a拿到数据的大小,这个数据的长度还必须知道,直接len(数据)长度是变化的,需要使用struct模块。b.在和recv每次接收的大小比较,超过了就循环接收。
struct模块中最重要的三个函数是pack(), unpack(), calcsize()
pack(fmt, v1, v2, ...) 按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)
unpack(fmt, string) 按照给定的格式(fmt)解析字节流string,返回解析出来的tuple,提取的时候用索引0
calcsize(fmt) 计算给定的格式(fmt)占用多少字节的内存
| Format | C Type | Python | 字节数 |
|---|---|---|---|
| x | pad byte | no value | 1 |
| c | char | string of length 1 | 1 |
| b | signed char | integer | 1 |
| B | unsigned char | integer | 1 |
| ? | _Bool | bool | 1 |
| h | short | integer | 2 |
| H | unsigned short | integer | 2 |
| i | int | integer | 4 |
| I | unsigned int | integer or long | 4 |
| l | long | integer | 4 |
| L | unsigned long | long | 4 |
| q | long long | long | 8 |
| Q | unsigned long long | long | 8 |
| f | float | float | 4 |
| d | double | float | 8 |
| s | char[] | string | 1 |
| p | char[] | string | 1 |
| P | void * | long |
修改后的代码
#服务端 import socket import subprocess import struct socket_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM) socket_obj.bind(('127.0.0.1', 8080)) socket_obj.listen(5) while True: fd, addr = socket_obj.accept() try: while True: cmd = fd.recv(1024) obj = subprocess.Popen( cmd.decode('utf8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout_res = obj.stdout.read() # 标准输出是byte格式,widows是gbk编码 stderr_res = obj.stderr.read() print(stdout_res.decode('gbk')) print(stderr_res.decode('gbk')) header=struct.pack('i',len(stdout_res+stderr_res)) print(header) fd.send(header) #i格式打包,生成的是4个byte fd.send(stdout_res) fd.send(stderr_res) except ConnectionResetError: print('客户端关闭') fd.close()
# 客户端 import socket import struct client_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_obj.connect(('127.0.0.1', 8080)) while True: cmd = input('请输入cmd命令:').strip() if not cmd: continue client_obj.send(cmd.encode('utf8')) # 1.拿到数据大小data_size header = client_obj.recv(4) # 接收头文件 data_size = struct.unpack('i', header) # 按i模式解包,查看数据的大小,是元祖格式(size,) print('头文件大小:%s byte' % data_size[0]) # 2.recv_size=0,循环接收,每次接收后recv_size+=接收的长度 recv_size = 0 while recv_size < data_size[0]: cmd_res = client_obj.recv(1024) print(cmd_res.decode('gbk'), end='') recv_size += len(cmd_res)
4.如若发的是个文件,头包含文件的基本信息,字典格式,就没法直接用struct,需要先用json序列化,在把序列化编码,再用struct打包
,客户端就反过来解码。
# 服务端 import socket import subprocess import struct import json socket_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM) socket_obj.bind(('127.0.0.1', 8080)) socket_obj.listen(5) while True: fd, addr = socket_obj.accept() try: while True: cmd = fd.recv(1024) obj = subprocess.Popen( cmd.decode('utf8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout_res = obj.stdout.read() # 标准输出是byte格式,widows是gbk编码 stderr_res = obj.stderr.read() print(stdout_res.decode('gbk')) print(stderr_res.decode('gbk')) data_size = len(stdout_res + stderr_res) # 1制作头 header_dict = { 'filename': 'a.txt', 'data_size': data_size, 'md5': "12223544555" } json_str = json.dumps(header_dict) json_bytes = json_str.encode('gbk') # 头文件要传输的格式 header_size = struct.pack('i', len(json_bytes)) # i格式打包,生成的是4个byte # 2先把头的长度发过去 fd.send(header_size) # 3 发头信息,里面包含有数据的大小 fd.send(json_bytes) # 4 发数据信息 fd.send(stdout_res) fd.send(stderr_res) except ConnectionResetError: print('客户端关闭') fd.close()
# 客户端 import socket import struct import json client_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_obj.connect(('127.0.0.1', 8080)) while True: cmd = input('请输入cmd命令:').strip() if not cmd: continue client_obj.send(cmd.encode('utf8')) # 1.拿到头的长度 header_size_info = client_obj.recv(4) header_size = struct.unpack('i', header_size_info)[0] print('头文件的长度%s' % header_size) # 2拿头 json_str_bytes = client_obj.recv(header_size) # 拿到的是json_bytes # 转成json_str json_str = json_str_bytes.decode('gbk') # 还原成dict header_dic = json.loads(json_str) print(header_dic) print('头文件大小:%s byte' % header_dic['data_size']) # 2.recv_size=0,循环接收,每次接收后recv_size+=接收的长度 recv_size = 0 while recv_size < header_dic['data_size']: cmd_res = client_obj.recv(1024) print(cmd_res.decode('gbk'), end='') recv_size += len(cmd_res)

浙公网安备 33010602011771号