ネットワークプログラミング入門② ~UDPで話そう~

ネットワーク
Pythonサーバーネットワークプログラミング

こんにちは.
前回に引き続きネットワークプログラミング入門をしていきます.
今回は実際に,PythonでUDPをネットワークプログラミングしていきます.
それでは,初めて行きましょう!!

作成するプログラム

ここでは,Pythonのsocketを使ってUDPの通信を実装していきます.
UDPの通信を実現するために下記のような2つのプログラムを実装してきます.

  • UDPクライアント
  • UDPサーバ

また,本記事では詳細なUDPの説明は行いませんのでご注意ください.
最低限の知識は前回の記事を参考にしてください.

UDPでのソケット通信

実装を始める前にシーケンス図を用いてUDPの通信の流れを確認していきましょう.
Pythonのsocketを用いたUDPの通信フローは下記の図のようになります.


上の図の流れは以下の表のようになります.

UDP通信を行うために socket() を用いてUDPのソケットを作成します.
bind() を用いて通信を待ち受けるIPアドレスとポート番号を指定します.
これでサーバは通信を受け取る準備が完了します.
サーバのIPアドレスとポート番号を指定し,sendto() を用いてデータを送信します.
recvfrom()1 でクライアントからのデータを受け取ります.
sendto() を用いてクライアントに応答を送信します.
recv()2 を用いてサーバからの応答を受け取ります.
close() で作成したソケットを閉じます
  1. 受信に recv() を使用することもできますが,他の方法で通信相手を指定する必要があります. ↩︎
  2. 受信に recvfrom() を使用することもできます.再帰的に同一サーバにアクセスする際にはrecvfromの方が良いかもしれません. ↩︎


使用する関数

UDPでのネットワークプログラミングをする上で使用する関数は以下の表のようになります.

socket()UDPのソケットを作成します.
bind()待ち受けするIPアドレスとポート番号を指定します.
sendto()宛先を指定してデータを送信します.
recv()データを受信します(データのみ取得).
recvfrom()データを受信します(データと送信元のアドレス情報を取得).
close()socketを閉じます.

データの受信に recv() と recvfrom() を使用していますが,これらの関数の大きな違いは,返り値に送信元IPアドレス (ポート番号) が含まれるかであり,今回のケースではクライアントではサーバのIPアドレスを取得させていないため,recv() を使用しています.

流れと使用する関数を理解した上で,実際にPythonで実装してみましょう.

UDPを使ったネットワークプログラミング

UDPのネットワークプログラミングをしていきます.ここでは最もシンプルなUDPサーバとUDPクライアントを実装していきます.
それではやっていきます.

UDPサーバの実装

「Hello Client!!」を応答する最もシンプルなUDPサーバを実装します.
待ち受けには以下の値を使用します.

IPアドレス127.0.0.1 (localhost)
ポート番号50000
# socketをインポート
import socket

# IPアドレス:127.0.0.1(localhost)とポート番号:50000 を指定
HOST = "127.0.0.1"
PORT = 50000

# 送信する文字列
RESPONSE = "Hello Client!!"

# ⓵ソケットを作成する
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 待ち受けに使用するIPアドレスとポート番号を指定
server_socket.bind((HOST, PORT))

# ⓶クライアントからの接続を受信する
client_message, client_address = server_socket.recvfrom(1024)
client_message = client_message.decode('utf-8')

# クライアントからのメッセージを表示する
print(f'Message received from {client_address}.')
print(f'The message is [ {client_message} ].')

# クライアントに応答する
server_socket.sendto(RESPONSE.encode('utf-8'), client_address)

# ソケットを閉じる
server_socket.close()

##①##
UDPで通信したいため,socket() の作成時に socket.sock_DGRAM を指定します.

##②##
recvfrom(hoge) で指定している値(hoge部分)は受信するデータの最大バイト数です.UDPの場合,最大は655535バイトまでになります.指定したバイト数以上のデータを受信した場合,超えたデータは全て切り捨てられます.

UDPクライアントの実装

次にメッセージとして「Hello Server!!」を送信する最もシンプルなUDPクライアントを実装していきます.
宛先となるIPアドレスとポート番号には,UDPサーバで指定したアドレスを使用します.

# socketをインポート
import socket

# IPアドレス:127.0.0.1(localhost)とポート番号:50000 を指定
HOST = "127.0.0.1"
PORT = 50000

# 送信する文字列
MESSAGE = "Hello Server!!"

# ソケットを作成する
clilent_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# メッセージを送信
clilent_socket.sendto(MESSAGE.encode('utf-8'), (HOST, PORT))
print('Sent a message.')

# サーバからの応答を受信
server_message = clilent_socket.recv(1024)
server_message = server_message.decode('utf-8')

print(f'Server message: {server_message}')

# ソケットを閉じる
clilent_socket.close()



実行してみる

UDPサーバとUDPクライアントを実際に試してみます.
下記コマンドを異なるターミナル(コマンドプロンプト)に入力して実行してください.

・サーバを起動する

python udp-server1.py

・クライアントを実行する

python udp-client1.py


上記のコマンドを実行すると下記のような結果が得られます.

UDPクライアント                       UDPサーバ

サーバ側ではクライアントのメッセージをクライアント側ではサーバのメッセージを確認することが出来ます.
実際に通信をキャプチャしてみるとUDPで通信されていることが分かります.
※No.77: クライアント → サーバへの通信
※No.78: サーバ → クライアントへの通信

UDPクライアントの通信内容

上記の図でもわかるように,UDPは通信の際にパケットを一方的に相手に送り付けています.これがコネクションレス型通信と呼ばれるUDPの特徴です.


以上のことからUDPを使った最もシンプルなネットワークプログラムを実装することが出来ました.

UDPネットワークプログラムの改良

作成したプログラムは一度通信を行うとプログラムが終了してしまいます.
そこで,UDPのプログラムを再帰的に通信ができるようにするなどして改良していきます.

UDPサーバの改良

UDPサーバを改良していきます.
プログラムは前に作成したものをベースに,While文を使うことで繰り返しアクセスができるようにしています.
UDPサーバからの応答内容は,接続してきた回数にします.

# socketをインポート
import socket

# IPアドレス 127.0.0.1(localhost)を指定
HOST = "127.0.0.1"
PORT = 50000
RESPONSE = "Access count: "

# ソケットを作成する
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 待ち受けに使用するIPアドレスとポート番号を指定
server_socket.bind((HOST, PORT))

# アクセス数をカウントする変数を定義
count = 0

while True:
    # クライアントからの接続を受信する
    client_message, client_address = server_socket.recvfrom(4096)
    client_message = client_message.decode('utf-8')
    
    #アクセス数をカウントアップする
    count +=1
    print(f'New access.(access count: {count})')
    
    #応答用のメッセージ内容を作成する
    response = RESPONSE + str(count)

    # クライアントからのメッセージを表示する
    print(f'Message received from {client_address}.')
    print(f'The message is [ {client_message} ].\n')

    # クライアントに応答する
    server_socket.sendto(response.encode('utf-8'), client_address)


UDPクライアントの改良

UDPサーバと同じくUDPクライアントも改良していきます.
UDPクライアントもWhile文を使うことで繰り返し通信を行えるようにしていきます.

ここでは以下の要素を踏まえてプログラムしていきます.

  • UDPクライアントは同一のソケットを用いてアクセスする
  • メッセージには任意の文字を入力できるように変更
# socketをインポート
import socket

# IPアドレス:127.0.0.1(localhost)とポート番号:50000 を指定
HOST = "127.0.0.1"
PORT = 50000

# ソケットを作成する
clilent_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

while True:
    # サーバに送信するメッセージを取得する
    message = input('Please input any message: ')
    
    # メッセージを送信
    clilent_socket.sendto(message.encode('utf-8'), (HOST, PORT))
    print('Sent a message.')

    # サーバからの応答を受信
    server_message = clilent_socket.recv(1024)
    print(server_message.decode('utf-8'))


改良したプログラムを実行してみる

改良したプログラムをさっそく実行してみましょう!
下記コマンドを異なるターミナル(コマンドプロンプト)に入力して実行してください.

・サーバを起動する

python udp-server2.py

・クライアントを実行する
 ※UDPクライアントは実行後に送信する文字列の入力を求められます.

python udp-client2.py

これらのプログラムを実行すると以下の図のようになります.

UDPクライアント                       UDPサーバ

クライアント側で入力した文字列がサーバ側でも確認することが出来ます.
また,サーバ側ではクライアントのアクセス数を確認することが出来ます.

以上より,サーバとクライアントのプログラムを改良して繰り返し接続ができるようになりました.
これらの他にも,様々な機能を追加実装することが出来ます.

まとめ

Pythonのsocketモジュールを使ってUDPで通信するネットワークプログラムを実装しました.
実際に実行し,通信内容を覗き見るとUDPで通信できていることが確認できました.
また,繰り返し通信ができるようにプログラムを改良しました.

今回の内容でUDPの理解は深められたでしょうか.
次回はTCPを使ってネットワークプログラミングしていきます.

コメント

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