[ C++で開発 ]

Windowsソケット(IPv6)プログラミング

WindowsOS上でWinSock APIを使用したIPv6ソケットプログラミングの方法を調べて記述します。

WinSockによる簡単なIPv6ソケットプログラミングのサンプル

まずWinSockを使った簡単なソケットプログラミングのサンプルを見てみましょう。

コネクション指向のソケット通信

クライアント側プログラム

TCP(コネクション指向)のソケット通信を行うサンプルです。エラー処理はばっさり省略しAPI使用に関わる部分だけを記述しています。

#include <winsock2.h>
#include <ws2tcpip.h>

// WinSockの初期化
WORD version = MAKEWORD(2, 2);
WSADATA wsa;
WSAStartup(version, &wsa);

// ホスト情報の作成
ADDRINFO hints;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;

LPADDRINFO res;
int ret = getaddrinfo("foo.bar", NULL, &hints, &res);
// ソケットの作成
SOCKET sock = socket(res->ai_family, res_ai_socktype, res->ai_protocol);

// 接続
connect(sock, res->ai_addr, res->ai_addrlen);

// 送信
const char *send_buffer = "Hello, WinSock World!";
send(sock, send_buffer, sizeof(send_buffer), 0);

// 受信
char recv_buffer[1024];
int size = recv(sock, recv_buffer, sizeof(recv_buffer)-1, 0);

// 切断
closesocket(sock);

// WinSockの終了手続き
WSACleanup();  
WinSockの初期化

WinSockバージョンとWinSock情報構造体(WSADATA)へのポインタを渡してWSAStartupを呼び出します。WSADATAはWinSock詳細データが格納されて返ります。WinSockバージョンは、メジャーバージョン番号とマイナーバージョン番号からなり、最新バージョンは2.2)、TCP/IP以外のトランスポート対応や性能向上などが加わっています。

ホスト情報の作成

ソケットではホスト情報をIPアドレスやホスト名で扱います。文字列で表現するIPアドレスおよびホスト名は、getaddrinfo関数を使用して文字列からアドレス情報へ変換します。アドレス情報は、ADDRINFO型ですが、これはPOSIX 1003.1g(またはRFC 2553)で記載されるstruct addrinfoのtypedefとなっています。(なぜかWindows系のAPIでは大文字で表現する型名へのtypedefが多用される)

ビルド

WinSockライブラリのリンク

WinSock APIを使用する場合、WinSockライブラリとリンクする必要があります。プロジェクトのリンク設定に、ws2_32.libを追加します。

WinSockで使用する型

使用関数/型 ヘッダファイル 用途
WORD (winsock.h) WSAStartupの第1引数に使用
MAKEWORD()
WSADATA WSAStartupの第2引数に使用
WSAStartup() WinSockライブラリ初期化
WSACleanup() WinSockライブラリ解放
LPHOSTENT struct hostent* のtypedef定義
gethostbyname() ホスト名からホスト情報へ変換
SOCKET u_intのtypedef定義
AF_INET
SOCK_STREAM
INVALID_SOCKET -1のデファイン
SOCKADDR_IN struct sockaddr_inのtypedef定義
htons()
LPIN_ADDR struct in_addr*のtypedef定義
connect()
LPSOCKADDR struct sockaddr*のtypedef定義
SOCKET_ERROR -1のデファイン
closesocket()

トラブルシュート

WinSock, WinSock2のバッティング

時折、以下のようなコンパイルエラーに直面します。

ws2def.h(91) : warning C4005: 'AF_IPX' : マクロが再定義されました。
winsock.h(460) : 'AF_IPX' の前の定義を確認してください
ws2def.h(127) : warning C4005: 'AF_MAX' : マクロが再定義されました。
winsock.h(479) : 'AF_MAX' の前の定義を確認してください
ws2def.h(163) : warning C4005: 'SO_DONTLINGER' : マクロが再定義されました。
winsock.h(402) : 'SO_DONTLINGER' の前の定義を確認してください
ws2def.h(206) : error C2011: 'sockaddr' : 'struct' 型の再定義
winsock.h(485) : 'sockaddr' の宣言を確認してください。
ws2def.h(384) : error C2143: 構文エラー : '}' が '定数' の前にありません。
ws2def.h(384) : error C2143: 構文エラー : ';' が '定数' の前にありません。
ws2def.h(384) : error C2059: 構文エラー : '定数'
ws2def.h(437) : error C2143: 構文エラー : ';' が '}' の前にありません。
ws2def.h(437) : error C4430: 型指定子がありません - int と仮定しました。メモ: C++ は int を既定値としてサポートしていません
ws2def.h(437) : error C4430: 型指定子がありません - int と仮定しました。メモ: C++ は int を既定値としてサポートしていません

主な原因は、<WinSock2.h>より先に<windows.h>がインクルードされたことです。
Windowsの制約として、windows.hよりも先にwinsock2.hをインクルードすることが必要です。

これは、Windowsには古いWinsockと新しいWinsockがあり、windows.hは何も制御しないと古いWinsockを読み込みます。

回避方法

1.WinSock2.hとwinsows.hのインクルード順番をあわせる
#include <WinSock2.h>
#include <windows.h>

リンク集