LoginSignup
5
6

More than 1 year has passed since last update.

Python Asyncio で作る Socket Server

Last updated at Posted at 2022-09-05

Python の asyncio で作成した socket サーバーです。 簡単なecho サーバです。 OSレベルのAPI selectors を直接使うのではなく、より高レベルの asyncioを使うことで簡潔で読みやすいプログラミングが可能となっています。クライアントとのやり取りをasyncioのコルーチンとして書き、イベントループ上で並列実行させています。
Python Asyncio入門 - Qiita

main.py
import asyncio, socket

async def handle_client(client, loop):
    while data := await loop.sock_recv(client, 1024):
        print( f"data = {data}")
        await loop.sock_sendall(client, data)
    client.close()

async def run_server():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # Server ソケット
    server.setblocking(False)                                   # ノンブロッキングソケット
    server.bind(('localhost', 8000))
    server.listen()
    loop = asyncio.get_event_loop()

    while True:
        client, address = await loop.sock_accept(server)         # Client ソケット
        client.setblocking(False)                                # ノンブロッキングソケット
        print(f"New client from {address}")
        asyncio.create_task(handle_client(client, loop))    # イベントループでスケジューリング

asyncio.run(run_server())

プログラムの起動

python main.py

クライアントからの接続

telnet localhost 8000

コルーチン run_server() はServer ソケットを作成しクライアントからの接続を待ち受けます。クライアントから説毒が来るとacceptしClient ソケットを作成し、コルーチン handle_client()をイベントループに放り込みスケジューリングします(asyncio.create_task())。
コルーチン handle_client()はクライアントからのデータを待ち受け(loop.sock_recv())、受け取るとそのままクライアントに返却します(loop.sock_sendall())。

ここで重要なのが、asyncio の イベントループ上で使える以下の3つのコルーチンです。

  • coroutine loop.sock_accept(sock)
    接続を受け付けます。ブロッキングコールの socket.accept() メソッドをモデルとしています。ソケット sock はアドレスにbind 済みで、接続を listen 中である必要があります。戻り値は (conn, address) のペアで、conn は接続を通じてデータの送受信を行うための 新しい ソケットオブジェクト、address は接続先の端点でソケットに束縛されているアドレスを示します。sock はノンブロッキングソケットでなければなりません。

  • coroutine loop.sock_recv(sock, nbytes)
    nbytes で指定したバイト数までのデータをソケット sock から受信します。 このメソッドは socket.recv() の非同期版です。受信したデータをバイトオブジェクトとして返します。sock はノンブロッキングソケットでなければなりません。

  • coroutine loop.sock_sendall(sock, data)
    データ data をソケット sock に送信します。 socket.sendall() メソッドの非同期版です。このメソッドは data をすべて送信し終えるか、またはエラーが起きるまでデータをソケットに送信し続けます。送信に成功した場合 None を返します。エラーの場合は例外が送出されます。エラーとなった場合、接続の受信側で正しく処理されたデータの総量を特定する方法はありません。sock はノンブロッキングソケットでなければなりません。

イベントループ - Pythonドキュメント

今回は以上です。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6