WinSock/HTTP通信

MSDNのコードではsockaddr_in 構造体を使わずにコードを書いている。(MSDNのコードでは addrinfo* 構造体でソケット通信のコードを書いている。)
だが、Windowsでも、addrinfo* 構造体をつかわずに、sockaddr_in 構造体でも、WinSockのコードを下記のようにして書ける。
下記コードでは、ローカルホスト(手元のパソコン自身)に接続している。
実務などでエラー処理などのための場合分けが必要になるだろうだが、しかし上記コードにおいてはエラー処理コードがあるとコードが煩雑になって学習者に分かりづらくなる恐れがあるので、上記コードからはエラー処理を除去してある。
なお、 inet_pton とは、「pton」の「P」が Presentation の略だと言われており、ここでの「P」は文字列などの意味であり、「n」はNetwaorkの意味であり、
の意味であり、ptonは、文字列などを、ソケット関連の構造体のもつ情報に変換する命令である。
逆に、 inet_ntop なら、
なので、ソケット関連の構造体のもつ情報を、文字列などに置き換える命令である。

Apathce などのサーバー上のHTMLファイルとどうやって通信するのか、読者は疑問に思ってるだろう。
さて、実験したいところであるが、しかし、いきなり外部と通信をすると、アクセスが集中したりして迷惑だし、アクセス遮断されたりしかねないので、まず手元のパソコンで試そう。
Windowsの場合、XAMPPというサーバソフトがあるので、これを手元のパソコンにインストールすれば、ローカルのHTTPサーバを立てられる。
文字コードの設定など、やや面倒ではあるが、しかしソケット通信のテストなら、英語のままでも構わないので、インストールしてしまおう。
PHP/確実に動作させるまで にXAMPPの設定方法など書いてあるので、参考に。
さて、まず、Windows版XAMPPの場合、フォルダ htdocs に、サーバにアップロードしたいファイルを入れる。このhtdocsのような、そこに入れたファイルをサーバにアップロードするフォルダのことを「ドキュメント・ルート」という。
ともかく、このhtdocsフォルダに、HTMLファイルでもPHPファイルでも何でもいいので、名前をつけてファイルを入れる。
ただし、「index.html」など一部の名前は、特別な用途が決まっているので、このような名前は避ける必要がある。
などで、とりあえず「aaaa.html」とか「detarame.html」(デタラメ.html の意味)とか、特別な用途を予約されたファイル名とは重ならないファイル名をつけて、htdocsに入れよう。
detarame.html に「seikou」(成功)とでも書いておこう。そのメッセージが読めたら、ソケット通信の成功である。
日本語はパソコンでは非アスキーコードという意味でマルチバイト文字であるが、しかしWindowsのchar型はシングルバイト文字対応の型であり、そしてWinSockのrecv関数はchar型しか認めていないので、どうあがいても、recv関数だけでは、日本語のようなマルチバイト文字を認識できない。なので、WinSockで日本語を処理するのは、文字コード処理に関する、かなり高度なスキルを要求され、面倒である。
よって、初心者レベルでは、WinSockで日本語を送受信するのは、諦めよう。
さて、XAMPP および Apacheを起動するのを忘れてはいけない。XAMPPの起動後に表れるXAMPPコントロールパネルから、モジュール『Apache』の欄にある「Start」ボタンを押せばいい。すると、Apatcheが手元のパソコン内で立ち上がる。
これさえ出来れば、あとは下記のコードをVisual Studio に入力して、実行・ビルドすればいいだけである。

これを実行すると、下記のような表示結果になる。

上記コードを実行したら無限にループになる場合、Apacheの立ち上げを忘れている可能性があります。初日は本節を読んでいてXAMPPのApacheを立ち上げるように説明しているので忘れなくても、2日目以降によく立ち上げを忘れるでしょう。
コード改修で立ち上げ忘れを考慮したコードに改修する事も可能ですが(for文に置き換えるなど)、しかし本節では初学者への分かりやすさの観点から、上記コードはそのまま、ループ箇所をwhile文のままにしておきます。

いわゆる外部ネットを含む、ウェブサイトとのHTTP通信を行う場合、ポート番号を「80」番に設定する必要があります。この理由は、そういうふうにHTTPの国際規格やTCP/IPの国際規格などがそう決まっているからです。
一般に、プロトコルの種類や通信の種類などによって、ポート番号が決まっています。
メール通信になると、なぜか上記とは別のポート番号を使います(読者の混同を防ぐため、メール通信のポート番号については、このページでは紹介しません)。
読者は「なぜ、プロトコルの種類に応じてポート番号が違うのか?」とか疑問はわくでしょうが、結局は単に昔の人がそう決めてしまっただけです。
「ポート」と聞くと、あたかも、ハードウェアの何かの端子の番号かのように思えるかもしれませんが、しかしハードウェア端子は一切、ポート番号とは無関係です。
「ポート番号」とは、その名に反して、実質的な意味はプロトコル番号です。
コードの送信メッセージの
で、GETメッセージを送っている。
なお、1回だけの
は改行の意味である。ホスト情報など追加の情報を送る場合、1回だけ「\r\n」と入力し、追加情報を1行ずつ入力していく。
また
と2個改行が続くことで、送信メッセージ全体がいったん終了する事を意味している(これはUnixソケット通信でも同様)。
なお、Bad Request の結果の表示は、
のようになる。

recv() 関数は、終了時に 0を返し、エラー時に -1 を返すという実装である。
また、一般にWinSockではエラー時の値は SOCKET_ERROR で表すので、念のため上記コードでは recvの直後のif文に SOCKET_ERROR も加えている。

実は、recvのブロックのif文は、
だけでも、とりあえず上記のコードは動くし、正常に終了する。
一方、
とか
だと、無限ループになってしまい、そのままでは終了しなくなるので、閉じるにはコマンドプロンプトのウィンドウ右上の「X」ボタンで閉じるしかない。

send(destSocket, SendMsg, strlen(SendMsg) , 0); の strlen の長さは、ピッタリと、 strlen(SendMsg)でなければなりません。
もし、「多いほうが安全かな?」と誤解して strlen(SendMsg)+1 のように大きくしてしまうと、2行目以降の送信メッセージが1文字ずれてしまうためか、もし、
で送信しようと思っても、送信に失敗します。
HTTP1.0ではHostメッセージは不要ですが、HTTP1.1ではHostメッセージが必ず必要なので、strlenはピッタリと過不足なくするように注意してください。

HTTP1.1では、Hostメッセージが必ず必要です。
Apache のローカルホスト名はそのまま「localhost」ですので、ポート番号の80番とあわせてメッセージを
のようにHostメッセージを加えて送信することになります。

sockaddr_in構造体はIPv4用の構造体なので、IPv6では使えません。
ですが、ほぼ同じ使い方のできるIPv6用の構造体 sockaddr_in6 がありますので、これを使うと IPv6対応のソケットプログラミングが簡単です。なお Linuxでも同様に sockaddr_in6 に置き換える方法で、IPv6対応できます。
さて、WinSockでも Linuxでも、 AF_INET を AF_INET6 に変える必要があります。
また、sin_portもsin6_portに、sin_family も sin6_family に変えるなど、 構造体のメンバもIpv6用に更新する必要があります。
s_addr も s6_addr に更新します。なお Linuxでも同様です。
手作業でやると大変なので、Visual Studioの一括変換の機能をつかうとラクだし、 更新し忘れによるエラーも防げるでしょう。
また、Ipv6のローカルホストは::1です。6ケタでなく3ケタなのは不思議に感じるかもしれませんが、国際規格(RFC 4291 など)でそう決まっています。(なお RFCとはw:Request for Commentsのこと)
なお、「::」というふうにコロンが2個続いた記号の意味は、のこりすべて「0」と言う意味です。
つまり、「::1」とは、一番右だけが1で、残りは0の、
という意味です。なお、(2進数ではなく)16進数です。
なので、たとえば、コードは下記のようになります。

FirefoxでもGoogle Chromeでも何でもいいですがwebブラウザで、ファンクションキー F12 を押すと、開発者モードになり、ページ下部にブラウザ内などにある情報が表れます。
そこで、いくつも出て来る項目のうち、「ネットワーク」を押すことで、どんなヘッダが送受信されているかを見ることができます。
実際、この「WinSock/HTTP通信」で開発者モードでヘッダを見てみると、
のような情報を見ることができます。
ローカルホストで作成したApacheサーバ上にあるwebページでも同様の方法で、通信ヘッダを見ることができます。

実は、Visual Studio を使わなくても、telnet (テルネット) という国際規格で決まっている通信形式を使って、WindowsではコマンドプロンプトからHTTP通信が出来ます。
なお、LInuxなどでも telnet 通信が可能です。
当然ながらVisual Studio を経由しない通信なので、(visual Studioとは異なり)マイクロソフトへの会員登録も不要です。
もしWindowsでtelnet通信する場合、まずはコントロールパネルにある「Windowsの機能の有効化または無効化」から(OSバージョンによって多少は手順が異なる)、telnetをインストールします。
その後、コマンドラインで
と入力すると、下記のような画面に移り変わります。
まず、文字が表示されて無い場合があるので、コマンド
で文字表示をするように設定します。
すると、成功すれば、
のような画面になります。
失敗したり、何かおかしくなったら、コマンド
または
でtelnetを終了できます。
この後、コマンド
で、ローカルホストにポート80番で接続できます。
その後、
のような画面に移り、もう接続しています。
Windows番では、カーソル位置がおかしな位置に移り、たとえば冒頭の「Microsoft Telnet クライアントへようこそ」の行頭に移ったりしますが、仕様です。
その後、コマンドで
とコマンド入力します。
のような画面になります。
なお、BackSpace キーや Delete キーは使えません。見た目では、文字は同じでも、telnetはBackSpaceなどのキー情報ごと送信してしまうので、間違えたら、t接続から、やり直す必要があります。
さらに、コマンド入力は、円ターキーを2回押す必要があります。
さて、成功すれば、下記のようにメッセージが返ってきます。