Pythonによるソケットプログラミング(UDP編)

pythonによるソケットプログラミング Linux

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の処理

  1. socketシステムコールを用いてUDP用のソケットを作成します。
  2. bindシステムコールを用いて使用するIPアドレスとポート番号を指定します。
    ここまででOS内部でパケットを受信する準備が出来ました。
  3. recvfromシステムコールを用いてClientからのmessageを待ちます。
    サーバプログラムはrecvfromの引数から、受信したメッセージの送り主(Client)のIPアドレスとポート番号を把握します。
  4. sendtoシステムコールでClientへmessageを送信します。
    Serverがmessageを送信する際はrecvfromシステムコールで受信したIPとポートをしようします。

Clientの処理

  1. socketシステムコールを用いてUDP用のソケットを作成します。
  2. sendtoシステムコールを用いてServerへmessageを送信します。
    Client側のポート番号は最初にsendtoを実行した際にOSによって自動的に割り当てます。
  3. 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

実行結果

上記ソースコードを実行して、実際の動作を確認してみます。
サーバーは起動すると、クライアントからのメッセージを受け付けます。
クライアントは起動すると、サーバーに送信するメッセージの入力を受け付けます。

udp_01

クライアントでメッセージを入力し、サーバーへ送信します。
サーバーでクライアントからのメッセージが受信されているのがわかります。
サーバーは受信完了のレスポンスをクライアントに送信し、クライアントは次のメッセージの受付待ち状態になります。

udp_02

以上で、動作確認も完了です。

終わりに

PythonによるUDP通信について動作フローを紹介し、シンプルなソースコードを動かしてみました。
需要がありそうでしたらTCP編も執筆したいと思います。

参考資料

コメント

タイトルとURLをコピーしました