memo

2016-10-27

Python で UNIX domain socket 経由で fd を受け渡す

プロセス間で fd を受け渡すには fork で引き継ぐ のが一般的だけど、 UNIX domain socket を通じて受け渡すことも可能らしいので、 python でやってみた。

コード

send_fd.py:

import array
import socket

input_file = open('spam.txt', 'rb', buffering=0)
fd = input_file.fileno()
print('send_fd:', input_file.read(5))

sock = socket.socket(socket.AF_UNIX)
sock.bind('/tmp/send_fd.sock')
sock.listen(1)
conn, addr = sock.accept()

# 送る
conn.sendmsg([b'\x00'], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array('i', [fd]))])

conn.close()
sock.close()
input_file.close()
print('end')

recv_fd.py:

import array
import os
import socket

sock = socket.socket(socket.AF_UNIX)
sock.connect('/tmp/send_fd.sock')

# 受け取る
fds = array.array('i')
msg, ancdata, flags, addr = sock.recvmsg(1, socket.CMSG_LEN(fds.itemsize))
for cmsg_level, cmsg_type, cmsg_data in ancdata:
    if (cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS):
        fds.fromstring(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])

fd = list(fds)[0]
input_file = os.fdopen(fd, 'rb')
print('recv_fd:', input_file.read())
input_file.close()

spam.txt:

Hello World!

実行結果

送信側 (サーバー側):

$ python3.5 send_fd.py
send_fd: b'Hello'
end

受信側 (クライアント側):

$ python3.5 recv_fd.py
recv_fd: b' World!\n'

サーバー側で最初の5バイト、クライアント側でその続きから読み出せている。

参考

https://docs.python.org/3/library/socket.html#socket.socket.sendmsg https://docs.python.org/3/library/socket.html#socket.socket.recvmsg