用IPC和子进程命名管道混乱的python

时间:2013-06-29 07:43:29

标签: python ipc named-pipes

我正在为UDP应用程序编写测试工具(TH),打开通往UDP应用程序另一个实例的隧道。要通过隧道发送内容,每个UDP应用程序都有一个与读取/写入模式相关联的命名管道(相对于TUN接口,我将来会使用它)。每个UDP应用程序都有几个子进程,TH在不同的线程中启动UDP应用程序作为子进程,以及通过子进程中的管道发送数据。这是一个混乱,但我希望它首先在一个简单的案例上工作,因为我想最终建立超过5个应用程序的隧道。

2个问题:

  1. 第一个应用程序上的Select()循环没有得到TH发送的所有消息 - 只是第一个。每次文件有新内容/准备好阅读时,select()都不会通知吗?我目前使用pipe.readline(),更改为pipe.readlines()获取大部分消息,但不是全部。有关详细信息,请参见下文。
  2. 在第二个应用程序中,我尝试写入管道的UDP数据(与第一个不同)。 TH永远不会得到数据。
  3. 以下是来自TH的隧道数据流:

    • TH print>> fifo1,“foo”
    • fifo1
    • UDP app#1接收fifo1,发送UDP数据包
    • UDP隧道
    • UDP app#2接收UDP数据包,发送fifo2
    • fifo2
    • TH fifo2.readline()

    当然,这根本不起作用。当我通过fifo1从TH发送几条消息到app#1时,只有第一条消息通过app#1。此消息通过UDP隧道正确传输到app#2。 App#2写入fifo2,但在TH中我没有看到fifo2中的任何内容。当我运行测试工具来说明这一点时,我已经包含了控制台输出。

    Sending data one way over tunnel...
    rxDataFifo: reading from pipe
    # Here we start sending messages (hello 0, hello 1, hello 2) via fifo1
    i= 0
    i= 1
    i= 2
    txDataFifo: finished # Now we are finished sending
    handleFifoData: hello 0 # first message received on UDP app #1
    UDP ('127.0.0.1', 9000):  fde04a5a1de6d473b70c184e5e981279hello 0 # Here's the message app #2 recv via UDP
    ...writing out to fifo! hello 0 # And app #2 write out to fifo2 (or should, at least)
    # Nothing received from fifo2 on test harness!??
    

    问题1: 在app#1一侧,select循环不会在每次写入管道时触发多个可读事件。如果不是从管道读取一行,而是读取所有可用的行,我得到所有最后两条消息(但不是第一条消息)。

    Sending data one way over tunnel...
    rxDataFifo: reading from pipe
    i= 0
    i= 1
    i= 2
    txDataFifo: finished
    handleFifoData: hello 1
    handleFifoData: hello 2
    UDP ('127.0.0.1', 9000):  e3270c47214147ae3698aeecc13e2acehello 1
    tunnel data: hello 1
    ...writing out to fifo! hello 1
    UDP ('127.0.0.1', 9000):  e3270c47214147ae3698aeecc13e2acehello 2
    tunnel data: hello 2
    ...writing out to fifo! hello 2
    

    问题2:我的理论是TH在它有任何内容之前尝试读取管道。但这没有意义,因为我在接收fifo2数据时做了一段时间的循环。我尝试了一些更简单的东西,但没有用。


    测试工具的相关部分:

    def main():
        # Create pipes for each process we want to launch
        # NOTE: These pipes are passed to the multiprocessing.Process and subprocess.call
        # that we use to launch the UDP apps
        tmpdir = tempfile.mkdtemp()
        pipe0 = os.path.join(tmpdir, 'pipe0')
        pipe1 = os.path.join(tmpdir, 'pipe1')
        try:
            os.mkfifo(pipe0)
            os.mkfifo(pipe1)
        except OSError, e:
            print "Failed to create FIFO: %s" % e
    
        #...
    
        queue = Queue() # this where output goes
        num_msg = 10 # number of messages to send over tunnel
        txFifo = Process(target=txDataFifo, args=(pipe0, queue, num_msg))
        rxFifo = Process(target=rxDataFifo, args=(pipe1, queue))
        rxFifo.start()
        txFifo.start()
    
    def txDataFifo(pipe_name, queue, num_msg):
        fifo = open(pipe_name, 'r+b')
        # print >> fifo, "hello over named pipe" # write stuff to fifo
    
    
        for i in range(0, 3):
            print "i=",i
            print >> fifo, "hello "+str(i)
    
        fifo.close()
        print "txDataFifo: finished"
    
    def rxDataFifo(pipe_name, queue):
        print "rxDataFifo: reading from pipe"
        fifo = open(pipe_name, 'w+b')
        while True:
            data = fifo.readline()
            print 'rxDataFifo:', data
            queue.put(data)
        fifo.close()
        print "txDataFifo: reading from pipe"
    

    UDP应用程序的选择循环以及UDP和命名管道数据的处理程序:

    def listen (self, ipaddress, udpport, testport, pipe_filename):
        # ...
        inputs = [sock, self.pipe] # stuff we read
        outputs = [] # stuff we expect to write
    
        # Set up the named pipe that we use to simulate the TUN interface
        # and use to communicate with the test harness
        fifo = None
        if pipe_filename:
            print "Opening pipe="+pipe_filename+" for IPC with test harness"
            fifo = open(pipe_filename, 'r+b')
            inputs.append(fifo)
    
        while inputs:
            readable, writable, exceptional = select.select(inputs, outputs, inputs)
    
            for event in readable:
                if fifo and event is fifo:
                    # Handle data from test harness simulating TUN (via pipe)
                    self.handleFifoData(sock, fifo)
                if event is sock:
                    # Handle tunnel/setup request data
                    self.handleUDPData(sock, fifo)
                if event is self.pipe:
                    # Handle commands from the UI in the other process (IPC)
                    data = self.pipe.recv()
                    print "pipe event", data
                    if data[0] == 'open':
                        # NOTE: For open command, data[1] and data[2] are
                        # an IP address and port, respectively
                        connId = self.generateConnId()
                        msg = connId + 'setup'
                        sock.sendto(msg, (data[1], data[2]))
                        self.mySetup[connId] = SetupInfo(data[1], data[2])
            # Handle exceptional?    
    
    def handleFifoData (self, sock, fifo, ):
        # Send data from sockTest to sock
        data = fifo.readline().rstrip()
    
        print "handleFifoData:", data
        for peerId in self.tunnels:
            # TODO: perhaps FIFO message should be parsed json, so we know which client to send them to?
            peer = self.tunnels[peerId]
            msg = peerId + data
            sock.sendto(msg, (peer.address, peer.port))
    
    def handleUDPData (self, sock, fifo): # reads a tuple 
        (data, addr) = sock.recvfrom(1024)
        print "UDP "+str(addr)+": ", data
    
        # ...
        elif peerId in self.tunnels: # We are functioning as a relay for this node
            # NOTE: This is where TUN interface forwarding would happen
            print "tunnel data:", data
            if fifo:
                print "...writing out to fifo!", data
                print >> fifo, data+'\n'
            return
        # ...
    

1 个答案:

答案 0 :(得分:1)

您可能看不到任何内容,因为数据卡在stdio缓冲区中;尝试在打印后添加fifo.flush()。一般来说,应该警惕混合缓冲IO和选择。

最好从printfile.readline()切换到os.write()os.read(),它们不会缓冲,并且具有更可预测的行为wrt {{1 }}