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

ソケットプログラミングによるUDP通信の概要から、基本的な流れをPythonで実装して行きたいと思います。
※[2020/11]こちらの記事にソースコードの実行結果も含めた記事を更新しました。
[Pythonによるソケットプログラミング(UDP編)](https://note-tech.com/python_socket_programming_udp/?preview=true)

# 注意事項
はじめに注意事項ですが、ネットワークプログラムでは使い方によってはDOS攻撃のように他人のネットワークに影響を与えてしまいます。
他人のネットワークに迷惑をかけるような通信を行わないように注意してください。
本記事中の内容によって発生したいかなる損害についても筆者は責任を取ることはできません。

# UDPによるソケットプログラミング
ソケットとはアプリケーションプログラムからネットワーク通信サービスを受けるときのAPIです[1]。
UDPによるソケット通信の一例をシーケンス図で表してみました。
Serverが通信を受け付けてClientからの通信に応答しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204989/c4dc484e-a76d-f350-b3f1-b25ff41d0b3e.png)

#### 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で実装したソースコードが以下になります。


## ソースコード

```client.py
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

```

```server.py
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 --- 低水準ネットワークインターフェイス](https://docs.python.org/ja/3/library/socket.html)


---
余談ですが、記事中のシーケンス図は[StackEdit](https://stackedit.io)を活用しMarkdownで記載したのをキャプチャして貼り付けています。
その時のMarkDownも貼っておきたいと思います。

```mermaid
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
```