适用于Windows的python os.mkfifo()

适用于Windows的python os.mkfifo(),python,subprocess,pipe,mkfifo,Python,Subprocess,Pipe,Mkfifo,简短版本(如果你能回答简短版本,它可以帮我完成任务,其余的主要是为了其他有类似任务的人的利益): 在Windows中的python中,我想创建两个文件对象,附加到同一个文件(它不必是硬盘上的实际文件),一个用于读取,一个用于写入,这样,如果读取端尝试读取,它将永远不会得到EOF(它只会阻塞,直到写入某些内容)。我认为在linux操作系统中,mkfifo()可以完成这项工作,但在Windows中它并不存在。可以做些什么?(我必须使用文件对象) 一些额外的细节: 我有一个python模块(不是我写的

简短版本(如果你能回答简短版本,它可以帮我完成任务,其余的主要是为了其他有类似任务的人的利益):

在Windows中的python中,我想创建两个文件对象,附加到同一个文件(它不必是硬盘上的实际文件),一个用于读取,一个用于写入,这样,如果读取端尝试读取,它将永远不会得到EOF(它只会阻塞,直到写入某些内容)。我认为在linux操作系统中,mkfifo()可以完成这项工作,但在Windows中它并不存在。可以做些什么?(我必须使用文件对象)

一些额外的细节: 我有一个python模块(不是我写的),它通过stdin和stdout(使用raw_input()和print)玩特定的游戏。我也有一个Windows可执行文件,通过stdin和stdout玩同样的游戏。我想让他们互相对抗,记录他们所有的交流

以下是我可以编写的代码(没有实现
get\u fifo()
函数,因为我不知道如何在Windows中实现它):


在Windows上,您正在查看

管道是进程用于通信的共享内存的一部分。创建管道的进程是管道服务器。连接到管道的进程是管道客户端。一个进程将信息写入管道,然后另一个进程从管道读取信息

要使用Windows管道,可以使用或。一个特殊的实用程序模块,提供到win32管道API的接口。它包括
popen[234]()
便利功能的实现


请参阅和类似的SO问题(不特定于管道,但指向有用的信息)。

对于跨平台解决方案,我建议在localhost(127.0.0.1)上的套接字上构建类似文件的对象——默认情况下,IDLE就是这样解决与您的问题的。

遵循上述两个答案,我不小心碰到了答案。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])
os.pipe()
在Windows上返回匿名管道或命名管道,这是非常轻量级和高效的

TCP套接字(如所建议的)更重:例如,您可以在netstat中看到它们,每个套接字都需要一个端口号,每个对等端口的可用端口数限制为64k(例如,从localhost到localhost的64k)

另一方面,命名管道(在窗口上)受到限制,因为:

  • 不能在Windows上安装,因为它们不是套接字
  • 没有明显的方法可以通过超时来读取()
  • 即使让它们成为非阻塞也很困难
可以使用
makefile()
将套接字包装在与Python兼容的文件句柄中,这样就可以使用它们。这使它成为一些用例的一个有吸引力的选项,例如从一个线程向另一个线程发送
stdout

可以使用自动分配的端口号构造套接字,如下所示(基于):


您是否尝试过删除
get_fifo()
并将
pythonmodule
与windows可执行文件直接连接:
sys.stdin,sys.stdout=p1.stdout,p1.stdin
。由
pythonmodule
编辑的所有
print
都写入
p1。在这种情况下,stdin
raw_input()
p1.stdout
读取。如果由于Python端的缓冲问题而失败;尝试使用无缓冲的stdin/stdout运行脚本:
python-u your_script.py
并将
bufsize=0
参数添加到
Popen
。好主意!当TCP套接字执行相同的操作时,不需要在管道上乱动。。。。为什么python不能将代码复制粘贴到自己的mkfifo函数中呢?
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])
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))
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}")