如何在Python中与服务器上的可执行文件互动?

1 投票
2 回答
1255 浏览
提问于 2025-04-18 11:17

我想在一个TCP服务器上运行一个可执行文件,并且希望能够通过socket连接与它进行互动,输入来自客户端,输出也要返回给客户端,直到这个可执行文件结束为止。我尝试使用subprocess模块中的Popen类来实现这个功能,但它无法与可执行文件进行持续的互动(它只能接受一次输入,而我希望它能一直接受输入,直到程序退出)。

举个例子,如果我向服务器发送了“1”这个输入,那么服务器就应该把与“1”对应的输出发送给客户端,然后再询问下一个输入,持续进行这个过程,直到可执行文件结束。

2 个回答

1

这里有一个使用 circuits 的实现:

server.py:

#!/usr/bin/env python


from uuid import uuid4 as uuid
from subprocess import Popen, PIPE


from circuits import handler, Component, Debugger, Event

from circuits.io import File

from circuits.net.sockets import TCPServer
from circuits.net.events import close, write


class kill(Event):
    """kill Event"""


class Command(Component):

    channel = "cmd"

    def __init__(self, sock, command, channel=channel):
        super(Command, self).__init__(channel=channel)

        self._sock = sock
        self._command = command

        self._buffer = None

        self._p = Popen(command, shell=True, stdin=PIPE, stdout=PIPE)

        self._stdin = File(
            self._p.stdin, channel="{0:s}.stdin".format(self.channel)
        ).register(self)

        self._stdout = File(
            self._p.stdout, channel="{0:s}.stdout".format(self.channel)
        ).register(self)

        self.addHandler(
            handler("eof", channel=self._stdout.channel)(self._on_stdout_eof)
        )
        self.addHandler(
            handler("read", channel=self._stdout.channel)(self._on_stdout_read)
        )

    def write(self, data):
        self.fire(write(data), self._stdin.channel)

    def kill(self):
        self._p.terminate()
        self.unregister()

    @staticmethod
    def _on_stdout_eof(self):
        self.fire(kill(), self.channel)
        self.fire(close(self._sock), self.parent.channel)

    @staticmethod
    def _on_stdout_read(self, data):
        self.fire(write(self._sock, data), "server")


class Server(Component):

    channel = "server"

    def init(self, bind, cmd):
        self.cmd = cmd

        self.clients = {}

        TCPServer(bind).register(self)

    def connect(self, sock, host, port):
        command = Command(sock, self.cmd, channel=uuid()).register(self)
        self.clients[sock] = command

    def disconnect(self, sock):
        command = self.clients[sock]
        self.fire(kill(), command.channel)
        del self.clients[sock]

    def read(self, sock, data):
        command = self.clients[sock]
        self.fire(write(data), command.channel)


server = Server(("0.0.0.0", 8000), "python app.py")
Debugger().register(server)

server.run()

app.py:

#!/usr/bin/env python


from __future__ import print_function


import sys


def function1():
    print("I am function 1!")


def function2():
    print("I am function 2!")


def function3():
    raise SystemExit(0)


MENU_OPTIONS = (
    (1, "Function 1"),
    (2, "Function 2"),
    (3, "Function 3")
)


FUNCTIONS = {
    1: function1,
    2: function2,
    3: function3
}


def main():
    while True:
        try:
            print("Menu:")
            for option, description in MENU_OPTIONS:
                print("{0:d}) {1:s}".format(option, description))
            print()
            sys.stdout.flush()

            choice = raw_input("> ")

            try:
                FUNCTIONS[int(choice)]()
            except ValueError:
                print("Invalid Input")
        except (KeyboardInterrupt, EOFError):
            raise SystemExit(0)


if __name__ == "__main__":
    main()

这是一个示例会话(这个例子经过了充分的测试):

玩得开心!:)

注意:我实际上是 1[circuits] 的开发者/作者。我觉得这个例子写起来很不错

1

只需要把这个套接字当作子进程的标准输入、输出和错误输出就可以了。例如:

import socket
import subprocess

listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
listener.bind(('0.0.0.0', 0))
listener.listen(5)
print(listener.getsockname())

try:
    while True:
        client, addr = listener.accept()
        subprocess.Popen(['cat'], stdin=client, stdout=client, stderr=client)
        client.close()
except KeyboardInterrupt:
    pass
finally:
    listener.close()

这可能需要一个符合POSIX标准的操作系统。

撰写回答