はじめての WinSock 2 プログラムの説明

それでは今回は、前回ビルドした はじめての WinSock2 プログラム の内容を説明します。 まだ試していない人はそちらもチラ見しておいてください。

では、コードを上からいきます。

ヘッダーファイル ~ WinSock2 用は winsock2.h と ws2tcpip.h

ヘッダーファイルは次の3つを取り込んでいます。

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

WinSock 2 を利用するときの基本的なヘッダファイルは winsock2.h です。 ws2tcpip.h には WinSock2 PSA (WinSock 2 Protocol-Specific Annex) で新しく定義されたデータ構造や関数が定義されています。

stdio.h は説明するまでもありませんね。 printf 等を使うために取り込んでいます。

WinSock の初期化

最初にやらないといけないことは、WinSock ライブラリの初期化です。初期化は WSAStartup 関数を使います。

int main(int argc, char* argv[]) {

     INT rc;
     WSADATA wsaData;
     addrinfo hints, *pAddrInfo = NULL;
     ZeroMemory(&hints, sizeof(addrinfo));
     SOCKADDR_IN *pSockAddr_in;

...

     // WinSock の初期化
     rc = WSAStartup(MAKEWORD(2, 2), &wsaData);

     if( rc ) {
          printf("WSAStartup Failed.\n");
          return 1;
     }

WSAStartup を呼び出す時に、第一引数にこれから使う WinSock のバージョンを指定します。 通常ためらわず最新バージョンの 2.2 を使うはずですから、ここでは 2.2 を指定しています。

ちなみに winsock2.h でも WINSOCK_VERSION という便利マクロが定義されてます。

#ifndef WINSOCK_VERSION
#define WINSOCK_VERSION MAKEWORD(2,2)
#endif

第二引数の WSADATA 構造体は次のような形をしています。

typedef struct WSAData {  
	WORD wVersion;  
	WORD wHighVersion;  
	char szDescription[WSADESCRIPTION_LEN+1];  
	char szSystemStatus[WSASYS_STATUS_LEN+1];  
	unsigned short iMaxSockets;  
	unsigned short iMaxUdpDg;  
	char FAR *lpVendorInfo;
} WSADATA,  *LPWSADATA;

一見ややこしいですがバージョン 2 以降ではあまり役に立つ情報を返してこないので、 気にしなくて構いません。

WSAStartup の確認ツール ~ StartupTest

「いや、気になる!」 という方は、私が以前使ったツールがあるので、それで試してみてください。


WSAStartup を呼び出し WSADATA の中身を表示する

StartupTest.zip のダウンロード

このツールは WSAStartup の第一引数のバージョンを変えて呼び出すと、 WSADATA にどのような値が返ってくるか見るだけのツールです。(私も暇ですね。こんなもの作って...(苦笑))

では次に行きましょう。

アドレス情報の取得

さていよいよ getaddrinfo 関数を使って、"www.microsoft.com" 等の名前を IP アドレスに解決します。

     addrinfo hints, *pAddrInfo = NULL;
     ZeroMemory(&hints, sizeof(addrinfo));
     SOCKADDR_IN *pSockAddr_in;
...
     // アドレス情報の取得
     hints.ai_flags    = AI_CANONNAME;
     hints.ai_family   = AF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_protocol = IPPROTO_TCP;

     rc= getaddrinfo( argv[1], "http", &hints, &pAddrInfo);

     if( rc ) {
          printf("getaddrinfo Failed. rc = %d \n", rc);
          return 1;
     }

getaddrinfo は次の形をしています。

int WSAAPI getaddrinfo(
  __in   const char *nodename,
  __in   const char *servname,
  __in   const struct addrinfo *hints,
  __out  struct addrinfo **res
);

第 1 引数に名前を渡し、第 2 引数にサービス名 (プロトコル名) またはポート番号を文字列で渡し、 第 3 引数に解決のヒントを渡して呼び出すと、第 4 引数にその結果が返ってくる、ということになります。

ぶっちゃけ通常、サービス名及びヒントの部分は両方 NULL でも問題ありません。ちゃんと名前解決されて返ってきます。 ここでわざわざヒントとして設定してみたパラメータの意味を詳しく知りたい方は MSDN をご覧ください。 またサービス名まで渡してあげると、結果のなかにポート番号まで入ってきて便利です。

結果の表示

結果は getaddrinfo の第 4 引数に返ってきます。型は addrinfo へのポインタです。 addrinfo は次のように定義されています。次のノードへのポインタを *ai_addr で保持する、単方向リストです。つまり言い換えると、第 4 引数に返ってくるのは addrinfo リストのリストヘッドが返ってきています。

typedef struct addrinfo {  
	int ai_flags;  
	int ai_family;  
	int ai_socktype;  
	int ai_protocol;  
	size_t ai_addrlen;  
	char *ai_canonname;  
	struct sockaddr *ai_addr;  
	struct addrinfo *ai_next;
} ADDRINFOA,  *PADDRINFOA;

リストですから、ループで結果を表示しています。次のノードのポインタが NULL になったところでループを抜けます。

     while (1) {

          printf ("------------------------------\n");
          printf ("Canon Name: %s\n", pAddrInfo->ai_canonname ? 
               pAddrInfo->ai_canonname : "");
          
          pSockAddr_in = (SOCKADDR_IN*) pAddrInfo->ai_addr;
          printf ("Port:       %d\n", ntohs(pSockAddr_in->sin_port));
          printf ("Addr:       %s\n", inet_ntoa(pSockAddr_in->sin_addr));
          
          if( !pAddrInfo->ai_next ) {
               break;
          }

          pAddrInfo = pAddrInfo->ai_next;

     }

     freeaddrinfo(pAddrInfo);

アドレス情報は ai_addr に返ってきます。これは IPv4 の環境では SOCKADDR_IN* にキャスト可能です。 SOCKADDR_IN が取れれば、アドレス情報はその中のメンバーである sin_addr を inet_ntoa に渡すことによって、IP アドレスの文字列表現を取得することが出来ます。

SOCKADDR_IN については別ページ WinSock での IP アドレスとポートの扱い方 で説明していますのでそちらも見てください。

結果が不要になったら freeaddrinfo に渡してメモリを解放します。

WinSock のクリーンアップ

WinSock2 を使ったら最後に WSACleanup を呼び出してクリーンアップします。

     // WinSock のクリーンアップ
     WSACleanup();

     return 0;
}

以上、はじめての WinSock2 の説明でした。慣れないデータタイプが出てきて、 最初は 「なんだこれ?」 と思うかもしれませんが、頑張ってみてください!

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説