WindowsでのIPv6プログラミング講座 第1回

WindowsでのIPv6プログラミング講座 第1回

タグ:
第1回 開発環境の導入とWin32 APIの基礎知識 (2005.10.24)

加藤淳也
NTT情報流通プラットフォーム研究所



はじめに

 マイクロソフトがリリースする最新のWindows OSには、すでにIPv6機能が搭載さています。Windows XP, Windows Server2003など、PCをターゲットとしたOSから、Windows CE .NETのように組み込みデバイスを対象としたOSまで広い対応を果たしています。Windows XPの後継OSであるWindows Vista(コードネームLonghorn)では標準でIPv6機能が有効化されて出荷されると言われており、今後、プログラム作成者はIPv6に対応したアプリケーションの開発を行う機会が増えると思われます。

 本シリーズでは6回にわたり、Windows系のOSをターゲットとしたIPv6アプリケーションの作成方法を解説します。特に、WindowsもしくはUNIXも含めて、IPv4でのアプリケーション開発者が、新たにWindows上でIPv6アプリケーションを開発するために必要となる知識に重点を置いて解説します。シリーズの前半ではUNIX系のOSと共通で利用可能なAPIの利用方法を解説します。参考文献にあげるドキュメントもあわせて参照してください。さらに、本シリーズの目的はWindows独自のネットワークプログラミングの作法について解説します。多くのWindowsアプリケーションでは独自に拡張されたWinSock APIを利用しています。たとえば非同期I/Oや多重I/O、IP Helper APIなど機構です。Windowsらしいプログラムの作成を行うため、これらの項目も扱います。

 第1回では、開発環境の整備方法を解説し、簡単なサンプルプログラムをコンパイルして動作させます。また、UNIXでの開発経験はあるものの、Windowsでのプログラミング経験が浅い方を対象として、WinSock/Win32 APIとバークレーソケット(UNIXシステム)との違いについて解説します。


ターゲット環境と開発ツールの整備

 本シリーズでは、Windows XP SP2上においてC/C++言語からWin32 API(WinSock 2)の利用を前提とします。WindowsではC#を初めとする.NET FrameworkやJavaなどからもIPv6プログラミングが可能ですが、本シリーズでは対象外とします。機会があれば別途取り上げたいと思います。

必要なソフトウェアの一覧とインストール
  1. Microsoft Windows XP SP2
  2. Microsoft Visual Studio .NET 2003
  3. Microsoft Platform SDK for Windows Server 2003 SP1

OSの準備

 1.についてWindows XP SP2をインストールし、IPv6機能を有効化しておいてください。

コンパイラの準備

 2.についてコンパイラは、Microsoft Visual Studio .NET 2003を使用します。お使いの環境にインストールしてください。Microsoft Visual Studio 6.0以降ならば利用可能のようですが、今後、本稿で提示するサンプルコードはMicrosoft Visual Studio .NET 2003でコンパイルして動作確認を行います。

Microsoft Visual C++ Toolkit 2003で構築するフリーの開発環境

 Microsoft Visual Studio .NET 2003は商用製品ですが、C/C++コンパイラの機能を切り出したものがマイクロソフトからフリー配布されています。Microsoft Visual C++ Toolkit 2003が下記のURLから入手可能です。

http://msdn.microsoft.com/visualc/vctoolkit2003/

 ただし提供されるツール群はコマンドラインベースのツールのみであり、GUIによる統合開発環境(IDE)は付属しません。またヘッダやライブラリも言語仕様に関連する最小限度のものの付属にとどまります。したがって、Microsoft Visual Studio .NET 2003と完全に同じ環境を構築できるわけではありません。本シリーズでは極力、Microsoft Visual C++ Toolkit2003でコンパイルと動作確認が行えるサンプルコードを提示しますが、中にはコンパイルできないものがあるかもしれません。


Platform SDKの入手とインストール

 次に、3. のMicrosoft Platform SDK for Windows Server 2003 SP1を入手してください。名称はWindows Server用となっていますが、Windows XPでの開発に必要なSDKも含まれています。Platform SDKも無償で配布されていますので、下記のURLからダウンロードしてください。

http://www.microsoft.com/downloads/
details.aspx?FamilyId=A55B6B43-
E24F-4EA3-A93E-40C0EC4F68E5&
displaylang=en


 ダウンロード後にインストーラを起動すると、導入するコンポーネントの選択画面になります。

Platform SDKの入手とインストール

 画面において「Microsoft Windows Core SDK」は必ず選択してください。本項目にWin32 API/WinSock関係のヘッダファイルやインポートライブラリ、またmakeコマンドやリソースコンパイラなどの基本ツール群が含まれています。

環境変数の確認

 コンパイラ本体を起動するためのコマンドサーチパス(PATH環境変数)。ヘッダファイル(INCLUDE環境変数)とインポートライブラリ(LIB環境変数)のサーチパスを確認します。Visual Studio .NET 2003とPlatform SDKのそれぞれのインストーラが設定を行いますが、念のためサーチパスの順番は確認してください。ポイントはPlatform SDKに関するフォルダが、Visual Studio .NET2003の環境よりも先にサーチされるような設定を行うことです。

 下記の例は下線(^^^^)部分がPlatform SDKの設定項目になります。それ以外のMicrosoft Visual Studio .NET 2003に関する設定はコンパイラのインストーラが設定した環境変数です。

  ●PATH=
    C:\Program Files\Microsoft Platform SDK\Bin;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    C:\Program Files\Microsoft Platform SDK\Bin\WinNT;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE;
    C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\BIN;
    C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools;
    C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\bin\prerelease;
    C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\bin;
    C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\bin;
    C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\v1.1.4322

  ●INCLUDE=
    C:\Program Files\Microsoft Platform SDK\include;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\ATLMFC\INCLUDE;
    C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\INCLUDE;
    C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\PlatformSDK\include\prerelease;
    C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\PlatformSDK\include;
    C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\include

  ●LIB=
    C:\Program Files\Microsoft Platform SDK\lib;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\ATLMFC\LIB;
    C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\LIB;
    C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\PlatformSDK\lib\prerelease;
    C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\PlatformSDK\lib;
    C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\lib
Microsoft Visual C++ Toolkit 2003を使用する場合の環境変数の設定です。

  ●PATH=
    C:¥Program Files¥Microsoft Platform SDK¥Bin;
    C:¥Program Files¥Microsoft Platform SDK¥Bin¥WinNT;
    C:¥Program Files¥Microsoft Visual C++ Toolkit 2003¥bin


  ●INCLUDE=
    C:¥Program Files¥Microsoft Platform SDK¥include;
    C:¥Program Files¥Microsoft Visual C++ Toolkit 2003¥include

  ●LIB=
    C:¥Program Files¥Microsoft Platform SDK¥lib;
    C:¥Program Files¥Microsoft Visual C++ Toolkit 2003¥lib

コンパイラの動作確認

 リスト1に掲載するコードをダウンロードして下さい。コンパイル方法はコマンドプロンプトを開き、図1に示したclコマンドを投入します。

 getv6addr.exeコマンドは引数に与えられたホスト名からIPv6アドレスを求めるプログラムです。コンパイルして生成されたバイナリにホスト名を与えて実行してください。図1の下部のの実行例は、ホスト "www.ipv6style.jp" のIPv6アドレスを取得して画面に表示したものです。

図1 getv6addr.cのコンパイルと実行例

C:\>cl getv6addr.c ws2_32.lib      ←コマンドラインからのコンパイル
Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 13.10.3077 for 80x86
Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.

getv6addr.c
Microsoft (R) Incremental Linker Version 7.10.3077
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:getv6addr.exe
getv6addr.obj
ws2_32.lib

C:\>getv6addr www.ipv6styel.jp     ← バイナリの実行
www.ipv6style.jp IPv6 address : 2001:218:2001:3000::56
リスト1 getv6addr.c
getv6addr.cの内容

#include ‹winsock2.h›
#include ‹ws2tcpip.h›
#include ‹stdio.h›

int main(int argc, char *argv[])
{
    char *nodename;
    WSADATA wsaData;
    ADDRINFO hints;
    LPADDRINFO ai, ai0;
    int e;

    /* parsing command line argument */
    if (argc != 2) {
        fprintf(stderr, "syntax: getv6addr HOSTNAME\n");
        exit(1);
    }
    nodename = argv[1];

    /* initilizing Windows Socket */
    WSAStartup(MAKEWORD(2, 2), &wsaData);

    /* resolving "www.ipv6style.jp" */
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET6;
    if (e = getaddrinfo(nodename, NULL, &hints, &ai0)) {
        fprintf(stderr, "%s: %s\n", nodename, gai_strerror(e));
        WSACleanup();
        exit(1);
    }

    /* print IPv6 address if resolv process is succeeded */
    for (ai = ai0; ai; ai = ai->ai_next) {
        char v6addrstr[NI_MAXHOST];
        getnameinfo(ai->ai_addr, ai->ai_addrlen,
            v6addrstr, sizeof(v6addrstr),  NULL, 0, NI_NUMERICHOST);

        printf("%s IPv6 address: %s\n", nodename, v6addrstr);
    }

    /* cleanup */
    freeaddrinfo(ai0);
    WSACleanup();
}

getv6addr.c についての解説

ヘッダファイルの違い

 IPv6プログラミングにおいてWinSock APIを利用するためには

 #include ‹winsock2.h›
 #include ‹ws2tcpip.h›
 上記の2つのヘッダを取り込みます。リスト1でも、winsock2.hとws2tcpip.hをインクルードしています。UNIX環境でバークレーソケットを使う場合は、ソースコードの冒頭で次のヘッダを取り込むのが一般的です。

 #include ‹sys/types.h›
 #include ‹sys/socket.h›
 #include ‹netdb.h›
 sys/types.h, sys/socket.h, netdb.h はWinSock APIには存在しないヘッダファイルなので、注意してください。

ライブラリのリンク

 図1に示した、リスト1のコンパイル時には、

   cl getv6addr.c ws2_32.lib
                  ^^^^^^^^^^ 
    "ws2_32.lib" というインポートライブラリをリンクしています。これはWinSock2に関連するAPIのエントリポイントを収めたライブラリで、WinSock2APIを利用する場合は必ずリンクします。※注1

getv6addr.cの大まかな流れ

getv6addr.cプログラムの大まかな流れを図2に示します。

図2 getv6addr.cの流れ
  [ プログラム開始 ]
          ↓
  [ コマンドラインの検査とホスト名の文字列を獲得 ]
          ↓
  [ WinSockのスタートアップ処理 ]
          ↓
  [ getaddrinfo() 関数によりホスト名からIPv6アドレスを取得(名前解決)] .... (1)
          ↓
  [ 名前解決が成功しているならば、
    getnameinfo() 関数によりIPv6アドレスを文字列表現に変換
    画面に表示 ] .... (2)
          ↓
  [ freeaddrinfo()により名前解決結果を開放 ] .... (3)
          ↓
  [ WinSockの終了処理 ]
          ↓
  [ プログラム終了 ]
 リスト1では、ブロック(1)において、getaddrinfo()関数はホスト名を受け取り、名前解決の結果得られたIPv6アドレスをADDRINFO構造体に格納します。もし名前解決の結果、複数のIPv6アドレスが得られたならばADDRINFOのリストの先頭(ai0)を返します。このIPv6アドレスを格納するADDRINFO構造体リストのバッファはgetaddrinfo()関数が生成してくれます。あらかじめプログラマが確保しておく必要はありませんが、不要になったらfreeaddrinfo()関数で開放する必要があります(ブロック(3))。
 ブロック(2)ではgetaddrinfo()が返却したADDRINFO構造体のリストを、for文によりひとつづつ取り出します。それぞれ、バイナリ表現のIPv6アドレス(binary network format)を文字列表現(presentation format)に変換しています。このときに使用しているAPIがgetnameinfo()関数です。文字列に変換された結果がv6addrに格納されます。getnameinfo()関数は本来、IPアドレスからホスト名を求める(逆引き)ことが主な役割ですが、バイナリ表現から文字列表現への変換も行うことができます。※注2

 getaddrinfo(), getnameinfo(), freeaddrinfo(), gai_strerror()の4つのAPIを使用しましたが、これらのAPIはサーバ・クライアントを問わず多くのプログラムで使用される重要なAPIです。RFC3493で使用方法が定義されており、個別のシステムによって制約や相違はあるのものの、ほとんどのIPv6対応OSで利用できるAPIです。今回はこれらの概略のみを説明しました。関連するデータ構造(ADDRINFO構造体)や引数の詳細な意味は次回に詳しく解説します。


WinSock(Windows)とバークレーソケット(UNIX)の違いについて

 WinSockはバークレーソケットを起源として派生したプログラミングAPIです。したがってAPIの利用方法についてはよく似ているため、UNIXでのネットワークプログラミングの経験がある開発者ならば容易に利用することができます。しかしWindowsの発展とともに独自のAPIが多数設置され、またコーディングスタイルについても独自の方言が発展していきました。以下、UNIXでの開発経験のある方のために、Win32 APIで多用される表現と、WinSock APIとバークレーソケットの表現の違いについて紹介します。

基本的な整数型について

 Windowsプログラミングで多用される整数型としてWORD, DWORD型があります(表1)。

表1
表: 32ビット環境でのWORD, DWORD型
typedefされる型 意 味
WORD
DWORD
unsigned short
unsigned int
符号なしの16ビット整数
符号なしの32ビット整数

マクロ
  MAKEWORD(a, b) = b * 16 + a

 リスト1にはMAKEWORD(2, 2)という行があります。これは、16ビットのWORD整数を生成するマクロで、WinSockバージョンを指定するときに非常によく用いられます。MAKEWORD(2, 2) = 0x0202となります。このマクロにより生成されるWORD値は、第1引数が下位バイト、第2引数が上位バイトになります。たとえばMAKEWORD(0x03,0x04)=4*16+3=67です。引数の順序には気を付けてください。

構造体とポインタ型の表現形式

 WinSockに限らず、Windowsプログラミングでは、構造体やそれへのポインタ型をすべて大文字で書く習慣があります。一例を表2に挙げると

表2
表: 構造体の型定義
typedefされる型
ADDRINFO
PADDRINFO
LPADDRINFO
struct addrinfo
struct addrinfo *
struct addrinfo FAR *

 SOCKADDR型はstruct sockaddr型としてtypedefされており、さらにそのポインタ型がPSOCKADDRとなっています。3番目にはポインタにFAR修飾子がついていますが、セグメントを越えることを示す、16ビットプログラミング時代の歴史的な表記です。32ビットプログラミングが当たり前になった現状では、PSOCKADDRとLPSOCKADDRは同じ意味です。

WinSockのバージョンと初期化方法について

WinSockのバージョンについて

 WinSock APIにはバージョンによって機能が大きく異なっています。バージョン1.1はTCP/IP(IPv4)のみの対応でWindows 95以前のOSに搭載されています。Windows NT/98以降では、OSに標準でWinSock 2が搭載されます。TCP/IP以外にも、複数のプロトコルに対応できるようにマルチプロトコル化されました。また、重複I/Oなどのアクセスモデルに対応しています。IPv6プログラミングではバージョン2.2を指定します。

WinSockの初期化方法

 リスト{getv6addr.c}において、WSADATA型の変数wsaData、及びその変数を参照するWSAStartup()とWSACleanup()関数が出現しています。これらはバークレーソケットにはないWindows独自の拡張です。WinSock APIではあらゆるソケットAPIを利用する前に、必ず初期化(WSAStartup())と終了処理(WSACleanup())が必要となります。

関数プロトタイプ

  int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
  int WSACleanup(void);
 WSAStartup()の第一引数にはプログラマが要求するWinSockのバージョンです。バージョン2.2を要求する場合は、MAKEWORD(2,2)(16進の整数値で0x0202)を指定します。WSAstartup()関数の戻り値は0の場合は成功(要求したバージョンのWinSockが利用可能)であり、失敗時は0でない値を返します。関数から復帰するとき、第2引数に渡したlpWSAData構造体に利用可能なWinSockのバージョンが書き込まれています。lpWSAData構造体の内容を調べることで、OSの持つWinSockのバージョンを知ることができます。

 リスト1では、プログラムの終了時にWSACleanup()関数を呼び出しています。これはWinSockの終了処理を行う関数です。


まとめ

 今回は開発環境の整備と簡単なサンプルプログラムのコンパイルを行いました。またUNIXプログラマを主な対象としてWin32 API/WinSockとバークレーソケットとの表記の違いも述べました。次回はTCPを利用したIPv6プログラムの作成について解説します。

参考文献

[1]萩野純一郎著,小川彩子訳,「IPv6ネットワークプログラミング」,2003年4月,アスキー
[2]大川誠,「WinSockアプリケーションのIPv6対応はどうやるか」,2003年7月,インプレスIPv6Style,http://www.ipv6style.jp/jp/apps/
20030711/index.html

[3]ルイス・ナッパー著,江村豊訳,「WinSock2プログラミング改定第2版」,ソフトバンクパブリシング,2005年1月

※注1
WinSockバージョン1.1ではインポートライブラリとしてwsock32.libを使用します。IPv6プログラミングではWinSock2の利用を前提としますので、ws2_32.libをリンクしてください。なおWinSockのバージョンについてはWinSockのバージョンについての節を参照してください。


※注2
アドレスの表現形式の変換には、UNIX(FreeBSD/Linuxなど)ではinet_ntop()/inet_pton()、WinSock APIではWSAAddressToString()/WSAStringToAddress()関数も利用可能です。

この記事のトラックバックURL

http://www.ipv6style.jp/trackback/205
Ads by Google

IPv6ブログ