socket模拟实现ssh远程执行命令(Python)

一、subprocess.Popen()

subprocess模块定义了一个类: Popen

类原型:

class subprocess.Popen( args,
   bufsize = 0,
   executable = None,
   stdin = None,
   stdout = None,
   stderr = None,
   preexec_fn = None,
   close_fds = False,
   shell = False,
   cwd = None,
   env = None,
   universal_newlines = False,
   startupinfo = None,
   creationflags = 0)

我们只需要关注其中几个参数:

args:

args参数。可以是一个字符串,可以是一个包含程序参数的列表。要执行的程序一般就是这个列表的第一项,或者是字符串本身。

shell=True:

在Linux下,当shell=True时,如果arg是个字符串,就使用shell来解释执行这个字符串。如果args是个列表,则第一项被视为命令,其余的都视为是给shell本身的参数。也就是说,等效于:
subprocess.Popen([‘/bin/sh‘, ‘-c‘, args[0], args[1], ...])

stdin stdout和stderr:

stdin stdout和stderr,分别表示子程序的标准输入、标准输出和标准错误。可选的值有PIPE或者一个有效的文件描述符(其实是个正整数)或者一个文件对象,还有None。如果是PIPE,则表示需要创建一个新的管道,如果是None,不会做任何重定向工作,子进程的文件描述符会继承父进程的。另外,stderr的值还可以是STDOUT,表示子进程的标准错误也输出到标准输出。

二、粘包现象

所谓粘包问题主要还是因为接收方不知道消息之间的界限,还有系统缓存区的问题,时间差的原因,不知道一次性提取多少字节的数据所造成的。

须知:只有TCP有粘包现象,UDP永远不会粘包

粘包不一定会发生,如果发生了:1.可能是在客户端已经粘了;2.客户端没有粘,可能是在服务端粘了

缓冲区的作用:存储少量数据

如果你的网络出现短暂的异常或者波动,接收数据就会出现短暂的中断,影响你的下载或者上传的效率。但是,缓

冲区解决了上传下载的传输效率的问题,带来了黏包问题。

收发的本质:不一定是一收一发

三、为什么出现粘包?

1,接收方没有及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) recv会产生黏包(如果recv接受的数据量(1024)小于发送的数据量,第一次只能接收规定的数据量1024,第二次接收剩余的数据量)

2,发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据也很小,会合到一起,产生粘包)send 也可能发生粘包现象。(连续send少量的数据发到输出缓冲区,由于缓冲区的机制,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络)

出现粘包现象的代码实例

server.py

import socket
import subprocess

# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 绑定
phone.bind(('127.0.0.1', 8081))

# 监听
phone.listen(5)

# 通信循环
while True:
    # 接收客户端连接请求
    conn, client_addr = phone.accept()
    while True:
        # 接收客户端数据/命令
        cmd = conn.recv(1024)
        if not cmd:
            break
        # 创建管道
        obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout = obj.stdout.read()
        stderr = obj.stderr.read()
        # 向客户端发送数据
        conn.send(stdout)
        conn.send(stderr)
    # 结束连接
    conn.close()

# 关闭套接字
phone.close()

client.py

import socket

# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接
phone.connect(('127.0.0.1', 8081))
while True:
    cmd = input('>>> ').strip()
    if not cmd:
        continue
    if cmd == 'quit':
        break
    # 给服务端发送数据/命令
    phone.send(cmd.encode('utf-8'))
    # 接收服务端数据/命令
    data = phone.recv(1024)
    print(data.decode('utf-8'))

# 关闭套接字
phone.close()

粘包现象运行结果

可以观察到执行两次ls命令后,服务端返回的仍然是ifconfig命令的结果,最后一次ls命令的末尾才出现ls命令返回的部分结果

四、解决粘包问题的代码实例

server.py

import socket
import subprocess
import json
import struct

# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 绑定
phone.bind(('127.0.0.1', 8081))

# 监听
phone.listen(5)

# 通信循环
while True:
    # 接收客户端连接请求
    conn, client_addr = phone.accept()
    while True:
        # 接收客户端数据/命令
        cmd = conn.recv(1024)
        if not cmd:
            continue
        # 创建数据流管道
        obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout = obj.stdout.read()
        stderr = obj.stderr.read()
        # 向客户端发送数据

        # 解决粘包问题
        # 1.制作固定长度的报头
        header_dic = {
            'filename': 'a.txt',
            'total_size': len(stdout)+len(stderr)
        }
        # 序列化报头
        header_json = json.dumps(header_dic)  # 序列化为byte字节流类型
        header_bytes = header_json.encode('utf-8')  # 编码为utf-8(Mac系统)
        # 2.先发送报头的长度
        # 2.1 将byte类型的长度打包成4位int
        conn.send(struct.pack('i', len(header_bytes)))
        # 2.2 再发报头
        conn.send(header_bytes)
        # 2.3 再发真实数据
        conn.send(stdout)
        conn.send(stderr)
    # 结束连接
    conn.close()

# 关闭套接字
phone.close()

client.py

import socket
import struct
import json

# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接
phone.connect(('127.0.0.1', 8081))
while True:
    cmd = input('>>> ').strip()
    if not cmd:
        continue
    if cmd == 'quit':
        break
    # 给服务端发送命令
    phone.send(cmd.encode('utf-8'))
    # 接收服务端数据

    # 1.先收报头长度
    obj = phone.recv(4)
    header_size = struct.unpack('i', obj)[0]
    # 2.收报头
    header_bytes = phone.recv(header_size)
    # 3.从报头中解析出数据的真实信息(报头字典)
    header_json = header_bytes.decode('utf-8')
    header_dic = json.loads(header_json)
    total_size = header_dic['total_size']
    # 4.接受真实数据
    recv_size = 0
    recv_data = b''
    while recv_size < total_size:
        res = phone.recv(1024)
        recv_data += res
        recv_size += len(res)
    print(recv_data.decode('utf-8'))

# 关闭套接字
phone.close()

原文地址:https://www.cnblogs.com/rainbow-ran/p/12538478.html

时间: 2024-08-04 23:35:15

socket模拟实现ssh远程执行命令(Python)的相关文章

102 模拟ssh远程执行命令

目录 一.subprocess模块 1.1 使用方法 二.模拟实现SSH远程执行命令 服务器 客户端 一.subprocess模块 subprocess 模块允许你去创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入.标准输出.标准错误以及返回码等.更多查看官网:https://docs.python.org/2/library/subprocess.html?highlight=subprocess#frequently-used-arguments 可以通过subproces

100 模拟ssh远程执行命令

目录 一.subprocess模块 1.1 使用方法 二.模拟实现SSH远程执行命令 服务器 客户端 一.subprocess模块 subprocess 模块允许你去创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入.标准输出.标准错误以及返回码等.更多查看官网:https://docs.python.org/2/library/subprocess.html?highlight=subprocess#frequently-used-arguments 可以通过subproces

day8-套接字sock 实现SSH远程执行命令功能

复习 #面向对象编程#类:#对象#实例化 :从一个类到产生一个对象的过程    #对象 = 类名()   #__init__初始化方法,是为了给一个具体的对象放一些初识的属性#在类中:    # 静态属性 直接定义在类中的属性,使用静态属性:类名.对象名都可以调用    # 动态属性 就是方法 就是定义在类中的函数 默认传一个self# class Person:#     money = 100# sister = Person()# father = Person()# Person.mon

ssh远程执行命令使用明文密码

经过不懈的搜索终于找到ssh远程执行命令使用明文密码使用sshpass. 例子: sshpass -p "sequoiadb" ssh [email protected] "ls /" sshpass在centos无法直接安装 cd /etc/yum.repos.d/ wget http://download.opensuse.org/repositories/home:Strahlex/CentOS_CentOS-6/home:Strahlex.repo yum

socket 基于udp实现远程执行命令

server.py # socket 基于udp实现远程执行命令 from socket import * import subprocess ip_port = ('127.0.0.1', 8080) buffer_size = 1024 udp_server = socket(AF_INET, SOCK_DGRAM) udp_server.bind(ip_port) while True: cmd, addr = udp_server.recvfrom(buffer_size) cmd =

粘包产生的原因 socket 基于tcp实现远程执行命令(解决粘包)low

# 粘包产生的原因 # 粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的. # 基于tcp协议的套接字会有粘包现象,而基于udp协议的套接字不会产生粘包现象 # tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住:而udp是基于数据报的,即使你输入的是空内容,那也不是空消息,udp协议会帮你封装上消息头(ip+端口的方式),这样就有了消息办界 # 两种情况下会发生粘包 # 1.发送端需要等缓冲区满才发送

解决SSH远程执行命令找不到环境变量的问题

通过SSH执行远程主机的命令或脚本时,经常会出现找不到自定义环境变量的问题.但是,如果通过SSH登录远程主机,然后再执行相同的命令或脚本,那么此时执行又是成功的.两种相似的方法,得到的结果却截然不同,看起来很诡异的现象,根本原因在于这两种方式使用的bash模式不同! 1. 通过SSH登录后再执行命令和脚本这种方式会使用Bash的interactive + login shell模式,这里面有两个概念需要解释:interactive和login. login故名思义,即登陆,login shell

python 实现ssh远程执行命令 上传下载文件

使用密码远程执行命令 [[email protected] script]# cat daramiko_ssh.py  #!/usr/bin/env python #_*_coding:utf-8 _*_ __author__ = 'gaogd' import paramiko import sys,os host = sys.argv[1] user = 'root' password = 'ddfasdsasda2015' cmd = sys.argv[2] s = paramiko.SSH

使用ssh远程执行命令批量导出数据库到本地

前天正在跟前端的同事调试功能.服务器开好,模拟的玩家登录好,就在倒计时.这时突然运营的同事跑过来说要统计几个服务器玩家的一些情况,也就是需要从几个服的数据库导出部分玩家的数据.好吧,我看了一下时间,11:47.心想,跟前端调试完,去吃个饭再午休一下那就下午再给吧.没想对方来一句"就导个数据库而已,要这么久么?",而且还是直接跟我上司说的.我嚓,好吧,我导.可问题来了,平时的统计是由php做的,批量部署这些是由运维做的.服务端完全没有对应的工具.而且服务器是在阿里云上的,数据库的用户是限