Pythonを用いてUDP通信を行う方法を、具体的なシーケンス及びソースコードを交えて紹介していきます。
※本記事は以前Qiitaに投稿したこちらの記事を更新したものになります。
注意事項
はじめに注意事項ですが、ネットワークプログラムでは使い方によってはDOS攻撃のように他人のネットワークに影響を与えてしまいます。
他人のネットワークに迷惑をかけるような通信を行わないように注意してください。
本記事中の内容によって発生したいかなる損害についても筆者は責任を取ることはできません。
UDPによるソケットプログラミング
ソケットとはアプリケーションプログラムからネットワーク通信サービスを受けるときのAPIです[1]。
UDPによるソケット通信の一例をシーケンス図で表しました。Serverは起動すると通信を受け付けるようになります。Clientはサーバーに向けてメッセージを送信します。
sequenceDiagram
participant Client
participant Server
Note over Server: ① socket
Note right of Server: UDP用のSocketを<br/>作成
Note over Client: ① socket
Note left of Client: UDP用のSocketを<br/>作成
Note over Server: ② bind
Note right of Server: ServerのIP、Portを<br/>設定
Note over Server: ③ recvfrom
Note right of Server: Clientからの受信<br/>待ち
Note over Client: ② sendto
Note left of Client: Serverへmessageを<br/>送信
Client ->> Server: message
Note over Client: ③ recvfrom
Note left of Client: Serverからの<br/>受信待ち
Note over Server: ④ sendto
Note right of Server: Serverへmessageを<br/>送信
Server ->> Client: message
Serverの処理
- socketシステムコールを用いてUDP用のソケットを作成します。
- bindシステムコールを用いて使用するIPアドレスとポート番号を指定します。
ここまででOS内部でパケットを受信する準備が出来ました。 - recvfromシステムコールを用いてClientからのmessageを待ちます。
サーバプログラムはrecvfromの引数から、受信したメッセージの送り主(Client)のIPアドレスとポート番号を把握します。 - sendtoシステムコールでClientへmessageを送信します。
Serverがmessageを送信する際はrecvfromシステムコールで受信したIPとポートをしようします。
Clientの処理
- socketシステムコールを用いてUDP用のソケットを作成します。
- sendtoシステムコールを用いてServerへmessageを送信します。
Client側のポート番号は最初にsendtoを実行した際にOSによって自動的に割り当てます。 - recvfromシステムコールでServerからのmessageを受信します。
ソケットシステムコール
今回の記事で使用したソケットシステムコールを記載します。
TCPのようなコネクション型の通信を行う場合には他にも必要なシステムコールが増えてきます。
システムコール | 説明 |
---|---|
socket() | ソケットを作成します。 |
bind() | ソケットに自ホストのIPアドレス(、ポート番号)を設定します。 |
recvfrom() | messageの受信を受け付けます(コネクションレス型)。送信元のアドレスを返り値として取得します。 |
sendto() | 送信先のアドレスを指定してmessageを送信します(コネクションレス型)。 |
close() | ソケットを閉じます。 |
以上の流れをpythonで実装したソースコードが以下になります。
ソースコード
- クライアントのソースコード
import socket
M_SIZE = 1024
# Serverのアドレスを用意。Serverのアドレスは確認しておく必要がある。
serv_address = ('127.0.0.1', 8890)
# ①ソケットを作成する
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
try:
# ②messageを送信する
print('Input any messages, Type [end] to exit')
message = input()
if message != 'end':
send_len = sock.sendto(message.encode('utf-8'), serv_address)
# ※sendtoメソッドはkeyword arguments(address=serv_addressのような形式)を受け付けないので注意
# ③Serverからのmessageを受付開始
print('Waiting response from Server')
rx_meesage, addr = sock.recvfrom(M_SIZE)
print(f"[Server]: {rx_meesage.decode(encoding='utf-8')}")
else:
print('closing socket')
sock.close()
print('done')
break
except KeyboardInterrupt:
print('closing socket')
sock.close()
print('done')
break
- サーバーのソースコード
import socket
import time
M_SIZE = 1024
#
host = '127.0.0.1'
port = 8890
locaddr = (host, port)
# ①ソケットを作成する
sock = socket.socket(socket.AF_INET, type=socket.SOCK_DGRAM)
print('create socket')
# ②自ホストで使用するIPアドレスとポート番号を指定
sock.bind(locaddr)
while True:
try :
# ③Clientからのmessageの受付開始
print('Waiting message')
message, cli_addr = sock.recvfrom(M_SIZE)
message = message.decode(encoding='utf-8')
print(f'Received message is [{message}]')
# Clientが受信待ちになるまで待つため
time.sleep(1)
# ④Clientへ受信完了messageを送信
print('Send response to Client')
sock.sendto('Success to receive message'.encode(encoding='utf-8'), cli_addr)
except KeyboardInterrupt:
print ('\n . . .\n')
sock.close()
break
実行結果
上記ソースコードを実行して、実際の動作を確認してみます。
サーバーは起動すると、クライアントからのメッセージを受け付けます。
クライアントは起動すると、サーバーに送信するメッセージの入力を受け付けます。
クライアントでメッセージを入力し、サーバーへ送信します。
サーバーでクライアントからのメッセージが受信されているのがわかります。
サーバーは受信完了のレスポンスをクライアントに送信し、クライアントは次のメッセージの受付待ち状態になります。
以上で、動作確認も完了です。
終わりに
PythonによるUDP通信について動作フローを紹介し、シンプルなソースコードを動かしてみました。
需要がありそうでしたらTCP編も執筆したいと思います。
参考資料
- [1][基礎からわかるTCP/IP ネットワーク実験プログラミング―Linux/FreeBSD対応]
(https://www.amazon.co.jp/基礎からわかるTCP-IP-ネットワーク実験プログラミング―Linux-FreeBSD対応-村山/dp/4274065847) - [2]socket --- 低水準ネットワークインターフェイス
コメント