Unixソケットプログラミング/HTTP通信
HTTP

『WinSock/外部通信の方法』で概要を説明してある。webブラウザなどのよるホームページなどの表示の仕組みは、原理的にはコレ。
HTTP

Unixの場合、下記のコードで、ローカルホスト上のApacheにアップロードしたhtmlファイルに、コマンドラインなどからソケット通信でアクセスできる。
HTTP

あらかじめ、アップロード用のhtmlファイルを作成しておいて、Apacheのドキュメントルートフォルダに入れること。(ドキュメントルートが分からなければ、wikibooks『PHP/確実に動作させるまで』を参照するか、ググること。)
HTTP

解説は、WinSock/HTTP通信 とほぼ同じなので、そちらを参照されたし。
HTTP

なお、アップロードしたhtmlファイルは、上記コードの場合
HTTP

という内容である。(ファイル名は、デタラメhtmlのつもり)
HTTP

意味的には
HTTP

のつもり。
HTTP

なお、Linuxの場合は日本語の全角文字列でも送受信できる(※ Fedora32 で 2020年7月9日に確認ずみ )。Windowsの場合などは文字コードなどの理由で日本語の設定が難しい場合があるので、本wikiでは説明の簡単化のため英語にしてある。
HTTP

コードの注意点として、もしWinSockのコードをコピーしたとき、Linuxでは strcpy_s を使えないので、strcpy に置き換える必要があり、引数なども調整する必要がある。
HTTP

また、send()関数ではなくwrite()関数にすること。Linuxにも互換性のためかsend()関数が用意されているようだが、若干、WinSockとは仕様が違うらしく、WinSockのコードそのままでは動かない。ならば、いっそwrite()関数に置き換えたほうがLinuxではラクである。
HTTP

上記コードを実行したら無限にループになる場合、Apacheの立ち上げを忘れている可能性があります。初日は本節を読んでいてApacheを立ち上げるように説明しているので忘れなくても、2日目以降によく立ち上げを忘れるでしょう。
HTTP

コード改修で立ち上げ忘れを考慮したコードに改修する事も可能ですが(for文に置き換えるなど)、しかし本節では初学者への分かりやすさの観点から、上記コードはそのまま、ループ箇所をwhile文のままにしておきます。
HTTP

いわゆる外部ネットを含む、ウェブサイトとのHTTP通信を行う場合、ポート番号を「80」番に設定する必要があります。この理由は、そういうふうにHTTPの国際規格やTCP/IPの国際規格などがそう決まっているからです。
HTTP

一般に、プロトコルの種類や通信の種類などによって、ポート番号が決まっています。
HTTP

メール通信になると、なぜか上記とは別のポート番号を使います(読者の混同を防ぐため、メール通信のポート番号については、このページでは紹介しません)。
HTTP

読者は「なぜ、プロトコルの種類に応じてポート番号が違うのか?」とか疑問はわくでしょうが、結局は単に昔の人がそう決めてしまっただけです。
HTTP

「ポート」と聞くと、あたかも、ハードウェアの何かの端子の番号かのように思えるかもしれませんが、しかしハードウェア端子は一切、ポート番号とは無関係です。
HTTP

「ポート番号」とは、その名に反して、実質的な意味はプロトコル番号です。
HTTP

場合によっては複数のクライアント側コンピュータが1つのホストにアクセスする事もよくあるので、同じポート番号を使う場合が多々あり(たとえば、どのクライアントでもHTTPなら80番を使う)、ポート番号だけでは通信先を特定できないです。
HTTP

よって、通信先の特定のために、ポート番号とIPアドレスの少なくとも2つ以上の関連付け(「アソシエーション」といいます)されたデータセットによって、通信が管理されています。
HTTP

コードの送信メッセージの
HTTP

でGETメッセージを送っている。
HTTP

なお、1回だけの
HTTP

は改行の意味である。ホスト情報など追加の情報を送る場合、1回だけ「\r\n」と入力し、追加情報を1行ずつ入力していく。
HTTP

また
HTTP

と2個改行が続くことで、送信メッセージ全体がいったん終了する事を意味している(これはUnixソケット通信でも同様)。
HTTP

なお、Bad Request の結果の表示は、
HTTP

のようになる。
HTTP

Windowsでは、write()関数ではなくsend()関数でGETメッセージを送るのだが、Unixの場合、send関数で送っていも、recv()関数で受け取る事ができる。
HTTP

Unixでもrecv() 関数の仕様は、正常な終了時には 0を返し、エラー時に -1 を返すという実装のようである。
HTTP

実は、recvのブロックのif文は、
HTTP

だけでも、とりあえず上記のコードは動くし、正常に終了する。
HTTP

一方、
HTTP

とか
HTTP

だと、無限ループになってしまい、そのままでは終了しなくなるので、閉じるにはコマンドプロンプトのウィンドウ右上の「X」ボタンで閉じるしかない。
HTTP

write(destSocket, SendMsg, strlen(SendMsg) ); の strlen の長さは、ピッタリと、 strlen(SendMsg)でなければなりません。
HTTP

もし、「多いほうが安全かな?」と誤解して strlen(SendMsg)+1 のように大きくしてしまうと、2行目以降の送信メッセージが1文字ずれてしまうためか、もし、次のHostメッセージ
HTTP

で送信しようと思っても、送信に失敗します。
HTTP

HTTP1.0ではHostメッセージは不要ですが、HTTP1.1ではHostメッセージが必ず必要なので、strlenはピッタリと過不足なくするように注意してください。
HTTP

※ 解説はWinSockとほぼ同じ。
HTTP

sockaddr_in構造体はIPv4用の構造体なので、IPv6では使えません。
HTTP

ですが、ほぼ同じ使い方のできるIPv6用の構造体 sockaddr_in6 がありますので、これを使うと IPv6対応のソケットプログラミングが簡単です。なお LinuxでもWindowsでも同様に sockaddr_in6 に置き換える方法で、IPv6対応できます。
HTTP

さて、Linuxでも、 AF_INET を AF_INET6 に変える必要があります。
HTTP

また、sin_portもsin6_portに、sin_family も sin6_family に変えるなど、 構造体のメンバもIpv6用に更新する必要があります。
HTTP

s_addr も s6_addr に更新します。なお Linuxでも同様です。
HTTP

手作業でやると大変なので、テキストエディタの一括変換の機能をつかうとラクだし、 更新し忘れによるエラーも防げるでしょう。
HTTP

また、Ipv6のローカルホストは::1です。6ケタでなく3ケタなのは不思議に感じるかもしれませんが、国際規格(RFC 4291 など)でそう決まっています。(なお RFCとはw:Request for Commentsのこと)
HTTP

なお、「::」というふうにコロンが2個続いた記号の意味は、のこりすべて「0」と言う意味です。
HTTP

つまり、「::1」とは、一番右だけが1で、残りは0の、
HTTP

という意味です。なお、(2進数ではなく)16進数です。なお、IPv6の4個の数字の並びは合計8グループです(6グループではないです)。(つまり、4×8=32個の数字がある。)
HTTP

Ipv6のvとはバージョンの事です。数字のグループの個数とは無関係です。IPv4の数字が「127.0.0.1」のように4グループなのは、偶然の一致です。
HTTP

ともかく、Unixの場合、IPv6対応のコードは、たとえば下記のようになります。
HTTP

HTTPは1.0でも1.1でも、どちらでも、IPv6対応が可能です。
HTTP

HTTP1.1では、Hostメッセージが必ず必要です。
HTTP

Apache のローカルホスト名はそのまま「localhost」ですので、ポート番号の80番とあわせてメッセージを
HTTP

のようにHostメッセージを加えて送信することになります。
HTTP

外部サイトにアクセスする場合、Hostメッセージは
HTTP

のように入力する事になる。
HTTP

Hostに入れるのは、アクセス先のドメイン名とポート番号である。まちがえて、localhostを入れても、単なるエラーになる。
gethostbyname は別物

gethostbyname という、サイト名をもとにIPアドレスを検索できる関数がソケット通信にはあるが(なおWindowsでは、IPv6対応などの事情により、2020年現在では他の関数( getaddrinfo など)に置き換わっている)、
gethostbyname は別物

しかし、この gethostbyname の問い合わせ先は DNSサーバ であるので、HTTP通信とはあまり関係ない。(IPアドレス先サーバーに接続はしない関数である。単にサイト名からIPアドレス情報をDNSサーバに問い合わせて受け取るだけである。)
gethostbyname は別物

しかも、企業のホームページの場合、たとえばどこかの企業の何らかのコンテンツをブラウザで閲覧した場合、アドレス欄に
gethostbyname は別物

のように各ページのURLが表示されるわけだが、
gethostbyname は別物

しかし通常、DNSサーバーに登録されているのは
gethostbyname は別物

の部分までである。
gethostbyname は別物

また、けっして gethostbyname は目的の企業サイトなどと接続するわけではない。下記コードのように、bind() や connect() などが無くても、 gethostbyname の結果を表示できる。
gethostbyname は別物

構造体であらかじめ hostent 型というのがOSなどにより用意されている。なので、この構造体の「hostent」という部分の名前は変えてはいけない。
gethostbyname は別物

*host は単なるポインタ変数名なので、他のポインタ変数名に変えてもいい。
gethostbyname は別物

IPアドレスを表示したい場合は、さらに
gethostbyname は別物

というコードを付けたす。
gethostbyname は別物

つまり、
gethostbyname は別物

のようになる。
gethostbyname は別物

struct in_addr *addr; のポインタ変数名 *addr は別にほかの変数名でもよいのだが(変えても動作することをLinuxのFedora32で2020年に確認ずみ)、慣習的に「*addr」という変数名をつかう場合が多い。
gethostbyname は別物

余談だが、gethostbyname で自機にホスト名やIPアドレスなどを問い合わせるとき、わざわざサーバー側のプログラムを立てる必要は無い。
gethostbyname は別物

このことからも、最終目的のサーバーに問い合わせているのではなく、別のところ(一般にはDNSサーバー)に問い合わせている様子が分かるだろう。
getaddrinfo

まず、コマンドラインで
getaddrinfo

と入れると、そのドメインのIPアドレスが出て来る。
getaddrinfo

たとえば、
getaddrinfo

みたいにコマンドを入れる。(yaahuleは架空の企業名。)
getaddrinfo

もし後述コードで getaddrinfo によるソケット通信をした結果、nslookupで表示された結果と同じIPv6アドレスまたはIPv6アドレスが表示されれば、そのコードはDNSサーバとの通信に成功している。
getaddrinfo

Ipv4用のコードだが、getaddrinfoが次のコードで、Linux上で動く。(※ 2020年7月17日に、Fedora32上で動くことを確認ずみ。)
getaddrinfo

なお、下記コードの企業名、ドメイン名はデタラメなので、実験の際には、実在する検索エンジンのドメインに変えること。(下記のままだと、./a.out などで実行しても、何も表示されない。)
telnet

telnet (テルネット)という国際規格に基づいたネット通信の方法が古くから(インターネット黎明期から)あり、Linuxでは、コマンドラインで telnet を利用できる。
telnet

Fedora Linux (Fedora 32) の場合、最初からtelnetはインストールされているので新規インストール作業は不要だが、もし入っていなかったら、
telnet

でインストールできる。
telnet

コマンドラインで
telnet

と入力すれば、コマンドラインがtelnet に移行する。 コマンド入力が成功すれば、
telnet

のような画面になる。
telnet

telnet をやめたい場合、
telnet

または単に
telnet

と入力すればいい。
telnet

Linuxの場合、telnetの文字表示の設定が最初から済んでいるので、特に設定をいじる必要は無い。(なおWindowsは、初期設定ではtelnetが文字表示されない。)
telnet

Linux の telnet で、ローカルホストに接続するには、Apacheを立ち上げたあと、コマンド入力
telnet

で接続できる。書式は
telnet

の書式である。
telnet

コマンド入力が成功すれば、
telnet

のような画面になる。
telnet

IPアドレスでもオープンできるが、
telnet

と書くと、数字が続いて見づらいので、なるべく「localhost」でオープンしたほうが良いだろう。
telnet

そして、Escape character なんとかの次の行にカーソルが移るので、
telnet

のように入力する。(リターン)とは、その位置でリターンキーを押すこと。けっして実際に「(リターン)」と文字入力してはいけない。
telnet

この結果、見た目は
telnet

のようになる。
telnet

成功すれば、
telnet

(長いので冒頭を抜粋)のような返事が返ってきて、以下のようになる。