Python を使ったプログラム間通信の仕方を勉強していた.
ずいぶん目標に近づいてきた.

したいことは,ネットワーク上で情報の受信可能状態になっているホストをみつけ,そのホスト宛に情報を送信する,ということ.

前半の,「ネットワーク上で情報の受信可能状態になっているホストをみつけ」る部分は前回紹介した Bonjour を用いて行い,後半の「そのホスト宛に情報を送信する」のは TCPServer を用いて行うことを検討した.

pybonjour パッケージの example として公開されている browse_and_resolve.py と register.py を基本として,これらに改良加えることで目的の動作をするようにした.
browse_and_resolve.py には メッセージの発信者(Sender)としての機能を追加し,register.py にはメッセージ受信者(Receiver)としての機能を追加した.


サンプルコードは長くなるので後回しにし,まず実行結果を紹介する.
同一物理ネットワーク上のホスト2台(soave: Browser 兼 Sender と barbaresco: Register 兼 Receiver)を用いて試した結果は以下の通り.

=== Browser / Sender === === Register / Receiver ===
soave[15]python browser_sender.py
Service added; resolving
Resolved service:
fullname = iside._is._tcp.local.
hosttarget = montepulciano.local.
port = 10465
IP Address = 192.168.50.18
#Connect with TCP to 192.168.50.18
Send: example XML [0, 1, 2, ..., 9999]
 
montepulciano[21]python register_receiver.py
#Create the TCPServer (at a parent process)
Wating data...
#Bonjour Registered service (at a child process)
name = iside
regtype = _is._tcp.
domain = local.
Recieved from 192.168.50.11:
example XML [0, 1, 2, ..., 9999]


まず,montepulciano (192.168.50.18) は TCPServer を使って情報の受信待機状態となると同時に,Bonjour を使って, _is._tcp というタイプのサービスを登録する.
このとき,fork を使ってプロセスを複製し,親プロセスには TCP 待機状態,子プロセスには Bonjour サービス登録,の役割をそれぞれ担わせる.

soave (192.168.50.11) は,Bonjour を使って,_is._tcp というタイプのサービスをネットワーク上に探す.
_is._tcp のサーバーが見つかればそのサーバーのホスト名から IP アドレスを取得し,そのアドレスに対して長い文字列を送信する.

browser_sender.py と register_receiver.py のどちらを先に起動しても良い.
browser_sender.py が先に起動された場合は,register_receiver.py が起動されてサービスが Bonjour で登録されるまで待機することになる.
register_receiver.py が先に起動された場合は,brower_sender.py は速やかにサービスを発見する.


サンプルプログラムは以下の通り.
オレンジ色の文字の部分が TCP による通信の処理をしているコードである.

=== Browser / Sender ===
import select
import sys
import pybonjour
import socket

regtype = "_is._tcp"
timeout = 5
resolved = []

def resolve_callback(sdRef, flags, interfaceIndex, errorCode,
fullname, hosttarget, port, txtRecord):
if errorCode != pybonjour.kDNSServiceErr_NoError:
return

addr = socket.gethostbyname(hosttarget)
print 'Resolved service:'
print ' fullname =', fullname
print ' hosttarget =', hosttarget
print ' port =', port
print ' IP Address =', addr
resolved.append(True)

# TCP connection to send data
print "#Connect with TCP to ", addr
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((addr, port))

num=range(10000)
data = "example XML %s" % str(num)
print "Send:", data
s.send(data)

s.close()

sys.exit(0)

def browse_callback(sdRef, flags, interfaceIndex, errorCode,
serviceName, regtype, replyDomain):
if errorCode != pybonjour.kDNSServiceErr_NoError:
return

if not (flags & pybonjour.kDNSServiceFlagsAdd):
print 'Service removed'
return

print 'Service added; resolving'

resolve_sdRef = pybonjour.DNSServiceResolve(0, interfaceIndex,
serviceName, regtype, replyDomain, resolve_callback)

try:
while not resolved:
ready = select.select([resolve_sdRef], [], [], timeout)
if resolve_sdRef not in ready[0]:
print 'Resolve timed out'
break
pybonjour.DNSServiceProcessResult(resolve_sdRef)
else:
resolved.pop()
finally:
resolve_sdRef.close()

browse_sdRef = pybonjour.DNSServiceBrowse(regtype = regtype,
callBack = browse_callback)

try:
try:
while True:
ready = select.select([browse_sdRef], [], [])
if browse_sdRef in ready[0]:
pybonjour.DNSServiceProcessResult(browse_sdRef)
except KeyboardInterrupt:
pass
finally:
browse_sdRef.close()

=== Register / Receiver ===
import select
import sys
import pybonjour
import SocketServer
import os

HOST = ''
PORT2 = 10465

name = "iside"
regtype = "_is._tcp"
port = 10465

pid = os.fork()
if pid == 0:
# Child process
def register_callback(sdRef, flags, errorCode, name, regtype, domain):
if errorCode == pybonjour.kDNSServiceErr_NoError:
print '#Bonjour Registered service (at a child process)'
print ' name =', name
print ' regtype =', regtype
print ' domain =', domain

sdRef = pybonjour.DNSServiceRegister(name = name,
regtype = regtype, port = port, callBack = register_callback)

try:
try:
while True:
ready = select.select([sdRef], [], [])
if sdRef in ready[0]:
pybonjour.DNSServiceProcessResult(sdRef)
except KeyboardInterrupt:
pass
finally:
sdRef.close()

else:
# Parent process
# TCP connection
class MyTCPHandler(SocketServer.StreamRequestHandler):

def handle(self):
self.data = self.rfile.readline().strip()
print "Recieved from %s:" % self.client_address[0]
print self.data

print "#Create the TCPServer (at a parent process)"
print " Wating data..."
server = SocketServer.TCPServer((HOST, PORT2), MyTCPHandler)
server.handle_request()


os.kill(pid, 8)





やじるし プログラム間通信関連メモの目次