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)占用多少字节的内存
 

FormatC TypePython字节数
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)

 

posted @ 2020-12-27 17:52  枫叶学python  阅读(255)  评论(0)    收藏  举报