UDPのクライアントを作ってみる

それでは、簡単なUDPのクライアントを作ってみましょう。 どういうものにするかは、2段階で行ってみます。

  1. 単純に送って受ける、echoのようなシステム。

  2. JSON形式のデータを送信し、簡単な処理を送り返してもらうシステム。

注釈

JSON版はもう少しお待ちください、後半で行う予定です。

まずは単純なものから行ってみましょう。

UDP echoクライアント

UDPでのechoクライアントを記述して、基本的なネットワークの操作コードを確認していきましょう。

ファイル名はecho-client.pyとします。

 1#!/usr/bin/env python3
 2import socket
 3
 4def main():
 5    # UDPソケットを作る
 6    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
 7        # 今回は、自分自身(localhost)のポート10000を接続先とします(10000/udp)
 8        server_address = ('localhost', 10000)
 9        
10        message = b'Hello, World!' # 送信したいデータ列(バイト列に変換しておく)
11        print(f'送信内容: {message}')
12        sock.sendto(message, server_address)
13
14        # Receive response
15        print('応答待ち')
16        data, server = sock.recvfrom(4096)
17        print(f'受信しました: {data}')
18
19    print('終了します')
20
21if __name__ == '__main__':
22    main()

いろいろしている感じもありますが、実はネットワークに関してはたいしたことはしていません。 少し読み込んでいきましょう。

ソケットの利用

    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:

ネットワークの通信では、ソケット(socket)という概念を使用します。 言葉として聞くであろうソケットであり、言い換えればケーブルのようなものです。 ただ、ソケットでは『皮』と『中身』を決めてあげる必要があります。

  • 皮: ソケットをケーブルと見立てたときの皮であり、どのような材質で相手までひっぱるかになります。

    • シンボルAF_INET(socket.AF_INET)は、IPv4のインターネットプロトコルを使用することを意味します。

    • 今回は使いませんが、IPv6もちろん可能で、AF_INET6(socket.AF_INET6)を使うこともできます。

  • 中身: ソケット(ケーブル)の中身の材質みたいなものです、具体的にはTCPを使うかUDPを使うかということになります。

    • シンボルSOCK_DGRAM(socket.SOCK_DGRAM)は、UDPを使用することを意味します。

    • TCPを使う場合は、SOCK_STREAM(socket.SOCK_STREAM)を使います。

注釈

DGRAMはデータグラムのことです。データグラムという『データのかたまり』を使った通信のことをUDPと呼びます。 TCPはストリーム通信という仕組みを用いて行うため、指定が socket.SOCK_STREAM となっています。

データの送信

データグラムの通信では、過去に学んだように『投げつける』感覚です。そのため、ソケットを作ったとしても明確な接続は行いません。すごく楽ですね。

        message = b'Hello, World!' # 送信したいデータ列(バイト列に変換しておく)
        print(f'送信内容: {message}')
        sock.sendto(message, server_address)

見ての通りで、生成したソケット(sock)を用いてのsendto() です。 送りたいデータと送り先をしているだけとなっています。相手との接続確認とかありません。

データの受信

さて、ソケットによる送信を行ったとき、ソケットの両端はトランスポート(UDP)であるため、ポートによる端点の識別が機能しています。

  • 送り先: コード内で指定したポート番号(今回は10000)

  • 送り元: 特に指定していないので『OSのお任せ』で設定(調べない限りわからない)

この情報は、UDPのヘッダにより相手(接続先)に送られているため、受信側はその情報を基に『必要があれば』返信します。 この操作も『事前の接続なしに』送られてくるため、すぐに受け取れるように待機する必要があります。

        print('応答待ち')
        data, server = sock.recvfrom(4096)
        print(f'受信しました: {data}')

ソケットのrecvfrom()を使うことで、受信待機を行います。 引数は受信データの最大サイズとなります。 戻り値は2つ(タプルで受け取っている)です。

  1. 受信データ(dataで受け取っている)

  2. サーバー側のアドレス情報(serverで受け取っている)

このアドレス情報は、受信したデータの送り元(すなわちサーバー)の情報となります。 今回は単純に表示しているだけですが、この情報を適宜操作してアプリケーションが利用することとなります。

まとめ

UDPのクライアント側は非常にシンプルな操作となっています。 この部分がUDPの特徴となっています。 続いて、サーバー部分を見てみましょう。