次のコードでは、名前付きFIFOを使用して2つのスクリプト間の通信を可能にしています。
- リーダーがライターよりも速く
read
にしようとすると、ブロックされます。
- リーダーがライターに追いつかない場合、ライターはブロックしません。
- 操作はバッファ指向です。ライン指向の操作は現在実装されていません。
- このコードは概念実証と考えるべきです。遅延とバッファサイズは任意です。
コード
import argparse
import errno
import os
from select import select
import time
class OneFifo(object):
def __init__(self, name):
self.name = name
def __enter__(self):
if os.path.exists(self.name):
os.unlink(self.name)
os.mkfifo(self.name)
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
if os.path.exists(self.name):
os.unlink(self.name)
def write(self, data):
print "Waiting for client to open FIFO..."
try:
server_file = os.open(self.name, os.O_WRONLY | os.O_NONBLOCK)
except OSError as exc:
if exc.errno == errno.ENXIO:
server_file = None
else:
raise
if server_file is not None:
print "Writing line to FIFO..."
try:
os.write(server_file, data)
print "Done."
except OSError as exc:
if exc.errno == errno.EPIPE:
pass
else:
raise
os.close(server_file)
def read_nonblocking(self):
result = None
try:
client_file = os.open(self.name, os.O_RDONLY | os.O_NONBLOCK)
except OSError as exc:
if exc.errno == errno.ENOENT:
client_file = None
else:
raise
if client_file is not None:
try:
rlist = [client_file]
wlist = []
xlist = []
rlist, wlist, xlist = select(rlist, wlist, xlist, 0.01)
if client_file in rlist:
result = os.read(client_file, 1024)
except OSError as exc:
if exc.errno == errno.EAGAIN or exc.errno == errno.EWOULDBLOCK:
result = None
else:
raise
os.close(client_file)
return result
def read(self):
try:
with open(self.name, 'r') as client_file:
result = client_file.read()
except OSError as exc:
if exc.errno == errno.ENOENT:
result = None
else:
raise
if not len(result):
result = None
return result
def parse_argument():
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--client', action='store_true',
help='Set this flag for the client')
parser.add_argument('-n', '--non-blocking', action='store_true',
help='Set this flag to read without blocking')
result = parser.parse_args()
return result
if __name__ == '__main__':
args = parse_argument()
if not args.client:
with OneFifo('known_name') as one_fifo:
while True:
one_fifo.write('one line')
time.sleep(0.1)
else:
one_fifo = OneFifo('known_name')
while True:
if args.non_blocking:
result = one_fifo.read_nonblocking()
else:
result = one_fifo.read()
if result is not None:
print result
client
はFIFOを開いた場合server
チェック。 client
がFIFOを開いている場合、server
は行を書き込みます。それ以外の場合は、server
が実行を継続します。ブロックされている読み取りが問題を引き起こすため、非ブロック読み取りを実装しました。server
が再起動すると、ほとんどの場合client
はブロックされたままになり、回復しません。ノンブロッキングclient
を使用すると、再起動がより容易になります。server
起動時に
出力
[[email protected]:~] python onefifo.py
Waiting for client to open FIFO...
Waiting for client to open FIFO...
Writing line to FIFO...
Done.
Waiting for client to open FIFO...
Writing line to FIFO...
Done.
[[email protected]:~] python onefifo.py -c
one line
one line
ノート
server
は、FIFOが既に存在していることを検出した場合、それを削除します。これはclients
にserver
が再開したことを通知する最も簡単な方法です。この通知は通常、ブロックバージョンclient
によって無視されます。
これはクールです。クライアントは受信準備ができていることをサーバーにどのように伝えますか?クライアントがFIFOを開いたかどうかを確認できますか?これは 'o.O_NONBLOCK'を使ってサーバーによって強制されますか? – dronus
'server'がFIFOをオープンしようとし、' ENXIO'(Device not configured)エラーを受け取った場合、 'client'がFIFOをオープンしていないことを知ります。この種のテストは、 'server'が' os.O_NONBLOCK'でFIFOを開く場合にのみ機能します。それ以外の場合、 'server'が' open'を呼び出すとブロックされます。 –