記事内にはプロモーションが含まれています

【Python】ソケット通信の基礎と実践!TCP/UDPの使い分けや複数接続の方法

【Python】ソケット通信の基礎と実践!TCP/UDPの使い分けや複数接続の方法 Python

ネットワークを通じてデータをやり取りする「ソケット通信」は、チャットアプリやIoTデバイスの制御、リアルタイムゲームなど、あらゆるネットワークプログラミングの根幹をなす技術です。

Pythonには標準で socket モジュールが用意されており、これを使えば比較的簡単にサーバーとクライアント間の通信プログラムを作成できます。

しかし、「TCPとUDPはどう違うの?」「サーバーとクライアント、どちらから動かせばいいの?」といった疑問を持つ方も多いでしょう。

この記事では、Pythonを使ったソケット通信の仕組みから、TCP/UDPそれぞれのサンプルコード、複数クライアントとの同時接続テクニックまで、現在の開発環境に即したベストプラクティスを徹底解説します。

【本記事の信頼性】
プロフィール
執筆者:マヒロ
  • 執筆者は元エンジニア
  • SES⇒大手の社内SE⇒独立
  • 現在はプログラミングスクールを運営
  • モットーは「利他の精神」
💻 本記事の検証環境(2026年2月確認)
  • OS:Windows 11 / macOS Sequoia
  • IDE:Visual Studio / VS Code / IntelliJ IDEA
  • その他:Chrome DevTools / 各言語最新安定版

※本メディアでは、上記環境にてコードの動作と情報の正確性を検証済みです。

ソケット通信とは?TCPとUDPの違いを理解する

ソケット通信とは、IPアドレスとポート番号を指定して、ネットワーク上の異なるコンピュータ(または同一PC内の異なるプログラム)間でデータを送受信する仕組みのことです。

通信プロトコルには主にTCPUDPの2種類があり、用途によって使い分けます。

特徴 TCP (Transmission Control Protocol) UDP (User Datagram Protocol)
信頼性 高い(データの到達確認を行う) 低い(送りっぱなし)
速度 UDPよりは遅い 高速
用途 Web閲覧、メール、ファイル転送、チャット 動画ストリーミング、オンラインゲーム、音声通話

「確実にデータを届けたいならTCP」「多少データが欠落してもリアルタイム性を重視するならUDP」と覚えておきましょう。

なお、多くのアプリケーションではTCPが使われます。

【基本】TCPによるサーバー・クライアント通信の実装

まずは、最も一般的なTCPを使った通信プログラムを作成してみましょう。

サーバー側で待ち受け(bind/listen)を行い、クライアント側から接続(connect)してメッセージを送る流れになります。

1. サーバー側のコード(受信側)

import socket

def start_server():
    # ソケットオブジェクトの作成
    # AF_INET: IPv4, SOCK_STREAM: TCP
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # IPアドレスとポート番号を指定してバインド
    # '127.0.0.1' はローカルホスト(自分自身)
    server_socket.bind(('127.0.0.1', 5000))

    # 接続の待ち受けを開始(引数はキューの最大数)
    server_socket.listen(1)
    print("サーバーを起動しました。接続を待機しています...")

    try:
        # クライアントからの接続を受け入れる
        # accept()は接続があるまで処理をブロック(待機)する
        client_socket, address = server_socket.accept()
        print(f"クライアント({address})と接続しました。")

        while True:
            # データを受信(最大1024バイト)
            data = client_socket.recv(1024)
            
            if not data:
                print("クライアントからの切断を検知しました。")
                break

            # 受信データをデコードして表示
            message = data.decode('utf-8')
            print(f"受信: {message}")

            # クライアントへ応答を送信
            response = f"サーバー受信完了: {message}"
            client_socket.send(response.encode('utf-8'))

    except KeyboardInterrupt:
        print("\nサーバーを停止します。")
    finally:
        # ソケットを閉じてリソースを解放
        # client_socket変数が定義されているか確認してから閉じる
        if 'client_socket' in locals():
            client_socket.close()
        server_socket.close()

if __name__ == '__main__':
    start_server()

socket.socket() でソケットを作成し、bind() でアドレスを割り当て、listen() で待ち受け状態にします。

重要なのは accept() メソッドです。
ここでプログラムはクライアントからの接続があるまで一時停止(ブロック)します。

接続が確立されると、新しいソケットオブジェクト(client_socket)が返され、これを使ってデータの recv(受信)や send(送信)を行います。

2. クライアント側のコード(送信側)

import socket

def start_client():
    # ソケットオブジェクトの作成
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        # サーバーに接続
        client_socket.connect(('127.0.0.1', 5000))
        print("サーバーに接続しました。")

        while True:
            message = input("送信するメッセージを入力してください (qで終了): ")
            
            if message == 'q':
                break

            # メッセージをエンコードして送信
            client_socket.send(message.encode('utf-8'))

            # サーバーからの応答を受信
            response = client_socket.recv(1024)
            print(f"サーバーからの応答: {response.decode('utf-8')}")

    except ConnectionRefusedError:
        print("サーバーに接続できませんでした。")
    finally:
        client_socket.close()
        print("接続を終了しました。")

if __name__ == '__main__':
    start_client()

クライアント側は bindlisten は不要で、connect() メソッドでサーバーのIPとポートを指定して接続します。

データの送受信には sendrecv を使いますが、文字列は必ず encode('utf-8') でバイト列(bytes型)に変換してから送信し、受信時は decode で文字列に戻す必要があります。

【実行方法】

  1. ターミナルを2つ開きます。
  2. 片方で python server.py を実行(待機状態になります)。
  3. もう片方で python client.py を実行。
  4. クライアント側で文字を入力すると、サーバーに送信され、応答が返ってきます。

【応用】UDPによる高速な通信の実装

次に、接続手続き(ハンドシェイク)を行わず、一方的にデータを送りつけるUDP通信の例です。
動画配信やセンサーデータの送信などで使われます。

UDPサーバー(受信側)とクライアント(送信側)

import socket

# --- サーバー側 ---
def udp_server():
    # SOCK_DGRAM を指定するとUDPになる
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_socket.bind(('127.0.0.1', 6000))

    print("UDPサーバー起動中...")
    while True:
        # データと送信元アドレスを同時に受け取る
        # 接続確立がないため recv ではなく recvfrom を使う
        data, address = udp_socket.recvfrom(1024)
        print(f"送信元: {address}, データ: {data.decode('utf-8')}")

# --- クライアント側 ---
def udp_client():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_addr = ('127.0.0.1', 6000)

    message = "UDPメッセージです"
    # 接続せずに sendto で宛先を指定して送信
    udp_socket.sendto(message.encode('utf-8'), server_addr)
    print("送信完了")
    udp_socket.close()

UDPでは connectaccept が不要です。

サーバーは bind した後、recvfrom でデータが来るのを待ちます。

クライアントは sendto で宛先を指定してデータを投げます。

「届いたかどうかの確認」を行わないため、コードがシンプルで処理が軽いのが特徴です。

複数クライアントと同時に通信する方法(マルチスレッド)

先ほどのTCPサーバーのコードは、一度に1つのクライアントとしか通信できません(while ループで特定のクライアントとの対話を続けるため)。

チャットアプリのように複数のクライアントを同時に扱いたい場合は、threading モジュールを使って処理を並列化します。

マルチスレッド対応サーバーの実装

import socket
import threading

def handle_client(client_socket, address):
    """クライアントごとの処理を担当する関数"""
    print(f"接続開始: {address}")
    try:
        while True:
            data = client_socket.recv(1024)
            if not data:
                break
            
            msg = data.decode('utf-8')
            print(f"受信({address}): {msg}")
            client_socket.send(f"ACK: {msg}".encode('utf-8'))
    except Exception as e:
        print(f"エラー発生: {e}")
    finally:
        client_socket.close()
        print(f"接続終了: {address}")

def start_threaded_server():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('0.0.0.0', 5000)) # 0.0.0.0は全てのインターフェースで待受
    server.listen(5)
    print("マルチスレッドサーバー起動...")

    while True:
        # メインスレッドは接続の受付(accept)に専念する
        client, addr = server.accept()
        
        # 接続されたら、新しいスレッドを作成して処理を任せる
        client_handler = threading.Thread(target=handle_client, args=(client, addr))
        client_handler.start()

if __name__ == '__main__':
    start_threaded_server()

accept() で接続を受け入れた直後に threading.Thread を作成し、クライアントとの具体的な通信処理(handle_client 関数)を別スレッドに逃がしています。

これにより、メインスレッドはすぐに次の accept() 待ちに戻れるため、複数のクライアントからの接続を次々に受け入れることができます。

ソケット通信でよくあるエラーと対処法

通信プログラミングでは特有のエラーが発生します。主なものと対策を知っておきましょう。

Address already in use(アドレスは既に使用されています)

サーバーを再起動した直後などに発生しやすいエラーです。

OSがポートをまだ解放しきれていない場合に起こります。

対策: ソケット作成時に SO_REUSEADDR オプションを設定します。

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# ポートの再利用を許可する設定
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(...)

ConnectionRefusedError(対象のコンピューターによって拒否されました)

クライアント実行時に、サーバーが起動していない、またはIPアドレス・ポート番号が間違っている場合に発生します。

対策: サーバーが正しく listen しているか、ファイアウォールでブロックされていないかを確認してください。

BlockingIOError / タイムアウト

データが届いていないのに recv を呼ぶと、プログラムがそこでフリーズ(ブロック)します。

対策: settimeout() を使ってタイムアウト時間を設定するか、ノンブロッキングモードを利用します。

Pythonのスキルを活かして年収を上げる方法

以上、Pythonを使ったソケット通信の仕組みなどについて解説してきました。

なお、Pythonのスキルがある場合には、「転職して年収をアップさせる」「副業で稼ぐ」といった方法を検討するのがおすすめです。

Pythonエンジニアの需要は非常に高いため、転職によって数十万円の年収アップはザラで、100万円以上年収が上がることも珍しくありません。

なお、転職によって年収を上げたい場合は、エンジニア専門の転職エージェントサービスを利用するのが最適です。
今すぐ転職する気がなくとも、とりあえず転職エージェントに無料登録しておくだけで、スカウトが届いたり、思わぬ好待遇の求人情報が送られてきたりするというメリットがあります。
併せて、副業案件を獲得できるエージェントにも登録しておくと、空いている時間を活かして稼げるようなPythonの案件を探しやすくなります。

転職エージェントも副業エージェントも、登録・利用は完全無料なので、どんな求人や副業案件があるのか気になる方は、気軽に利用してみるとよいでしょう。
エンジニアのキャリア・スキルアップ相談窓口
当ブログの読者に選ばれている、実績豊富な転職エージェントを厳選しました。
【転職】年収・環境を改善したい
年収アップにこだわりたい方 (平均アップ額138万円の実績)
未経験・経験者問わず幅広く探したい方
業界に精通した担当者に相談したい方
ゲーム業界への転職を志望する方
エンジニア未経験からキャリアを築く方
【独立】フリーランスとして稼ぎたい
国内最大級のフリーランス案件数から比較したい方
週1〜3日など柔軟な働き方を希望する方
【学習】スキルに不安のある方向け(格安スクール「デイトラ」)