一般に、ネットワークサービスは、従来のクライアント/サーバーモデルに従っています。あるコンピューターがあるサービスを提供するサーバーとして機能し、別のコンピューターがこのサービスを利用するクライアント側を代表します。ネットワーク上で通信するために、ネットワークソケットが使用されます。このソケット通信は、コンピュータの内部でプロセス間通信(IPC)として利用されることもあります。
この記事では、プログラミング言語Pythonを使用して、ネットワークソケットで通信する簡単なクライアント/サーバーアプリケーションの書き方を説明します。簡単のために、この例のサーバーは受信したデータを標準出力に出力するだけです。クライアント/サーバーアプリケーションのアイデアは、気象観測所のセンサーで、時間の経過とともに温度データを収集し、収集したデータをサーバーアプリケーションに送信し、そこでさらにデータを処理することです。
Socketとは?
ネットワークソケットは、ネットワーク上で動作する2つのプログラムまたはプロセス(この例ではクライアントとサーバー)間の双方向通信リンクの終点となるものです。同じコンピューター上でも、ネットワークで接続された異なるシステム上でも可能です。
両者は、ネットワークソケットに書き込んだり、ネットワークソケットから読み出したりすることで通信を行います。現実の技術的には、2人の参加者が電話で通信するのと同じです。ネットワークソケットは、電話回線に対応する番号、または携帯電話の場合は契約書を表しています。
例
ソケット機能を利用するためには、Pythonのsocketモジュールだけが必要です。以下のコード例では、気象観測所のシミュレーションと時間計算を簡単にするために、Pythonのtimeモジュールもインポートしています。
この場合、クライアントとサーバーは同じコンピュータで実行されます。ソケットはポート番号に対応しており、この例では23456です。必要であれば、1024から65535の間の無制限の番号から別のポート番号を選択することができます。
サーバー
Python の socket
モジュールをロードすると、インターネットストリーミング用のソケットが socket.socket
クラスを用いて、 socket.AF_INET
と socket.SOCK_STREAM
という 2 つのパラメータで生成されます。ホスト名、完全修飾ドメイン名、IP アドレスの取得は、それぞれ gethostname()
, getfqdn()
, gethostbyname()
というメソッドによって行われる。次に、bind()
メソッドの助けを借りて、ソケットをIPアドレスとポート番号23456にバインドします。
listen()メソッドの助けを借りて、サーバは指定されたポート番号で着信を待ちます。while ループでは、サーバはリクエストが来るのを待ち、
accept()メソッドを用いてそれを受け入れる。クライアントから送信されたデータは
recv()` メソッドによって 64 バイトのチャンクとして読み込まれ、単に標準出力に出力される。最後に、クライアントからそれ以上データが送信されない場合、現在の接続は閉じられる。
# load additional Python module
import socket
# create TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# retrieve local hostname
local_hostname = socket.gethostname()
# get fully qualified hostname
local_fqdn = socket.getfqdn()
# get the according IP address
ip_address = socket.gethostbyname(local_hostname)
# output hostname, domain name and IP address
print ("working on %s (%s) with %s" % (local_hostname, local_fqdn, ip_address))
# bind the socket to the port 23456
server_address = (ip_address, 23456)
print ('starting up on %s port %s' % server_address)
sock.bind(server_address)
# listen for incoming connections (server mode) with one connection at a time
sock.listen(1)
while True:
# wait for a connection
print ('waiting for a connection')
connection, client_address = sock.accept()
try:
# show who connected to us
print ('connection from', client_address)
# receive the data in small chunks and print it
while True:
data = connection.recv(64)
if data:
# output received data
print ("Data: %s" % data)
else:
# no more data -- quit the loop
print ("no more data.")
break
finally:
# Clean up the connection
connection.close()
クライアント
さて、次はクライアント側を見てみましょう。Pythonのコードはソケットを使う以外はほとんどサーバ側と同じです。クライアントは代わりに connect()
メソッドを使っています。forループの中で、
sendall()メソッドを用いて温度データをサーバに送信します。time.sleep(2)
メソッドを呼び出すと、クライアントは次の温度データを送信する前に 2 秒間停止します。すべての温度データがリストから送信された後、最後に close()
メソッドを使用して接続が閉じられます。
# load additional Python modules
import socket
import time
# create TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# retrieve local hostname
local_hostname = socket.gethostname()
# get fully qualified hostname
local_fqdn = socket.getfqdn()
# get the according IP address
ip_address = socket.gethostbyname(local_hostname)
# bind the socket to the port 23456, and connect
server_address = (ip_address, 23456)
sock.connect(server_address)
print ("connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address))
# define example data to be sent to the server
temperature_data = ["15", "22", "21", "26", "25", "19"]
for entry in temperature_data:
print ("data: %s" % entry)
new_data = str("temperature: %s
" % entry).encode("utf-8")
sock.sendall(new_data)
# wait for two seconds
time.sleep(2)
# close connection
sock.close()
サーバーとクライアントの実行
サーバーとクライアントの両方のプログラムを実行するには、2つのターミナルウィンドウを開き、以下のコマンドをターミナルウィンドウごとに1つずつ、以下の順序で実行してください。
$ python3 echo-server.py
と
$ python3 echo-client.py
以下の2つの図は、サンプルプログラムの対応する出力を示しています。
図1
황황
図 2
まとめ
Pythonでソケットを使ったIPCプログラムを書くのは簡単です。この例は、より複雑な処理を行うために拡張することができます。より詳細な情報や追加メソッドについては、Pythonのソケットプログラミングのリソースを参照してください。