带有IPC和子进程的python命名管道
我正在为一个UDP应用程序编写一个测试工具TH,它打开了一个到UDP应用程序另一个实例的隧道。为了通过隧道发送内容,每个UDP应用程序都有一个与之关联的命名管道(读+写模式)和一个TUN接口,我将在将来使用它。每个UDP应用程序都有几个子进程,TH在不同线程中将UDP应用程序作为子进程启动,并通过子进程中的管道发送数据。这真是一团糟,但我想先让它在一个简单的案例中发挥作用,因为我想最终在5+个应用程序上构建隧道 2个问题: 第一个应用程序上的Select loop未获取TH发送的所有消息-仅第一个。选择此选项是否不会在文件每次有新内容/准备读取时通知?我目前使用pipe.readline,更改为pipe.readlines会获得大部分消息,但不是全部消息。详见下文。 在第二个应用程序中,我尝试将接收到的UDP数据写入到与第一个不同的管道中。他永远得不到数据。 以下是通过隧道从TH传输的数据流: 第次打印>>fifo1,foo fifo1 UDP应用程序1接收fifo1,发送UDP数据包 UDP隧道 UDP应用程序2接收UDP数据包,发送fifo2 fifo2 TH fifo2.readline 当然,这根本不起作用。当我通过fifo1从TH向应用程序1发送多条消息时,只有第一条消息可以发送到应用程序1。此消息通过UDP隧道正确传输到应用程序2。应用程序2写入fifo2,但在TH中,我在fifo2中没有看到任何内容。为了说明这一点,我在运行测试线束时包含了控制台输出带有IPC和子进程的python命名管道,python,ipc,named-pipes,Python,Ipc,Named Pipes,我正在为一个UDP应用程序编写一个测试工具TH,它打开了一个到UDP应用程序另一个实例的隧道。为了通过隧道发送内容,每个UDP应用程序都有一个与之关联的命名管道(读+写模式)和一个TUN接口,我将在将来使用它。每个UDP应用程序都有几个子进程,TH在不同线程中将UDP应用程序作为子进程启动,并通过子进程中的管道发送数据。这真是一团糟,但我想先让它在一个简单的案例中发挥作用,因为我想最终在5+个应用程序上构建隧道 2个问题: 第一个应用程序上的Select loop未获取TH发送的所有消息-仅第一
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:
在app1端,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:我的理论是,它试图在管道有任何内容之前读取管道。但这没有意义,因为我在接收fifo2数据时做了一个while循环。我试过一些简单的东西,但没用
测试线束的相关部分:
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应用程序的select循环以及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
# ...
您可能什么也看不到,因为数据被困在stdio缓冲区中;尝试在打印后添加fifo.flush。一般来说,应该注意混合缓冲IO和select
最好从print和file.readline切换到os.write和os.read,这两个选项没有缓冲区,并且在wrt select中有更可预测的行为。您可能什么也看不到,因为数据被卡在stdio缓冲区中;尝试在打印后添加fifo.flush。一般来说,我会小心混合缓冲IO和select。我建议您从print和file.readline切换到os.write和os.read,它们不缓冲,而且wrt select的行为更可预测。此外,如果缺少换行符,print已经附加了一个换行符,因此您可能希望避免添加“\n”,或者更好的做法是,避免打印优先于sys.stdout.write,或者如上所述,os.write.将所有内容转换为os.open/read/write非常有效,谢谢!我之前试过fifo.flush,但没用。我还尝试过使用python-u启动选项,但也没有成功——我的印象是强迫所有东西都不受缓冲。好的,我已经回复了上面的评论作为答案。