Python. Redirect stdout to a socket

2019-03-15 19:23发布

问题:

I run my script on "A" computer, then i connect to "A" computer from "B" computer through my script. I send my message to computer "A" and my script run it with 'exec()' instruction.

I want to see result of execution my message on "A" computer, through socket on "B" computer. I try to change sys.stdout = socket_response but have a error: "Socket object has no attribute write()"

So, how can i redirect standart output (for print or exec()) from "A" computer to "B" computer through socket connection."

It will be some kind of 'python interpreter' into my script.

SORRY, I CAN'T ANSWER MY OWN QUESTION WITHOUT REPUTATION

Thanks to all!

I use simple way, which adviced me @Torxed . Here my monkey-code (it's just an example, not my real script)

    #-*-coding:utf-8-*-
import socket
import sys

class stdout_():

    def __init__(self, sock_resp):
        self.sock_resp = sock_resp

    def write(self, mes):
        self.sock_resp.send(mes)


MY_IP = 'localhost'
MY_PORT = 31337

srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Start server")
old_out = sys.stdout


srv.bind((MY_IP, MY_PORT))
srv.listen(0)
sock_resp, addr_resp = srv.accept()
new_out = stdout_(sock_resp)
sys.stdout = new_out
#sys.stdout = sock_resp ### sock_object has no attribute 'write'
while 1:
    try:
        a = sock_resp.recv(1024)
        exec(a)
    except socket.timeout:
        #print('server timeout!!' + '\n')
        continue

I connected to script with Putty and send "print 'abc'" and then i recieved answer 'abc'

回答1:

There is makefile function in python socket class:

socket.makefile(mode='r', buffering=None, *, encoding=None, errors=None, newline=None)

Return a file object associated with the socket. The exact returned type depends on the arguments given to makefile(). These arguments are interpreted the same way as by the built-in open() function.

Closing the file object won’t close the socket unless there are no remaining references to the socket. The socket must be in blocking mode; it can have a timeout, but the file object’s internal buffer may end up in a inconsistent state if a timeout occurs.

How to use it you can read in Mark Lutz book (chapter Making Sockets Look Like Files and Streams)

Example from book (idea is simple: make file object from socket with socket.makefile and link sys.stdout with it):

def redirectOut(port=port, host=host):
    """
    connect caller's standard output stream to a socket for GUI to listen
    start caller after listener started, else connect fails before accept
    """
    sock = socket(AF_INET, SOCK_STREAM)
    sock.connect((host, port))                # caller operates in client mode
    file = sock.makefile('w')                 # file interface: text, buffered
    sys.stdout = file                         # make prints go to sock.send
    return sock                               # if caller needs to access it raw


回答2:

Server side:

from subprocess import Popen, STDOUT, PIPE
from socket import socket
from time import sleep

server_sock = socket()
server_sock.bind(('', 8000))
server_sock.listen(4)

def close_process(p):
    p.stdin.close()
    p.stdout.close()

while 1:
    try:
        client, client_address = server_sock.accept()
        data = client.recv(8192)
    except:
        break
    # First, we open a handle to the external command to be run.
    process = Popen(data.decode('utf-8'), shell=True, stdout=PIPE, stdin=PIPE, stderr=STDOUT)
    # Wait for the command to finish
    # (.poll() will return the exit code, None if it's still running)
    while process.poll() == None:
        sleep(0.025)
    # Then we send whatever output the command gave us back via the socket
    # Python3: sockets never convert data from byte objects to strings,
    # so we'll have to do this "manually" in case you're confused from Py2.X
    try:
        client.send(bytes(process.stdout.read(), 'UTF-8'))
    except:
        pass

    # And finally, close the stdout/stdin of the process,
    # otherwise you'll end up with "to many filehandles openened" in your OS.
    close_process(process)
    client.close()

server_sock.close()

This assumes Python3.

If no one else have a better way of just redirecting output to a socket from a process, this is a solution you could work with.