WinSock
WinSockとは、プログラミングの際、Windowsでインターネット通信をするためのAPIなどである。webブラウザを作成したりしたい場合、このようなAPI(ソケット通信API)が必要である。もし単にホームページを作りたい場合には、Winsock は不要である(ホームページの作り方は、簡単なものなら HTML を使って作れる)。
Windowsに限らず、UnixやLinuxなどでも、OSが提供している、ネット通信をするためのAPI群を「ソケット」(socket)といい、ソケットを用いて通信を実現することを「ソケット通信」などという。
用語の注意

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

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

MSDN のページをもとに作成したコードだが、しかしMSDNのサイトでは「int main(int argc, char** argv)」の引数 「(int argc, char** argv) 」の部分の説明が抜けているのである。
とりあえずのコード

「(int argc, char** argv) 」とは、C言語界隈で慣用的によく用いられる引数であり、「プログラム仮引数」という。(詳しくは『C言語/中級者向けの話題#プログラム仮引数を持つ関数』を参照。)
とりあえずのコード

さて、構造体の部分は、
とりあえずのコード

(以下略) は、Windows仕様やあるいは規格か何かでこう書くと決まっているらしく、 MSDNにもこう構造体が書いてあるので、
とりあえずのコード

読者はそのまま受け入れてもらうしかない。
MSDNコード例の解説

上記のMSDNのコードの予備を、諸般の事情のため、wikibooksでも保管する。
文法の解説

WSAStartup が成功した場合、0を返す仕様である。
文法の解説

つまり、もし返り値が 0 でなければ、エラーである。
文法の解説

なので、続くif文で、返り値が 0 かどうかを見ている。
文法の解説

IPv4 のころは gethostbyname が使われていたが現在ではあまりサポートされておらず、 2010年以降の現代では代わりに getaddrinfo() を使うのが推奨されています。 (上述MSDNの完成形コード例でも gethostbyname は使われておらず、getaddrinfo() だけが使われています。)
文法の解説

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

残念ながら日本のネットにあるwinsockについての古い解説サイトでは gethostbyname でいまだに解説しているサイトもありますが、単にそのサイトの作者が更新をサボっているだけなので無視しましょう。
文法の解説

getaddrinfo() は成功したら返り値として 0 を返却します。
文法の解説

なお、 getaddrinfo() で確保されたメモリは freeaddrinfo() で解放されます。
文法の解説

Linuxのソケットプログラミングにも同様の関数がありますが、しかし仕様が微妙に異なるので、読者はwinsockプログラミングではwindows用のサイトを参考にしましょう。
文法の解説

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

※ 架空の企業のIPアドレスですので、数字はデタラメです。また、一部、伏字にしています。
文法の解説

のような結果になる。
文法の解説

※ 上記コードは2023年のwindows11でも問題なく動作する。
文法の解説

のMAKEWORDというのは、この場合、WinSockのバージョンが2.2であることを表している。 あまり仕様の詳細のハッキリしない関数であるが、MSDNでこの関数(正確にはマクロ)が使われているので、それに従おう。
文法の解説

なお、生成されるWinSockのバージョンは
文法の解説

の書式である。
文法の解説

また、もし
文法の解説

のようにバージョン2.0を指定しても、実行時に生成されるWinSockのバージョンは自動的に2.2のような実行時の最新安定版のバージョンになる。
文法の解説

あまり重要ではないので、詳しくは後述する。
文法の解説

さて、かつて inet_ntoa という関数があったが、これは2020年代のVisual Studio では使えないので、 inet_ntop に置き換える必要がある。
文法の解説

さて、それとは別件だが、
文法の解説

は、何をやっているかというと、大まかには、まず、getaddrinfo()の結果が構造体 res (またはそのポインタ)に入り、
文法の解説

そしてgetaddrinfo(hostname, NULL, &hints, &res); の第4引数でアドレスを指定した構造体(&res)は、addrinfo構造体というものに格納される。 そして、Windowsの場合、addrinfo構造体は、要素として
文法の解説

と言う要素を持つ仕様になっている。(一方、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 だけに対応しているので、これに変換しなけれんばならない。
文法の解説

とあるが、
文法の解説

のような書式である。
文法の解説

なお、むりやり sockaddr 構造体の情報を引数に入れてもコンパイルできてしまうが、まったく違うIPアドレスが表示されてしまう。
文法の解説

および
文法の解説

は、まったく見当違いのアドレスを表示するだけである。
文法の解説

なので、まず、 nameaddr.s_addr = ((struct sockaddr_in*)(res->ai_addr))->sin_addr.s_addr; は、addrinfo構造体の中から、IPアドレス関連の要素を抜き出すために、
文法の解説

という項目がある。
文法の解説

そして、
文法の解説

によって、addrinfo構造体のメンバとしての sockaddr 構造体の情報を、独立した sockaddr_in 構造体のポインタとなるように転写している。
文法の解説

さて、 sockaddr_in 構造体は、メンバ変数として「sin_addr.s_addr」というのを持つが、
文法の解説

(struct sockaddr_in*)(res->ai_addr)は、 sockaddr_in 構造体ではなく、 sockaddr_in 構造体のポインタ なので、なので、「sin_addr.s_addr」にアクセスしたい場合には、アロー演算子でアクセスしなければならない。
文法の解説

なお、もし、冒頭の WSAStartup のコードを変えてしまって、
文法の解説

と変えてもコンパイルできてしまい、実行時に表示される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を直接指定した場合、
文法の解説

の書式である。MAKEWORDで生成した場合とは、マイナーバージョンとメジャーバージョンの順序が逆なので、気をつけよう。
文法の解説

なので、
文法の解説

でもコンパイルが通るし、表示されるIPアドレスも正常である。
文法の解説

一方、
文法の解説

では実行しても、IPアドレスが表示されない。
文法の解説

ちなみに、
文法の解説

というコードをWinSockの生成以降からクローズ前の任意の場所に付け足すことで、バージョンを表示すことができる。 >> というのはシフト演算。上記コード例の場合なら、右に8ビットのシフトをするという意味。
文法の解説

上記コード中の「data」とは、
文法の解説

の第2引数の&dataに由来している。なので、もし引数名が別の名前(たとえば wsadata)で、ソケット生成時のコードも
文法の解説

なら、
文法の解説

のようにコードが変わる。
文法の解説

追加コードの表示結果は、
文法の解説

のように表示される。バージョン2.2であっても、「WinSock 2.0」と表示される。
文法の解説

下記コードでgetaddrinfoでIPv6でDNSサーバと通信できる。
文法の解説

socket() 関数の返却値は、intではなく、SOCKET型という専用の型である必要があります。
文法の解説

そのため、事前に SOCKET型の変数 ConnectSocket を宣言しています。
文法の解説

またsocket() 関数は失敗すると INVALID_SOCKET という返却値を返します。
文法の解説

socket()関数は3つの引数を取ります。
文法の解説

これら3つの引数の組み合わせで、通信方法を指定しています。
文法の解説

第1引数はアドレスファミリというものの指定です。
文法の解説

MSDNのコード中に
文法の解説

とありますが、
文法の解説

これは既にwindowsによって定義されている addrinfo 構造体についてのポインタ変数 result を作成する宣言です(Unixでも、構造体の名前は違いますが、ソケット通信の専用の構造体が最初から用意されており、その構造体の変数を書き換えることで通信する方法です)。
文法の解説

さて、Windowsの上記の宣言では、 addrinfo は作成されません(なぜなら、すでにaddrinfo構造体は作成されているので)。
文法の解説

ソケット作成のコードで
文法の解説

とあります。 「->」はアロー演算子というもので、構造体のポインタ の メンバ にアクセスするときに使います。
文法の解説

ポインタ 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 」という項目があります。
文法の解説

ZeroMemory() というのは指定した変数のメモリにゼロ 0 を書き込む関数型APIマクロ。
文法の解説

ZeroMemory() の第1引数は、0を書き込みたいインスタンスへのポインタ。
文法の解説

第2引数は 0 を書き込む文字の長さ。
外部通信の方法

telnet(テルネット) でのHTTP通信についても、上記リンク先にある。
WinInet

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

下記に、WinInetを用いたHTTP通信のコード例を示す。
WinInet

下記コードを実験するには、事前に XAMPP や Apache などの webサーバ を立ち上げること。なおwindowsの場合、apacheを直接インストールするのは難しいので、xamppをインストールするのが入門的である(なお『PHP/確実に動作させるまで』にXAMPPなどの設定の解説あり)。
WinInet

実験用のHTMLファイルには、detarame.html というHTMLファイルに、「seikou」(成功の意味)という文字列が書いてあるとする。
WinInet

※ http://localhost/detarame.html のページ内容と同じ内容が、そのまま表示される。
WinInet

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

のように表示される。
WinInet

WinInetのHTTP通信の場合、ポート番号などの指定の必要がない。
WinInet

指定するのは、ほぼURLのみで、関数 InternetOpenURL でアクセス先 URL を指定する。「URLのどこからどこがドメイン名か?」とか、ユーザーは意識の必要がない。
WinInet

その他、InternetOpen などが用意されており、割とC言語のファイル入出力の雰囲気に近いようなユーザーフレンドリーな感じで操作できるように、マイクロソフトが仕様を作っている。
WinInet

先にInternetOpen してからでないと、関数 InternetOpenURL を使えないハズである。
WinInet

さて、C言語のファイル入出力では、ファイルポインタというものを介して、ファイルのOpenやcloseなどの処理をする。
WinInet

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

InternetOpenの内容を、ファイルハンドルに保管する。
WinInet

通信が終わったら、使用したファイルハンドルを片付けるため、関数 InternetCloseHandleでクローズする。
WinInet

GlobalAllocは、Windows特有の、メモリの動的確保の関数。標準C言語でいうmallocのようなメモリ確保の関数である。
WinInet

本WinInet通信では実装の都合などで、もしコレを使わずに直接にchar型の配列に受信した文字を格納しようとしても、文字化けで
WinInet

以下略のように表示されてしまう。
WinInet

メモリ確保したら、本来なら、使用が終わったらGlobalFree()でメモリ解放しないといけない。なので、上記コードでは、メモリ解放してある。
WinInet

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

メモリ確保したら、本来なら、使用が終わったらGlobalFree()でメモリ解放しないといけない。なので、上記コードでは、メモリ解放してある。 実は、この程度の長さのコードの場合、Windowsが気を利かして、コード終了時にメモリを解放してくれるのでGlobalFree()の省略も可能である。だが今後、別の長いコードにコピーする場合など将来的な学習も考え、上記コードではメモリを行儀よく解放してある。
WinInet

なお、もし単に GlobalAlloc(GPTR, 1000); の部分をそのまま malloc(1000); に置き換えても、Windowsの実装の都合なのか、文字化け「seikouヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ」が出てしまう。
WinInet

malloc だと、GlobalAlloc引数の「GPRT」などのフラグ設定を出来ない。
WinInet

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

InternetReadFile の仕様として、ファイルを読み終わると、 InternetReadFile(hFile, outBuf, readSizeMax, (DWORD*)&realRead); の最後の引数 realRead に 0 が代入される。
WinInet

なので、この仕様を使うことで、HTTP通信における受信のwhileループを下記のように書き換えることもできる。
WinInet

※ windows11で試したところ、realRead を (int) で変換しないといけなかった。変換しないとビルドエラーになった。
外部リンク集

ソケットプログラミングについての出版状況は、まるで Visual C++ の Windows APIプログラミングの書店での不足ぶりと類似の状況であり、あまり市販の入門書ではソケットプログラミングが解説されていない状態である。
外部リンク集

そのため当面はネットの解説サイトを参考にするしかない。
外部リンク集

下記のサイトに、充実したWinSockの解説がある。