it

TCPのソケットとは?プログラミングでの使い方も!(通信エンドポイント:IPアドレス:ポート番号:API:サーバー・クライアントなど)

当サイトでは記事内に広告を含みます

TCPのソケットとは?プログラミングでの使い方も!(通信エンドポイント:IPアドレス:ポート番号:API:サーバー・クライアントなど)

ネットワークプログラミングを学ぶうえで、「ソケット」という概念は避けて通れない重要なテーマです。Webアプリケーションやサーバー間通信、チャットアプリなど、あらゆるネットワーク通信の根幹にはソケットが存在しています。しかし「ソケットって何?」「TCPとどう関係するの?」と疑問を持つ方も多いのではないでしょうか。

この記事では、TCPのソケットの基本概念から始まり、IPアドレスやポート番号との関係、サーバー・クライアントモデル、そして実際のPythonを使ったプログラミングでの使い方まで、わかりやすく解説していきます。初心者の方でも理解しやすいよう、サンプルコードも交えながら丁寧に説明しますので、ぜひ最後までお読みください。

TCPのソケットとは通信エンドポイントを識別する仕組み

それではまず、TCPのソケットとは何かという根本的な部分から解説していきます。

ソケット(Socket)とは、ネットワーク通信において「通信の出入り口」となるエンドポイントのことです。電話でたとえるなら、電話機そのものに相当します。通信したい相手と「つながるための口」を用意することで、データの送受信が可能になるわけです。

TCPソケットは、TCP(Transmission Control Protocol)というプロトコルを使った信頼性の高い通信を実現するためのソケットです。TCPは「コネクション型」のプロトコルであり、送受信の前に必ず接続を確立し、データが確実に届くことを保証するという特徴を持っています。

TCPソケットの最大の特徴は「信頼性」です。データの順序保証・再送制御・エラー検出といった仕組みにより、相手にデータが届いたかどうかを確認しながら通信を行います。WebブラウザやメールクライアントなどほとんどのアプリケーションがTCPを採用している理由はここにあります。

ソケットとIPアドレスの関係

ソケットを理解するには、IPアドレスとの関係を把握することが欠かせません。IPアドレスとは、ネットワーク上のコンピューターを識別するための住所のようなものです。

ソケットは内部的にIPアドレスを持っており、「どのコンピューターと通信するか」を特定するために使用されます。IPv4アドレスであれば「192.168.1.1」のような形式で表現され、インターネット上では世界中で一意に識別できる仕組みになっています。

ソケットを作成する際には、このIPアドレスを指定することで、通信相手のマシンを特定します。言い換えれば、IPアドレスはソケット通信における「宛先の住所」の役割を担っているわけです。

ソケットとポート番号の関係

IPアドレスだけでは、そのコンピューター上のどのアプリケーションと通信するかが不明です。そこで登場するのがポート番号です。

ポート番号は0から65535までの整数で表され、同一のIPアドレスを持つマシン上で動作する複数のアプリケーションを区別するために使われます。下の表によく使われるポート番号の例をまとめています。

ポート番号 プロトコル/アプリケーション 用途
80 HTTP Webページの閲覧
443 HTTPS セキュアなWebページの閲覧
25 SMTP メール送信
22 SSH リモートログイン
3306 MySQL データベース接続

ソケットは「IPアドレス+ポート番号」の組み合わせによって一意に特定されます。この組み合わせが、まさに通信エンドポイントの正体です。

TCPにおける3ウェイハンドシェイク

TCPソケットによる通信では、データ送信の前に3ウェイハンドシェイクと呼ばれる接続確立の手順が行われます。これはTCPの信頼性を支える重要な仕組みです。

具体的には、クライアントからサーバーへ「SYN」(接続要求)を送り、サーバーが「SYN-ACK」(了承+要求)を返し、クライアントが「ACK」(確認)を返すという3段階のやり取りで接続が確立されます。この手順を経ることで、両者が通信できる状態にあることを相互に確認してからデータ送受信を開始できるわけです。

サーバーとクライアントの役割とソケット通信の流れ

続いては、TCPソケット通信における「サーバー」と「クライアント」の役割と、実際の通信の流れを確認していきます。

ネットワーク通信は基本的にサーバー・クライアントモデルで成り立っています。サービスを提供する側が「サーバー」、サービスを要求する側が「クライアント」です。WebブラウザがWebサーバーにページを要求する仕組みがその代表例でしょう。

サーバー側のソケット処理の流れ

サーバー側では、クライアントからの接続を待ち受けるための手順が必要です。大まかな流れは以下のとおりです。

ステップ 操作 内容
1 socket() ソケットの作成
2 bind() IPアドレスとポート番号を紐づける
3 listen() 接続の待ち受け開始
4 accept() クライアントからの接続を受け入れる
5 recv() / send() データの受信・送信
6 close() ソケットのクローズ

bind()でポート番号を指定することで、「このポートに来た通信を受け付ける」という宣言を行います。続いてlisten()で待機状態に入り、accept()でクライアントとの接続を確立します。

クライアント側のソケット処理の流れ

クライアント側の処理は、サーバーよりシンプルです。ソケットを作成し、サーバーのIPアドレスとポート番号を指定してconnect()を呼び出すだけで接続が開始されます。

接続が確立したら、send()でデータを送信し、recv()でサーバーからの応答を受信します。処理が完了したらclose()でソケットを閉じるのが基本的な流れです。クライアント側ではbind()が不要な点がサーバーとの大きな違いでしょう。

ソケット通信におけるAPIの役割

プログラムからソケット通信を扱う際には、ソケットAPIと呼ばれるインターフェースを使用します。OSが提供するこのAPIを通じて、socket()やbind()、connect()といった関数を呼び出すことで、ネットワーク通信が可能になります。

PythonやJava、C言語など多くのプログラミング言語では、このOSのソケットAPIをラップしたライブラリが標準で用意されています。Pythonではsocketモジュールがその役割を担っており、少ないコードで本格的なネットワーク通信を実装できます。

Pythonによるソケットプログラミングの実践

続いては、実際にPythonを使ったソケットプログラミングの具体的なコードを確認していきます。

Pythonには標準ライブラリとしてsocketモジュールが用意されており、importするだけですぐに使い始めることができます。ここではサーバー側とクライアント側のコードをそれぞれ見ていきましょう。

サーバー側のサンプルコード

まずはサーバー側のコードです。ポート番号12345で待ち受け、クライアントからメッセージを受信して返信するシンプルなエコーサーバーの例です。


import socket
サーバーのIPアドレスとポート番号を設定
HOST = '127.0.0.1'
PORT = 12345
TCPソケットを作成(AF_INET:IPv4、SOCK_STREAM:TCP)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ポート番号をソケットに紐づける
server_socket.bind((HOST, PORT))
接続の待ち受けを開始(最大接続待ち数:1)
server_socket.listen(1)
print('サーバー起動。接続を待っています...')
クライアントからの接続を受け入れる
conn, addr = server_socket.accept()
print(f'接続されました:{addr}')
クライアントからデータを受信(最大1024バイト)
data = conn.recv(1024)
print(f'受信したメッセージ:{data.decode()}')
クライアントへ返信
conn.send('サーバーから返信:メッセージを受け取りました'.encode())
ソケットを閉じる
conn.close()
server_socket.close()
出力結果:サーバー起動。接続を待っています...
出力結果:接続されました:('127.0.0.1', 54321)
出力結果:受信したメッセージ:こんにちは、サーバー!

socket.AF_INETはIPv4を使用することを意味し、socket.SOCK_STREAMはTCP通信を行うことを指定しています。この2つの引数の組み合わせが、TCPソケットを作成するための基本的な書き方です。

クライアント側のサンプルコード

次にクライアント側のコードです。サーバーに接続してメッセージを送り、返信を受け取るシンプルな例になります。


import socket
接続先サーバーのIPアドレスとポート番号
HOST = '127.0.0.1'
PORT = 12345
TCPソケットを作成
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
サーバーに接続
client_socket.connect((HOST, PORT))
print('サーバーに接続しました')
サーバーへメッセージを送信
message = 'こんにちは、サーバー!'
client_socket.send(message.encode())
サーバーからの返信を受信
response = client_socket.recv(1024)
print(f'サーバーからの返信:{response.decode()}')
ソケットを閉じる
client_socket.close()
出力結果:サーバーに接続しました
出力結果:サーバーからの返信:サーバーから返信:メッセージを受け取りました

クライアント側ではbind()を呼ばず、connect()で直接接続先を指定している点がポイントです。encode()とdecode()はテキストをバイト列に変換したり、バイト列をテキストに戻したりするメソッドで、ソケット通信ではバイト列でデータを扱う必要があるため忘れずに使用しましょう。

複数クライアントに対応するマルチスレッドサーバー

実際のアプリケーションでは、複数のクライアントから同時に接続を受け付けることが必要なケースが多いです。そのような場合はthreadingモジュールを組み合わせたマルチスレッドサーバーが有効です。


import socket
import threading
HOST = '127.0.0.1'
PORT = 12345
各クライアントの処理を担当する関数
def handle_client(conn, addr):
print(f'接続:{addr}')
# クライアントからデータを受信
data = conn.recv(1024)
print(f'{addr} からのメッセージ:{data.decode()}')
# 返信を送信
conn.send(f'アボカド・ドラゴンフルーツ・サーモンの在庫を確認しました'.encode())
conn.close()
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
print('マルチスレッドサーバー起動中...')
複数クライアントからの接続を受け付けるループ
while True:
conn, addr = server_socket.accept()
# 新しいスレッドを生成してクライアント処理を行う
thread = threading.Thread(target=handle_client, args=(conn, addr))
thread.start()
print(f'現在のスレッド数:{threading.active_count() - 1}')
出力結果:マルチスレッドサーバー起動中...
出力結果:接続:('127.0.0.1', 54400)
出力結果:('127.0.0.1', 54400) からのメッセージ:在庫確認をお願いします
出力結果:現在のスレッド数:1

threading.Threadを使うことで、クライアントごとに独立したスレッドを立ち上げて処理できます。listen(5)の引数は接続待ちキューの最大数を表しており、同時に複数のクライアントからの接続要求を受け付けることが可能になります。

ソケット通信に関連する重要な概念と注意点

続いては、TCPソケット通信をより深く理解するために押さえておきたい重要な関連概念と注意点を確認していきます。

ブロッキングとノンブロッキングソケット

デフォルトのソケットはブロッキングモードで動作します。ブロッキングとは、recv()やaccept()などの処理が完了するまでプログラムの実行が止まる(待機する)動作のことです。

シンプルなプログラムではブロッキングモードで十分ですが、大規模なアプリケーションではノンブロッキングモードや非同期処理(asyncio)が有効です。setblocking(False)を呼び出すことでノンブロッキングモードに切り替えることができます。


import socket
ノンブロッキングモードのソケット例
nb_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ノンブロッキングに設定
nb_socket.setblocking(False)
タイムアウトを設定する方法(秒単位)
nb_socket.settimeout(5.0)
print('ノンブロッキングソケットを作成しました')
print(f'タイムアウト設定:{nb_socket.gettimeout()} 秒')
nb_socket.close()
出力結果:ノンブロッキングソケットを作成しました
出力結果:タイムアウト設定:5.0 秒

settimeout()を使うとブロッキングとノンブロッキングの中間的な動作が実現できます。指定した秒数を超えるとsocket.timeout例外が発生するため、エラーハンドリングと組み合わせて使うとよいでしょう。

ソケットオプションとSO_REUSEADDR

サーバープログラムを開発していると、一度停止したサーバーをすぐに再起動しようとした際に「Address already in use」というエラーが発生することがあります。これは、OSがポートをしばらくの間保持し続けるためです。

この問題を解決するのがSO_REUSEADDRオプションです。


import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
SO_REUSEADDRを設定してポートを即時再利用可能にする
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('127.0.0.1', 12345))
server_socket.listen(1)
print('SO_REUSEADDRが有効なサーバーを起動しました')
print('ポート 12345 で接続を待っています')
server_socket.close()
出力結果:SO_REUSEADDRが有効なサーバーを起動しました
出力結果:ポート 12345 で接続を待っています

setsockopt()の第1引数はソケットレベルを指定し、第2引数はオプション名、第3引数は値(1で有効)を表しています。サーバープログラムでは必ず設定することを推奨します。

TCPとUDPの違いと使い分け

ソケット通信にはTCPのほかにUDP(User Datagram Protocol)という選択肢もあります。両者の違いを理解することで、用途に応じた適切な選択が可能です。

項目 TCP UDP
接続方式 コネクション型 コネクションレス型
信頼性 高い(再送・順序保証あり) 低い(再送・順序保証なし)
速度 比較的遅い 高速
主な用途 Web・メール・ファイル転送 動画配信・オンラインゲーム・DNS
ソケット指定 SOCK_STREAM SOCK_DGRAM

信頼性が最優先な場合はTCPを、速度や低遅延が重要なリアルタイム通信ではUDPを選ぶのが一般的です。PythonではSOCK_DGRAMを指定するだけでUDPソケットを作成できます。

TCPは「確実に届けること」を優先するプロトコルです。一方UDPは「速く届けること」を優先し、多少のデータロスは許容するプロトコルです。動画ストリーミングで多少コマ落ちしても視聴を継続できるのは、UDPのこの特性があるからこそです。

まとめ

この記事では、TCPのソケットとは何かについて、通信エンドポイント・IPアドレス・ポート番号・API・サーバー・クライアントといった関連概念を交えながら幅広く解説しました。

ソケットはネットワーク通信の根幹をなす仕組みであり、「IPアドレス+ポート番号」の組み合わせで通信相手を一意に特定するエンドポイントです。PythonのsocketモジュールはこのOSのソケットAPIをシンプルに扱えるように設計されており、数十行のコードでサーバー・クライアント通信を実装することが可能です。

ブロッキング・ノンブロッキングの概念やSO_REUSEADDRといった実践的な知識も、実際の開発では欠かせません。TCPとUDPの特徴の違いを理解することで、アプリケーションに最適なプロトコルを選べるようになるでしょう。

ソケットプログラミングは最初は難しく感じるかもしれませんが、基本の流れさえ掴めば応用の幅は無限に広がります。ぜひ今回のサンプルコードを実際に動かしながら、ネットワークプログラミングの世界を体験してみてください。