(Translated by https://www.hiragana.jp/)
WinSock - Wikibooks コンテンツにスキップ

WinSock

出典しゅってん: フリー教科書きょうかしょ『ウィキブックス(Wikibooks)』
プログラミングWinSock

WinSockとは、プログラミングのさい、Windowsでインターネット通信つうしんをするためのAPIなどである。webブラウザを作成さくせいしたりしたい場合ばあい、このようなAPI(ソケット通信つうしんAPI)が必要ひつようである。もしたんにホームページをつくりたい場合ばあいには、Winsock は不要ふようである(ホームページのつくかたは、簡単かんたんなものなら HTML を使つかってつくれる)。

Windowsにかぎらず、UnixやLinuxなどでも、OSが提供ていきょうしている、ネット通信つうしんをするためのAPIぐんを「ソケット」(socket)といい、ソケットをもちいて通信つうしん実現じつげんすることを「ソケット通信つうしん」などという。

用語ようご注意ちゅうい

[編集へんしゅう]

この科目かもくでいっている「クライアント」がわのプログラムとは、たとえばwebブラウザそのもののようなプログラムである(HTMLファイルのことではないので、混同こんどうしないように)。

初心者しょしんしゃむけのページ

[編集へんしゅう]
リンクさきのページ内容ないよう
導入どうにゅう方法ほうほう (Visual Studio 関連かんれん)
通信つうしん全体ぜんたいぞう (bind とか)
コードの基本きほん骨格こっかく概要がいよう (#include <winsock2.h> とか)


とりあえずのコード

[編集へんしゅう]

とりあえず、うごくコード(MSDN Creating a Socket for the Client などにあるコードをつなげただけ)

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

#pragma comment(lib, "Ws2_32.lib")


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

    WSADATA wsaData;
    int iResult;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }



    struct addrinfo* result = NULL,
        * ptr = NULL,
        hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;



    #define DEFAULT_PORT "27015"

    // Resolve the server address and port
    iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }


    SOCKET ConnectSocket = INVALID_SOCKET;

    // Attempt to connect to the first address returned by
// the call to getaddrinfo
    ptr = result;

    // Create a SOCKET for connecting to server
    ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
        ptr->ai_protocol);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Error at socket(): %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }


    // Connect to server.
    iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        closesocket(ConnectSocket);
        ConnectSocket = INVALID_SOCKET;
    }

    // Should really try the next address returned by getaddrinfo
    // if the connect call failed
    // But for this simple example we just free the resources
    // returned by getaddrinfo and print an error message

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }



    return 0;
}


MSDN のページをもとに作成さくせいしたコードだが、しかしMSDNのサイトでは「int main(int argc, char** argv)」の引数ひきすう 「(int argc, char** argv) 」の部分ぶぶん説明せつめいけているのである。

「(int argc, char** argv) 」とは、C言語げんご界隈かいわい慣用かんようてきによくもちいられる引数ひきすうであり、「プログラムかり引数ひきすう」という。(くわしくは『C言語げんご/中級ちゅうきゅうしゃけの話題わだい#プログラムかり引数ひきすう関数かんすう』を参照さんしょう。)


さて、構造こうぞうたい部分ぶぶんは、

struct addrinfo* result = NULL,

以下いかりゃく) は、Windows仕様しようやあるいは規格きかくなにかでこうくとまっているらしく、 MSDNにもこう構造こうぞうたいいてあるので、

読者どくしゃはそのままれてもらうしかない。

MSDNコードれい解説かいせつ

[編集へんしゅう]

MSDNの完成かんせいがたプログラムの概要がいよう

[編集へんしゅう]

MSDNソースコードの予備よび

[編集へんしゅう]

上記じょうきのMSDNのコードの予備よびを、諸般しょはん事情じじょうのため、wikibooksでも保管ほかんする。

文法ぶんぽう解説かいせつ

[編集へんしゅう]

WSAStartup が成功せいこうした場合ばあい、0をかえ仕様しようである。

つまり、もしかえが 0 でなければ、エラーである。

なので、つづくifぶんで、がえが 0 かどうかをている。


getaddrinfo()

[編集へんしゅう]

IPv4 のころは gethostbyname が使つかわれていたが現在げんざいではあまりサポートされておらず、 2010ねん以降いこう現代げんだいではわりに getaddrinfo() を使つかうのが推奨すいしょうされています。 (上述じょうじゅつMSDNの完成かんせいがたコードれいでも gethostbyname は使つかわれておらず、getaddrinfo() だけが使つかわれています。)

getaddrinfo()を使つかうには winsock2 が必要ひつようですのでインクルードする必要ひつようがあります(つまりバージョン1ではダメ)。


残念ざんねんながら日本にっぽんのネットにあるwinsockについてのふる解説かいせつサイトでは gethostbyname でいまだに解説かいせつしているサイトもありますが、たんにそのサイトの作者さくしゃ更新こうしんをサボっているだけなので無視むししましょう。

getaddrinfo() は成功せいこうしたらがえとして 0 を返却へんきゃくします。


なお、 getaddrinfo() で確保かくほされたメモリは freeaddrinfo() で解放かいほうされます。


Linuxのソケットプログラミングにも同様どうよう関数かんすうがありますが、しかし仕様しよう微妙びみょうことなるので、読者どくしゃはwinsockプログラミングではwindowsようのサイトを参考さんこうにしましょう。

IPv4はん

[編集へんしゅう]

とりあえず、下記かきのようなコードで、サイトのドメインめいから、IPv4対応たいおうのIPアドレスをることが出来できます。(※いきなりIPv6対応たいおうのコードをくのはむずかしいので、まずIPv4対応たいおうのコードからこう。)

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

#pragma comment( lib, "ws2_32.lib" )


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

    // WinSockの初期しょきなど
    WSADATA data;
    WSAStartup(MAKEWORD(2, 2), &data);


    char hostname[20] = "www.yaahole.com"; // 架空かくう企業きぎょうめい
    struct addrinfo hints, * res;

    int err;

    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family = AF_INET;

    hints.ai_protocol = IPPROTO_TCP;


    err = getaddrinfo(hostname, NULL, &hints, &res);

    if (err != 0) {
        return -1;
    }

    struct sockaddr_in* temp = (struct sockaddr_in*)(res->ai_addr);

    struct in_addr nameaddr;
    nameaddr.s_addr = (temp)->sin_addr.s_addr;


    char destbuf_text[500];

    inet_ntop(AF_INET, &(nameaddr.s_addr), destbuf_text, 100);
    printf("IPアドレス %s \n", destbuf_text);


    freeaddrinfo(res);

    // WinSockの終了しゅうりょう
    WSACleanup();

}
実行じっこう結果けっか
[編集へんしゅう]

架空かくう企業きぎょうのIPアドレスですので、数字すうじはデタラメです。また、一部いちぶ伏字ふせじにしています。

IPアドレス 123.△〇△.987.〇〇×

のような結果けっかになる。

上記じょうきコードは2023ねんのwindows11でも問題もんだいなく動作どうさする。

※よくあるエラーの場合ばあい対処たいしょほう
  • 架空かくう企業きぎょうめいのアドレスを、実際じっさい企業きぎょうのアドレスにえること。
  • 上記じょうきのアドレスえのさい、まちがえて企業きぎょうめいにhttpまたはhttpsを間違まちがえてつけていないか確認かくにんすること。httpもhttpsもつかないのがただしい。


解説かいせつ
[編集へんしゅう]
ソケット指定していのバージョン指定していなど
   WSAStartup(MAKEWORD(2, 2), &data);

のMAKEWORDというのは、この場合ばあい、WinSockのバージョンが2.2であることをあらわしている。 あまり仕様しよう詳細しょうさいのハッキリしない関数かんすうであるが、MSDNでこの関数かんすう正確せいかくにはマクロ)が使つかわれているので、それにしたがおう。

なお、生成せいせいされるWinSockのバージョンは

   WSAStartup(MAKEWORD(メジャーバージョン, マイナーバージョン), &data);

書式しょしきである。

また、もし

   WSAStartup(MAKEWORD(2, 0), &data);

のようにバージョン2.0を指定していしても、実行じっこう生成せいせいされるWinSockのバージョンは自動的じどうてきに2.2のような実行じっこう最新さいしん安定あんていばんのバージョンになる。

あまり重要じゅうようではないので、くわしくは後述こうじゅつする。


廃止はいしされた関数かんすう

さて、かつて inet_ntoa という関数かんすうがあったが、これは2020年代ねんだいのVisual Studio では使つかえないので、 inet_ntopえる必要ひつようがある。


ソケット構造こうぞうたい

さて、それとは別件べっけんだが、

nameaddr.s_addr = ((struct sockaddr_in*)(res->ai_addr))->sin_addr.s_addr;

は、なにをやっているかというと、おおまかには、まず、getaddrinfo()の結果けっか構造こうぞうたい res (またはそのポインタ)にはいり、

そしてgetaddrinfo(hostname, NULL, &hints, &res); のだい4引数ひきすうでアドレスを指定していした構造こうぞうたい(&res)は、addrinfo構造こうぞうたいというものに格納かくのうされる。 そして、Windowsの場合ばあい、addrinfo構造こうぞうたいは、要素ようそとして

struct sockaddr *ai_addr;

要素ようそ仕様しようになっている。(一方いっぽう、BSDでは、OPアドレス情報じょうほうは sockaddr にふくまないらしい。)

この sockaddr *ai_addr は構造こうぞうたいのポインタなので、利用りようさいにはアロー演算えんざん->で指定していしなければいけない(なぜ構造こうぞうたいそのものではなく構造こうぞうたいのポインタなのかになるかもしれないが、規格きかくなどでそうまってしまっているようなので、したがうしかない。外国がいこくのサイトをても、同様どうようのコードである)。

本来ほんらいのBSDなどのsockaddr と sockaddr_in は、ちがいとして、あてさきIPアドレスの情報じょうほうふくんでいるかどうかのちがいがある。sockaddr は、その構造こうぞうたい変数へんすう内部ないぶ情報じょうほうには、IPアドレスの情報じょうほうふくまない。一方いっぽう、 sockaddr_in はIPアドレス情報じょうほうふくむ。なので、上記じょうきコードでは、最終さいしゅうてきにIPアドレス情報じょうほうふくむ sockaddr_in に変換へんかんしているわけである。

BSD本来ほんらいの sockaddr はIPアドレス情報じょうほうふくまないが、Windowsでは sockaddr がなんらかの方法ほうほうでIPアドレス情報じょうほうふくんでおり、ともかくWindowsでは sockaddr_in に変換へんかんできるように実装じっそうをされている。


IPアドレス情報じょうほうをテキスト文字もじできる関数かんすう inet_ntop が、sockaddr_in だけに対応たいおうしているので、これに変換へんかんしなけれんばならない。

 inet_ntop(AF_INET6,  &nameaddr.s_addr, destbuf_text, 100 );

とあるが、

 inet_ntop(アドレスファミリ,  & sockaddr_in 構造こうぞうたい変数へんすう, たんなるものよう変数へんすう, みのはば );

のような書式しょしきである。


なお、むりやり sockaddr 構造こうぞうたい情報じょうほう引数ひきすうれてもコンパイルできてしまうが、まったくちがうIPアドレスが表示ひょうじされてしまう。

 inet_ntop(AF_INET6,  (res->ai_addr), destbuf_text, 100 );

および

 inet_ntop(AF_INET6,  (res->ai_addr), destbuf_text, 100 );

は、まったく見当けんとうちがいのアドレスを表示ひょうじするだけである。


なので、まず、 nameaddr.s_addr = ((struct sockaddr_in*)(res->ai_addr))->sin_addr.s_addr; は、addrinfo構造こうぞうたいなかから、IPアドレス関連かんれん要素ようそすために、

(res->ai_addr)

という項目こうもくがある。

そして、

(struct sockaddr_in*)(res->ai_addr)

によって、addrinfo構造こうぞうたいのメンバとしての sockaddr 構造こうぞうたい情報じょうほうを、独立どくりつした sockaddr_in 構造こうぞうたいのポインタとなるように転写てんしゃしている。

さて、 sockaddr_in 構造こうぞうたいは、メンバ変数へんすうとして「sin_addr.s_addr」というのをつが、

(struct sockaddr_in*)(res->ai_addr)は、 sockaddr_in 構造こうぞうたいではなく、 sockaddr_in 構造こうぞうたいのポインタ なので、なので、「sin_addr.s_addr」にアクセスしたい場合ばあいには、アロー演算えんざんでアクセスしなければならない。


備考びこう
[編集へんしゅう]
MAKEWORDについて

なお、もし、冒頭ぼうとうの WSAStartup のコードをえてしまって、

   WSAStartup(0x0002, &data);

えてもコンパイルできてしまい、実行じっこう表示ひょうじされるIPアドレスも相手あいてさきのアドレスであるが(※ 2020ねんにVisual Studio 2019で確認かくにん)、しかしMSDNでは推奨すいしょうしていない書式しょしきなので、MAKEWORDでいたほうが安全あんぜんだろう。

なお、「0x」とは「16進数しんすうで」という意味いみ

WORDは、「16進数しんすうで4ケタ」のという意味いみ

なお、16進数しんすうの1ケタは、4ビットである。(2×2×2×2=16なので。)

なので、1 WORD は、4×4=16より、1 WORD は16ビットちょうである。


そして、1バイトは8ビットなので、つまりWORDとは2バイトである。

8ビットで0から256の数値すうちあつかえるので、2バイトだと0から65536の数値すうちあつかえる。つまり、WORDは0から65536の数値すうちあつかえる。


MAKEWORDマクロ でつくられるビット列びっとれつも、1 WORDである。なので、16進数しんすうで4ケタを指定していしてもいいのである。


なお、DWORD とは「16進数しんすうで8ケタ」である。つまりDWORDなら合計ごうけい4バイトである。DWORDは4バイトなので 0から 4,294 967,296 の数値すうちあつかえる。


ちなみに、0xでWSAStartupを直接ちょくせつ指定していした場合ばあい

   WSAStartup(0x うえ2ケタがマイナーバージョン 2ケタがメジャーバージョン, &data);

書式しょしきである。MAKEWORDで生成せいせいした場合ばあいとは、マイナーバージョンとメジャーバージョンの順序じゅんじょぎゃくなので、をつけよう。

なので、

   WSAStartup(0x0202, &data);

でもコンパイルがとおるし、表示ひょうじされるIPアドレスも正常せいじょうである。

一方いっぽう

   WSAStartup(0x0200, &data);

では実行じっこうしても、IPアドレスが表示ひょうじされない。


ちなみに、

    printf("%s\n", data.szDescription); // WinSock 2.0

    printf("バージョン : %d.%d \n" ,
        (BYTE)data.wHighVersion, (BYTE)(data.wHighVersion >> 8)
    );    // バージョン : 2.2

というコードをWinSockの生成せいせい以降いこうからクローズまえ任意にんい場所ばしょすことで、バージョンを表示ひょうじすことができる。 >> というのはシフト演算えんざん上記じょうきコードれい場合ばあいなら、みぎに8ビットのシフトをするという意味いみ

上記じょうきコードちゅうの「data」とは、

WSAStartup(MAKEWORD(2, 2), &data);

だい2引数ひきすうの&dataに由来ゆらいしている。なので、もし引数ひきすうめいべつ名前なまえ(たとえば wsadata)で、ソケット生成せいせいのコードも

WSAStartup(MAKEWORD(2, 2), &wsadata);

なら、

   printf("%s\n", wsadata.szDescription); // WinSock 2.0

のようにコードがわる。

追加ついかコードの表示ひょうじ結果けっかは、

WinSock 2.0
バージョン : 2.2

のように表示ひょうじされる。バージョン2.2であっても、「WinSock 2.0」と表示ひょうじされる。

IPv6対応たいおうのgetaddrinfo()

[編集へんしゅう]

下記かきコードでgetaddrinfoでIPv6でDNSサーバと通信つうしんできる。

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

#include <windows.h>
#include <stdlib.h>

#include <locale.h>

#define _WINSOCK_DEPRECATED_NO_WARNINGS // ふる関数かんすうをいくつか使つかっているので。ふるいのをえできるなら不要ふよう

#define _CRT_SECURE_NO_WARNINGS


#pragma comment( lib, "ws2_32.lib" )


// Winsockようパラメータ // ないとコンパイルにエラーになる。
// なぜだか、はやめに宣言せんげんする必要ひつようがある。
int status;
int numsnt;


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

    // WinSockの初期しょきなど
    WSADATA data;
    WSAStartup(MAKEWORD(2, 0), &data);

    char hostname[20] = "www.yaahule.com"; // 架空かくう企業きぎょうめい
    struct addrinfo hints, * res;
    int err;

    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family = AF_INET6;

    hints.ai_protocol = IPPROTO_TCP;


    err = getaddrinfo(hostname, NULL, &hints, &res);

    if (err != 0) {
        return -1;
    }

    // いっぺんに↓こういても、コンパイルできない。いろいろアレンジしてもダメ。
    // &nameaddr->sin6_addr = ((struct sockaddr_in6*)(res->ai_addr))->sin6_addr.s6_addr;

    struct sockaddr_in6* nameaddr; // in6_addr がたではなく sockaddr_in6 にえること

    nameaddr = (struct sockaddr_in6*)res->ai_addr;

    char destbuf_text[500];

    inet_ntop(AF_INET6, &(nameaddr->sin6_addr), destbuf_text, 200);
    printf("IP v6 アドレス - %s\n", destbuf_text);


    freeaddrinfo(res);

    // WinSockの終了しゅうりょう
    WSACleanup();
}

socket() 関数かんすう

[編集へんしゅう]

socket() 関数かんすう返却へんきゃくは、intではなく、SOCKETがたという専用せんようかたである必要ひつようがあります。

そのため、事前じぜんに SOCKETがた変数へんすう ConnectSocket を宣言せんげんしています。


またsocket() 関数かんすう失敗しっぱいすると INVALID_SOCKET という返却へんきゃくかえします。

socket()関数かんすうは3つの引数ひきすうります。


これら3つの引数ひきすうわせで、通信つうしん方法ほうほう指定していしています。


だい1引数ひきすうはアドレスファミリというものの指定していです。


ソケット構造こうぞうたい

[編集へんしゅう]

基本きほん

[編集へんしゅう]

MSDNのコードちゅう

struct addrinfo *result

とありますが、

これはすでにwindowsによって定義ていぎされている addrinfo 構造こうぞうたいについてのポインタ変数へんすう result を作成さくせいする宣言せんげんです(Unixでも、構造こうぞうたい名前なまえちがいますが、ソケット通信つうしん専用せんよう構造こうぞうたい最初さいしょから用意よういされており、その構造こうぞうたい変数へんすうえることで通信つうしんする方法ほうほうです)。

さて、Windowsの上記じょうき宣言せんげんでは、 addrinfo は作成さくせいされません(なぜなら、すでにaddrinfo構造こうぞうたい作成さくせいされているので)。


ptr->ai_family について

ソケット作成さくせいのコードで

   ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
       ptr->ai_protocol);

とあります。 「->」はアロー演算えんざんというもので、構造こうぞうたいのポインタ の メンバ にアクセスするときに使つかいます。

ポインタ ptr がてきて厄介やっかいですが、

みとくと、ptr はたんなる構造こうぞうたい addrinf のポインタ変数へんすうです。

そしてWindowsのすで用意よういしている addrinfo 構造こうぞうたいには、すでに ai_family メンバがありますので 『ADDRINFOA (ws2def.h) - Win32 apps | Microsoft Docs』『ADDRINFOA structure』

よって、 ptr->ai_family により、addrinfo構造こうぞうたいの ai_family メンバにアクセスできるのです。

hints 関連かんれん項目こうもくにある AF_INET というのはIPv4のアドレスファミリですが、なぜかIPv6時代じだい現代げんだいでも流用りゅうようされており、これで色々いろいろ通用つうようします。

さて WindowsにかぎらずLinuxなどでも一般いっぱんに hints では、通信つうしん方法ほうほう指定していする情報じょうほうあつかわれている仕様しようである。

しかしWinSockのこのコードの hints はたんにaddrinfo から流用りゅうようした構造こうぞうたい変数へんすうである。 実際じっさい、MSDN の ADDRINFOA structure をると、ページちゅうほどにある ai_family の項目こうもく一覧いちらんなかに「 AF_INET 」という項目こうもくがあります。

※ hintsは結局けっきょくは addrinfo 構造こうぞうたい流用りゅうようなので、極端きょくたんはなし、もしかしたら hints変数へんすう作成さくせいしなくてもソケット通信つうしんできる可能かのうせいもあるが、しかし互換ごかんせいなどのため、hints 変数へんすうのこすほうがのぞましいだろう。

ZeroMemory() というのは指定していした変数へんすうのメモリにゼロ 0 を関数かんすうがたAPIマクロ[1]

ZeroMemory() のだい1引数ひきすうは、0をみたいインスタンスへのポインタ。

だい2引数ひきすうは 0 を文字もじながさ。

外部がいぶ通信つうしん方法ほうほう

[編集へんしゅう]

外部がいぶ通信つうしん原理げんり

[編集へんしゅう]

HTTP通信つうしん

[編集へんしゅう]

telnet(テルネット) でのHTTP通信つうしんについても、上記じょうきリンクさきにある。

Windowsでは、じつはWinSockを使つかわずとも、HTTPやFTPなど、既存きそん有名ゆうめい通信つうしんプロトコルに対応たいおうした、簡略かんりゃくてき通信つうしんできる関数かんすうった関数かんすうグループがWindowsにはすで用意よういされており、たとえばWinInetとわれる関数かんすうグループがそのような目的もくてきのために用意よういされている。


分類ぶんるいじょうは、WinInet技術ぎじゅつは WinSock とはべつ分類ぶんるいではあるが、ほんwikiでは説明せつめいのレベルの都合つごうじょう、WinSockの科目かもくないでWinInet技術ぎじゅつについても説明せつめいすることにする。

下記かきに、WinInetをもちいたHTTP通信つうしんのコードれいしめす。

下記かきコードを実験じっけんするには、事前じぜんに XAMPP や Apache などの webサーバ をげること。なおwindowsの場合ばあい、apacheを直接ちょくせつインストールするのはむずかしいので、xamppをインストールするのが入門にゅうもんてきである(なお『PHP/確実かくじつ動作どうささせるまで』にXAMPPなどの設定せってい解説かいせつあり)。

実験じっけんようのHTMLファイルには、detarame.html というHTMLファイルに、「seikou」(成功せいこう意味いみ)という文字もじれついてあるとする。

コードれい
#include <stdio.h>
#include <windows.h>
#include <wininet.h> // 当然とうぜんだが、wininet.h をインクルードしないと、wininet技術ぎじゅつ使つかえない

#pragma comment(lib,"wininet.lib")

int main() {
	// ハンドルの作成さくせい
	HINTERNET hInet;
	hInet = InternetOpen(TEXT("sample"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);

	// URLのオープン
	HINTERNET hFile;
	hFile = InternetOpenUrl(hInet,
		TEXT("http://localhost/detarame.html"), // ここにアクセスしたいページめいのURLなど
		NULL, 0, INTERNET_FLAG_RELOAD, 0);

	// ファイルのみの事前じぜん処理しょり
	DWORD readSizeMax = 1000; // りの上限じょうげんサイズ。仕様しようにより、DWORDとまっている。
	char* outBuf; // 受信じゅしんぶつ格納かくのう仕様しようにより、ポインタでなければならない

	outBuf = (char*)GlobalAlloc(GPTR, 1000); // メモリの動的どうてき確保かくほ。これしないと文字もじけ「seikouフフフフフフ」以下いかりゃく発生はっせい

	DWORD* realRead; // 実際じっさいまれるサイズの格納かくのうよう

	// みのループ
	while (1) {
		bool rVal = InternetReadFile(hFile, outBuf, readSizeMax, (DWORD*)&realRead);

		if (rVal == true) {
			break;
		}
	}

	printf("%s\n", outBuf);
	GlobalFree(outBuf); // さきほど動的どうてき確保かくほしたメモリの解放かいほう


	// 終了しゅうりょう処理しょり
	InternetCloseHandle(hFile);
	InternetCloseHandle(hInet);

	return 0;
}


実行じっこう結果けっか

http://localhost/detarame.html のページ内容ないようおな内容ないようが、そのまま表示ひょうじされる。

たとえば、webブラウザで上記じょうきページにアクセスした場合ばあいに「seikou」と表示ひょうじされるなら、上記じょうきC++コードを実行じっこうしたさいには、コマンドプロンプトで「seikou」と表示ひょうじされる。

seikou

のように表示ひょうじされる。

※ 2023ねんのwindows11でも問題もんだいなく上記じょうきコードは動作どうさする。
技術ぎじゅつ解説かいせつ

WinInetのHTTP通信つうしん場合ばあい、ポート番号ばんごうなどの指定してい必要ひつようがない。

指定していするのは、ほぼURLのみで、関数かんすう InternetOpenURL でアクセスさき URL を指定していする。「URLのどこからどこがドメインめいか?」とか、ユーザーは意識いしき必要ひつようがない。


そのInternetOpen などが用意よういされており、わりとC言語げんごのファイル入出力にゅうしゅつりょく雰囲気ふんいきちかいようなユーザーフレンドリーなかんじで操作そうさできるように、マイクロソフトが仕様しようつくっている。

さきInternetOpen してからでないと、関数かんすう InternetOpenURL使つかえないハズである。


さて、C言語げんごのファイル入出力にゅうしゅつりょくでは、ファイルポインタというものをかいして、ファイルのOpenやcloseなどの処理しょりをする。

Win32APIではファイルハンドルというものが用意よういされており、ファイルポインタとたような使つかいかたをする。


InternetOpen内容ないようを、ファイルハンドルに保管ほかんする。

通信つうしんわったら、使用しようしたファイルハンドルを片付かたづけるため、関数かんすう InternetCloseHandleでクローズする。


GlobalAllocは、Windows特有とくゆうの、メモリの動的どうてき確保かくほ関数かんすう標準ひょうじゅんC言語げんごでいうmallocのようなメモリ確保かくほ関数かんすうである。

ほんWinInet通信つうしんでは実装じっそう都合つごうなどで、もしコレを使つかわずに直接ちょくせつにcharがた配列はいれつ受信じゅしんした文字もじ格納かくのうしようとしても、文字もじけで

seikouフフフフフフフフフフフフフフフ

以下いかりゃくのように表示ひょうじされてしまう。


メモリ確保かくほしたら、本来ほんらいなら、使用しようわったらGlobalFree()でメモリ解放かいほうしないといけない。なので、上記じょうきコードでは、メモリ解放かいほうしてある。

なお、mallocも、free()関数かんすうでメモリ解放かいほうするのが、行儀ぎょうぎよい、とされる。なぜなら、mallocなどのメモリ確保かくほ関数かんすう使つか場合ばあい使用しようにはメモリ解放かいほうをしないと、バグとして「メモリリーク」というメモリ圧迫あっぱくのバグの原因げんいんになりやすいからである。


メモリ確保かくほしたら、本来ほんらいなら、使用しようわったらGlobalFree()でメモリ解放かいほうしないといけない。なので、上記じょうきコードでは、メモリ解放かいほうしてある。 じつは、この程度ていどながさのコードの場合ばあい、Windowsがかして、コード終了しゅうりょうにメモリを解放かいほうしてくれるのでGlobalFree()省略しょうりゃく可能かのうである。だが今後こんごべつながいコードにコピーする場合ばあいなど将来しょうらいてき学習がくしゅうかんがえ、上記じょうきコードではメモリを行儀ぎょうぎよく解放かいほうしてある。


なお、もしたん GlobalAlloc(GPTR, 1000); 部分ぶぶんをそのまま malloc(1000); えても、Windowsの実装じっそう都合つごうなのか、文字もじけ「seikouヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ」がてしまう。

malloc だと、GlobalAlloc引数ひきすうの「GPRT」などのフラグ設定せってい出来できない。


その

マイクロソフトは、WinInetについてはサンプルコードのひとつも公式こうしきMSDNに紹介しょうかいしていない(2020ねん7がつ確認かくにん)。せっかくユーザフレンドリーなWinInetがあるのに、サービスしん中途半端ちゅうとはんぱである。



そののコードれい

[編集へんしゅう]

InternetReadFile の仕様しようとして、ファイルをわると、 InternetReadFile(hFile, outBuf, readSizeMax, (DWORD*)&realRead); 最後さいご引数ひきすう realRead に 0 が代入だいにゅうされる。

なので、この仕様しよう使つかうことで、HTTP通信つうしんにおける受信じゅしんのwhileループを下記かきのようにえることもできる。


コードれい
#include <stdio.h>
#include <windows.h>
#include <wininet.h> // 当然とうぜんだが、wininet.h をインクルードしないと、wininet技術ぎじゅつ使つかえない

#pragma comment(lib,"wininet.lib")

int main() {
	// ハンドルの作成さくせい
	HINTERNET hInet;
	hInet = InternetOpen(TEXT("sample"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);

	// URLのオープン
	HINTERNET hFile;
	hFile = InternetOpenUrl(hInet,
		TEXT("http://localhost/detarame.html"), // ここにアクセスしたいページめいのURLなど
		NULL, 0, INTERNET_FLAG_RELOAD, 0);

	// ファイルのみの事前じぜん処理しょり
	DWORD readSizeMax = 1000; // りの上限じょうげんサイズ。仕様しようにより、DWORDとまっている。
	char* outBuf; // 受信じゅしんぶつ格納かくのう仕様しようにより、ポインタでなければならない

	outBuf = (char*)GlobalAlloc(GPTR, 1000); // メモリの動的どうてき確保かくほ。これしないと文字もじけ「seikouフフフフフフ」以下いかりゃく発生はっせい

	DWORD* realRead; // 実際じっさいまれるサイズの格納かくのうよう

	// みのループ
	while (1) {
		bool rVal = InternetReadFile(hFile, outBuf, readSizeMax, (DWORD*)&realRead);

		if ( (int)realRead <= 0) {
			break;
		}
	}
    
	printf("%s\n", outBuf);
	GlobalFree(outBuf); // さきほど動的どうてき確保かくほしたメモリの解放かいほう

	// 終了しゅうりょう処理しょり
	InternetCloseHandle(hFile);
	InternetCloseHandle(hInet);

	return 0;
}

※ windows11でためしたところ、realRead を (int) で変換へんかんしないといけなかった。変換へんかんしないとビルドエラーになった。

関連かんれんページ

[編集へんしゅう]

外部がいぶリンクしゅう

[編集へんしゅう]

ソケットプログラミングについての出版しゅっぱんじょうきょうは、まるで Visual C++ の Windows APIプログラミングの書店しょてんでの不足ふそくぶりと類似るいじ状況じょうきょうであり、あまり市販しはん入門にゅうもんしょではソケットプログラミングが解説かいせつされていない状態じょうたいである。

そのため当面とうめんはネットの解説かいせつサイトを参考さんこうにするしかない。

下記かきのサイトに、充実じゅうじつしたWinSockの解説かいせつがある。

参考さんこう文献ぶんけん

[編集へんしゅう]
  1. ^ 2021ねん8がつ現在げんざい、ZeroMemoryはObsolateで、SecureZeroMemory の使用しよう推奨すいしょうされています(最適さいてき揮発きはつしません)。 ⇒ https://docs.microsoft.com/ja-jp/previous-versions/windows/desktop/legacy/aa366877(v=vs.85)