Winsock socket関数でTCP/UDPソケットを作成する手順
ネットワークプログラミングを始めるにあたって、Windows環境では Winsock (Windows Sockets) API が標準的に利用されます。通信の基盤となるソケットを生成するのが socket 関数であり、トランスポート層プロトコルとして TCP と UDP を選ぶかでソケットの性質が大きく変わります。本記事では、ソケット作成に必要なパラメータ、典型的な呼び出しコード、そしてエラー時の確認ポイントまでを順に整理します。
ソケット作成は、その後の bind、accept、connect、send、recv といった一連の操作の入口になります。プロトコルの選定からディスクリプタの管理まで、最初の一歩を正しく押さえておくことが、安定した通信実装への近道となります。
socket関数の基本仕様
Winsock2 で提供される socket 関数のプロトタイプは次の通りです。引数はアドレスファミリ、ソケットタイプ、プロトコルの三つから構成されています。
SOCKET WSAAPI socket(
int af,
int type,
int protocol
);
戻り値は SOCKET 型で、失敗時には INVALID_SOCKET が返されます。値の判定には定数比較を用い、符号付き整数との比較は避けてください。第一引数には AF_INET (IPv4) または AF_INET6 (IPv6) を、第二引数には用途に応じたソケットタイプを、第三引数には通常 0 を渡すことで自動選択させます。
呼び出しに先立って、WSAStartup で Winsock を初期化しておく必要があります。対応するバージョンは MAKEWORD(2, 2) が推奨され、winsock2.h のインクルードも必須です。
TCPソケットを作成する場合
TCP でストリーム通信を行う場合は、信頼性の高いバイトストリームが得られます。コード例を示します。
SOCKET tcpSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (tcpSock == INVALID_SOCKET) {
// エラー処理
}
SOCK_STREAM を指定すると内部的に IPPROTO_TCP が選ばれますが、明示的に IPPROTO_TCP を渡しても同じ結果になります。作成したソケットはサーバー側では bind → listen → accept、クライアント側では connect の流れで利用します。
ソケット作成後すぐに setsockopt で SO_REUSEADDR を有効化しておくと、サーバープログラムを再起動した際の "Address already in use" エラーを回避しやすくなります。
UDPソケットを作成する場合
UDP でデータグラム通信を行う場合は、コネクションレスで軽量な送受信が可能になります。コードは次の通りです。
SOCKET udpSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udpSock == INVALID_SOCKET) {
// エラー処理
}
SOCK_DGRAM を指定することで、UDP のベストエフォート型通信が得られます。到達保証や順序制御はアプリケーション側で実装する必要がありますが、そのぶんオーバーヘッドが少なく、リアルタイム性が求められる用途に向いています。
UDP ソケットでは connect を省略して sendto/recvfrom を都度使うのが一般的ですが、同一ホストとの固定的な通信であれば connect した後 send/recv を使うパターンも可能です。
エラーハンドリングと WSAGetLastError
socket 関数が失敗した原因を調べるには WSAGetLastError を使います。よく返されるエラーコードには次のようなものがあります。
- WSAEAFNOSUPPORT (10047): アドレスファミリ未対応
- WSAESOCKTNOSUPPORT (10044): ソケットタイプ未対応
- WSAEINPROGRESS (9007): ブロッキング処理が進行中
エラー時は WSACleanup を呼び出してリソースを解放し、必要に応じて再試行や上位層への通知を行います。エラーログにはエラーコードと発生コンテキストを併記しておくと、デバッグが容易になります。
パラメータ選択の指針
アドレスファミリには、現状では AF_INET6 が IPv4 と IPv6 の両方を扱える WSA_PROTOCOL_INFO 指定で利用できるケースもあります。プロトコル自動選択に任せるほうが、互換性確保の観点では有利です。
ソケットタイプに SOCK_RAW を指定すれば ping 相当のパケットを自前で組み立てられますが、Windows では管理者権限が必要であり、ICS 制御やネットワーク解析など特殊な用途に限られます。
プロトコル定数には IPPROTO_TCP、IPPROTO_UDP 以外にも IPPROTO_ICMP などがあります。日常的に TCP/UDP を扱う分には 0 渡しで十分なケースがほとんどです。
ソケット作成後の運用ポイント
作成したソケットは、利用が終わったら closesocket で必ずクローズします。プロセス終了時にリソースが残ると TIME_WAIT 状態が続いたり、他のプロセスがポートを掴めなくなったりする恐れがあります。
高負荷なサーバー用途では、AcceptEx を使った非同期受け入れや、IOCP (I/O Completion Port) との組み合わせが効果的です。シングルスレッド設計でも、WSAEventSelect を併用すればイベント駆動型の実装に移行できます。
ソケットオプションの調整も忘れてはなりません。TCP_NODELAY を有効にすれば Nagle アルゴリズムを無効化できますし、SO_RCVBUF と SO_SNDBUF でバッファサイズをチューニングできます。参考資料として ネットワークプログラミング参考 のような外部ドキュメントを併読しておくと、理解の補強になります。
TCPとUDPソケットの特徴比較
| 項目 | TCP (SOCK_STREAM) | UDP (SOCK_DGRAM) |
|---|---|---|
| 接続方式 | コネクション型 (connect 必須) | コネクションレス |
| 信頼性 | 順序保証・再送制御あり | 到達・順序の保証なし |
| 送受信API | send / recv | sendto / recvfrom |
| 主な用途 | Web、ファイル転送、メール | DNS、VoIP、ゲーム、放送 |
| オーバーヘッド | 比較的大きい | 比較的小さい |
| 作成例 | socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) | socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) |
ソケット作成時の推奨事項
- 呼び出し前に WSAStartup を必ず実行し、戻り値を確認する
- AF_INET6 を検討し、IPv4/IPv6 両対応を見据えた設計にする
- SO_REUSEADDR をサーバー側で設定し、再起動時の競合を防ぐ
- INVALID_SOCKET の判定には定数比較を用い、符号判定を避ける
- WSAGetLastError を取得し、エラーコードごとの分岐処理を準備する
- 必要に応じて SO_SNDBUF と SO_RCVBUF でバッファサイズを最適化する
- 利用後は closesocket を確実に呼び、リソースリークを防止する
実際の開発現場では、ソケット作成だけで完結することは少なく、bind、listen、connect、send、recv、closesocket がセットになります。まずは TCP で echo サーバーを実装し、その後 UDP に拡張していくと、両者の挙動の違いを体感しやすいです。プロトコル選定は要件次第ですが、まずは socket 関数の戻り値とエラーハンドリングを確実に押さえることが、ネットワークプログラミングの土台作りにつながります。