はじめに
こんにちは。アメリカで独学でエンジニアを目指している者です。
現在インターネットについて勉強していますが、そこで出てきたsocketライブラリを使用したデータの送受信についての記述があったので忘れないように記述しようと思いました
コンピュータ間の通信を実現するうえで、socketライブラリは非常に基本的かつ重要な役割を果たします。たとえば、WebブラウザでWebSocket APIなどを利用する場合でも、裏側ではソケットを使った通信の仕組みが動作しています。
ここでは、socketライブラリを利用する際の一連の手順(ソケットの生成、接続、データの送受信、切断)について、基本的な概念とともに詳しく説明します。
ソケットの作成とファイルディスクリプタ
ソケット生成の基本
まず、通信に利用するソケットを作成するために、socket()
システムコール(または対応するライブラリ関数)を呼び出します。この呼び出しは、指定した通信プロトコル(例:TCP/IP)に基づく新しいソケットを生成し、操作用のファイルディスクリプタ(整数値)を返します。
- 重要なポイント: この返り値は接続先(リモート側)を表すものではなく、あくまで自分のプロセス内でソケットを参照・操作するためのハンドルです。
ファイルディスクリプタの役割
ファイルディスクリプタは、UNIX系OSで入出力操作に利用される識別子です。ソケットに対して行う読み書き(read()
, write()
, send()
, recv()
など)は、このファイルディスクリプタを通じて実行されます。どの接続に対する操作かを意識せず、同一プロセス内で一貫して管理できるようになる点が特徴です。
なお、UNIX系OSでは socket()
を呼び出すとカーネル内部にソケットの情報(通信プロトコルやバッファ状態など)を格納するテーブルエントリが作成されます。返されるファイルディスクリプタは、このテーブルエントリを参照するためのインデックスとして機能します。したがって、read()
や write()
といったシステムコールでファイルディスクリプタを引数に指定すると、OSは内部テーブルを参照して「どのソケット」に対するI/Oかを判断できる仕組みです。
接続の確立とソケットの関連付け
connect()
関数の役割
クライアント側は、生成したソケットのファイルディスクリプタを用いて、connect()
関数を呼び出し、リモートのサーバー(IPアドレスやホスト名)およびポート番号を指定して接続を試みます。接続が確立されると、同じファイルディスクリプタでリモートとの通信路が確立され、データ送受信に使用できるようになります。
-
補足:
connect()
は内部的にソケットへリモート接続先の情報を紐付ける処理を行います。つまり、socket()
の返り値そのものはリモートの識別子ではなく、あくまでローカル側の操作対象である点に注意してください。
データの送受信
送信処理
接続確立後のデータ送信には、主に send()
関数(あるいは write()
)を使用します。
-
send()
の特徴- 引数として送信するデータのバッファ、サイズ、オプション(例:ブロッキング/ノンブロッキング指定)などを受け取る。
- 一度の呼び出しで送信可能なデータ量が不足する場合、戻り値として送信済みのバイト数が返るため、必要に応じて再送処理を行う必要がある。
受信処理
リモートからのデータ受信には、recv()
関数(あるいは read()
)を利用します。
-
recv()
の特徴- 引数として受信用のバッファおよび受信可能なサイズを指定する。
- 接続が切断された場合やエラーが発生した場合、エラーコードや 0 が返されるため、適切なエラーハンドリングが必要となる。
切断処理
通信が完了したら、close()
関数を呼び出してソケットを閉じます。これにより使用していたリソースが解放され、ネットワーク上の接続も終了します。
-
注意点: TCP などの場合は、正常な切断(
FIN/ACK
シーケンスなど)を介して相手側にも接続終了が通知されます。
まとめ
socketライブラリを用いたデータの送受信は、以下のステップで行われます。
-
ソケット生成:
socket()
によってローカルに新たなソケットが作成され、対応するファイルディスクリプタが返される。 -
接続の確立:
connect()
を使い、リモートのサーバー(IPアドレスやポート番号)を指定して接続を確立する。 -
データの送受信:
-
send()
(またはwrite()
)を用いてデータを送信 -
recv()
(またはread()
)を用いてデータを受信
-
-
切断:
通信が完了したらclose()
でソケットを閉じ、接続を終了する。