python os.mkfifo () для Windows - PullRequest
4 голосов
/ 27 мая 2010

Короткая версия (если вы можете ответить на короткую версию, она делает работу за меня, остальное в основном для других людей с похожей задачей):

В python в Windows я хочу создать 2 файловых объекта, прикрепленных к одному и тому же файлу (это не обязательно должен быть реальный файл на жестком диске), один для чтения и один для записи, так что если конец чтения пытается прочитать, он никогда не получит EOF (он будет просто блокироваться, пока что-то не записано). Я думаю, что в Linux os.mkfifo () сделает эту работу, но в Windows ее нет. Что можно сделать? (Я должен использовать файл-объекты).

Некоторые дополнительные детали: У меня есть модуль python (не написан мной), который играет в определенную игру через stdin и stdout (используя raw_input () и print). У меня также есть исполняемый файл Windows, играющий в ту же игру, через stdin и stdout. Я хочу заставить их играть друг против друга и записывать все их сообщения.

Вот код, который я могу написать (функция get_fifo() не реализована, потому что я не знаю, как это сделать в Windows):

class Pusher(Thread):
        def __init__(self, source, dest, p1, name):
                Thread.__init__(self)
                self.source = source
                self.dest = dest
                self.name = name
                self.p1 = p1

        def run(self):
                while (self.p1.poll()==None) and\
                      (not self.source.closed) and (not self.source.closed):
                        line = self.source.readline()
                        logging.info('%s: %s' % (self.name, line[:-1]))
                        self.dest.write(line)
                        self.dest.flush()


exe_to_pythonmodule_reader, exe_to_pythonmodule_writer =\
                          get_fifo()
pythonmodule_to_exe_reader, pythonmodule_to_exe_writer =\
                          get_fifo()

p1 = subprocess.Popen(exe, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)

old_stdin = sys.stdin
old_stdout = sys.stdout

sys.stdin = exe_to_pythonmodule_reader
sys.stdout = pythonmodule_to_exe_writer

push1 = Pusher(p1.stdout, exe_to_pythonmodule_writer, p1, '1')
push2 = Pusher(pythonmodule_to_exe_reader, p1.stdin, p1, '2')

push1.start()
push2.start()
ret = pythonmodule.play()
sys.stdin = old_stdin
sys.stdout = old_stdout

Ответы [ 4 ]

15 голосов
/ 27 мая 2010

Следуя двум ответам выше, я случайно наткнулся на ответ. os.pipe () делает работу. Спасибо за ваши ответы.

Я публикую полный код на тот случай, если кто-то другой ищет это:

import subprocess
from threading import Thread
import time
import sys
import logging
import tempfile
import os

import game_playing_module

class Pusher(Thread):
    def __init__(self, source, dest, proc, name):
        Thread.__init__(self)
        self.source = source
        self.dest = dest
        self.name = name
        self.proc = proc

    def run(self):
        while (self.proc.poll()==None) and\
              (not self.source.closed) and (not self.dest.closed):
            line = self.source.readline()
            logging.info('%s: %s' % (self.name, line[:-1]))
            self.dest.write(line)
            self.dest.flush()

def get_reader_writer():
    fd_read, fd_write = os.pipe()
    return os.fdopen(fd_read, 'r'), os.fdopen(fd_write, 'w')

def connect(exe):
    logging.basicConfig(level=logging.DEBUG,\
                        format='%(message)s',\
                        filename=LOG_FILE_NAME,
                        filemode='w')

    program_to_grader_reader, program_to_grader_writer =\
                              get_reader_writer()

    grader_to_program_reader, grader_to_program_writer =\
                              get_reader_writer()

    p1 = subprocess.Popen(exe, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)        

    old_stdin = sys.stdin
    old_stdout = sys.stdout

    sys.stdin = program_to_grader_reader
    sys.stdout = grader_to_program_writer

    push1 = Pusher(p1.stdout, program_to_grader_writer, p1, '1')
    push2 = Pusher(grader_to_program_reader, p1.stdin, p1, '2')

    push1.start()
    push2.start()

    game_playing_module.play()

    sys.stdin = old_stdin
    sys.stdout = old_stdout

    fil = file(LOG_FILE, 'r')
    data = fil.read()
    fil.close()
    return data

if __name__=='__main__':
    if len(sys.argv) != 2:
        print 'Usage: connect.py exe'
        print sys.argv
        exit()
    print sys.argv
    print connect(sys.argv[1])
5 голосов
/ 27 мая 2010

Для кроссплатформенного решения я бы порекомендовал создать файловый объект поверх сокета на localhost (127.0.0.1) - это то, что IDLE по умолчанию делает для решения проблемы, которая очень похожа на вашу.

5 голосов
/ 27 мая 2010

В Windows вы просматриваете (именованные или анонимные) каналы .

Канал - это раздел разделяемой памяти, который процессы используют для связи. Процесс, который создает канал, является сервером канала. Процесс, который подключается к каналу, является клиентом канала. Один процесс записывает информацию в канал, затем другой процесс считывает информацию из канала.

Для работы с Windows Pipes вы можете использовать Python для расширений Windows (pywin32) или модуль Ctypes . Специальный служебный модуль win32pipe предоставляет интерфейс для API-интерфейса win32 pipe. Включает в себя реализации вспомогательных функций popen[234]().

См. how-to-use-win32-apis-with-python и аналогичные вопросы SO (не относится к Pipes, но указывает на полезную информацию).

0 голосов
/ 28 ноября 2018

os.pipe() возвращает анонимный канал или именованный канал в Windows, что очень просто и эффективно.

TCP-сокеты (как предложено user1495323 ) являются более тяжелыми: например, их можно увидеть с помощью netstat, для каждого из них требуется номер порта, а количество доступных портов ограничено 64 КБ на одноранговый узел (например, 64 КБ с локального на локальный).

С другой стороны, именованные каналы (в Windows) ограничены, потому что:

И сокеты можно обернуть в Python-совместимые файловые дескрипторы, используя makefile(), что позволяет использовать их для перенаправления stdout или stderr . Это делает этот вариант привлекательным для некоторых случаев использования, таких как отправка stdout из одного потока в другой.

Сокет может быть создан с автоматически назначенным номером порта, как этот (на основе превосходного Python-сокета HOWTO ):

with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as input_socket:
    # Avoid socket exhaustion by setting SO_REUSEADDR <https://stackoverflow.com/a/12362623/648162>:
    input_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # localhost doesn't work if the definition is missing from the hosts file,
    # and 127.0.0.1 only works with IPv4 loopback, but socket.gethostname()
    # should always work:
    input_socket.bind((socket.gethostname(), 0))
    random_port_number = input_socket.getsockname()[1]
    input_socket.listen(1)

    # Do something with input_socket, for example pass it to another thread.

    output_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # close() should not strictly be necessary here, but since connect() could fail, it avoids leaking fds
    # in that case. "If a file descriptor is given, it is closed when the returned I/O object is closed".
    with output_socket:
        output_socket.connect((socket.gethostname(), random_port_number))

Пользователь input_socket (например, другой поток) может затем сделать:

with input_socket:
    while True:
        readables, _, _ = select.select([input_socket], [], [input_socket], 1.0)

        if len(readables) > 0:
            input_conn, addr = self.input_socket.accept()
            break

    with input_conn:
        while True:
            readables, _, errored = select.select([input_conn], [], [input_conn], 1.0)
            if len(errored) > 0:
                print("connection errored, stopping")
                break

            if len(readables) > 0:
                read_data = input_conn.recv(1024)
                if len(read_data) == 0:
                    print("connection closed, stopping")
                    break
                else:
                    print(f"read data: {read_data!r}")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...