class: title, smokescreen, shelf, no-footer # WWW<br>- アプリケーション層の例(1) - --- class: col-2, compact # 概要 <div class=footnote> <small><small> (脚注) われわれプロにとってのインターネットは<B>データ(パケット)転送システム</B>のことです。 素人さんには 「インターネットってブラウザでいろいろできることじゃないの?」 というイメージがあると思いますが、 それは(データ転送後の)ある一つのアプリケーションの動作の話にすぎません。 ブラウザというアプリが圧倒的に利用頻度が高いので、 そういう偏見が生まれるのでしょうけどもね </small></small> </div> - 目標 - プロトコルに慣れましょう - 可読性のある簡単な例です - ウエブ: HTTP (本節) - メールの受信: POP3(次節) - メールの送信,転送: SMTP(次節) <wbr> - アプリケーション層 - 俗にレイヤー7 - 正確にはOSI L7と同等ではない - サーバクライアントモデルの一例 - 注: 実際には裏でDNSも動いています - 簡単化のため今回はDNS抜きで説明 <div class=digest-begin-01></div> --- class: col-2,compact # WWW (ワールドワイドウエブ)の全体像 <small> - 転送プロトコルは HTTP - 特にHTTP/1.0はシンプルなステートレスプロトコルの良い例(後述) - データ(転送する内容)は自由 - 代表例はHTML形式のファイルですが画像でも動画でも何でもOK - **【重要】** データの種類や表現などはTCP/IPと無関係! **TCP/IPの仕事**はブラウザに**データを確実に届ける**こと - 組織と規格文書 - W3C (World Wide Web Consorcium)からの勧告 - IETFでRFCになるものもある - だれでも参加可能 <wbr> - ブラウザでは URI を指定する - かつては URL と呼んでいたため、歴史的に URL と呼ぶ人も多い - 用語: URI が URL と URN の上位概念 - 情報空間で一位に特定できるIDがURI - URL (Uniform Resource Locator), 場所に相当するもの - 例: https://サーバ名/場所/ - URN 場所という概念が無いもの - 例: 電話番号, 本のISBN番号 ``` [URIの例] https://portal.mc.chitose.ac.jp/portal2/ tel:0123-27-6097 isbn:978-0321336316 ``` </small> --- class: col-2,compact # WWW サーバの動作 (HTTP/1.0 の動作説明) <div class=footnote> <small><small> (脚注1)後日、実際に手動でこれをやってみる演習をします <br> (脚注2)listen(2)の2は引数ではなく、Unix マニュアルの第2章「システムコール」を参照という意味 </small></small> </div> <small> - WWW サーバはリクエストを待つ - ポート番号は 80, プロトコルはTCP - 専門用語では「80/tcp で listen(2)」 - クライアント(WWWブラウザ)〜サーバ間にTCPの通信回路を確立(詳細は後日) - クライアントからサーバに「このURIのデータをください」とリクエスト - サーバクライアントモデル - サーバ = WWWサーバ - クライアント = WWWブラウザ <wbr> - サーバからクライアントに返答 - 「HTTP ヘッダ + データ本体」を返す - **ヘッダ**には制御情報が書かれます - ヘッダとデータ本体の区切りとして空行を1行挿入します (これはメールと同じRFC822準拠フォーマット) <!-- HTTP の図 --> ![width320px](images/http-overview.gif) </small> --- class: img-right,compact # WWW サーバの動作 (サーバの内側をもうすこし詳細に) ![height480px](images/httpd-internal.png) <small> - WWW サーバは要求を待っている - (1) クライアントからサーバに接続し「このURIをください」と要求 - `GET /index.html HTTP/1.0`は「/index.htmlをください」という要求で、 HTTP/1.0はプロトコル番号の指定 - (2) WWWサーバ内の動作 - URIから対象のコンテンツが/index.htmlであることを割り出す - サーバのコンテンツ領域を検索しindex.htmlファイルを探しだす - ファイルからデータを読み込む - (3) サーバからクライアントに返答 - HTTP ヘッダ + データ本体(index.html) </small> <div class=digest-end-01></div> --- class: col-2,compact # デモおよび演習で使うコマンドについて <div class=footnote> <small><small> (脚注) AWSでは Macのひとはtelnetのかわりにncコマンドで試してください、例: nc localhost 80 </small></small> </div> <small> ``` telnet ホスト telnet ホスト ポート番号 ``` - telnetを使い手動でプロトコルを再現できます、デバッグの基本です - telnet というコマンドは本来リモートログインするためのコマンドですが、 オプションで任意のポート番号を引数に取れます - 任意のポート番号へアクセスし手動でプロトコルを人間が再現できます - ホスト名のlocalhostはホスト自身を意味しています(詳細は後日) <wbr> - ただし、このようなことが出来るのは、 歴史のあるTCPアプリケーションの一部に限られます - 1970〜1980年代からある代表的なアプリケーション群で、 このような人間デバッグ操作が可能です - 1ビットで十分なのに、 わざわざ可読性のあるテキストで命令をやりとりするのはデータ量の無駄です。 デバッグ要素が強いといえます </small> <div class=digest-begin-02></div> --- class: compact,center # HTTP/1.0のデモ <iframe width="679" height="394" src="https://www.youtube.com/embed/hq-CTEZK1-k?rel=0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> 埋め込みが動作しない方は[こちら](https://www.youtube.com/embed/hq-CTEZK1-k?rel=0) --- class: col-2,compact # ステートレスプロトコル, ステートフルプロトコル <div class=footnote> <small><small> </small></small> </div> <small> - stateless protocol (例: HTTP 1.0) - サーバに要求、データを受信 - 一回ずつ終了、つまり「状態」がない(ステートレス) - HTTP/1.0ではブラウザで表示する部品の数だけ取りにいく - 右図の例: index.html 背景画像 ... - (もちろん、あれば広告なども) - statefull protocol (例: POP, SMTP) - 現在の状態を追跡、状態によって挙動も変わりうる。 メールの受信を例にとると、認証前後で挙動が異なる </small> ![width320px](images/http-stateless.png) <div class=digest-end-02></div> --- name: www-src-examples class: title, smokescreen, shelf, no-footer # 参考資料<br>WWWサーバのソースコードの概略 <div class=footnote> <small><small> 中間試験には出ません </small></small> </div> --- class: compact # 参考: listenしてデータIO (C言語) ``` struct sockaddr_in sa; // サーバのIPアドレスとポート番号を格納する構造体 int listen_socket, fd; // ソケット // TCP/IP の socket (出入り口)を作成, 作成後オプション設定 listen_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_IP); setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &sopt, sizeof(sopt)); // socket とアドレス情報 sa を結びつける(だから bind システムコール) bind(listen_socket, (struct sockaddr *)&sa, sizeof(sa)); // 作成した socket で待ち受ける(listen) listen(listen_socket, SOMAXCONN); for(;;) { // 待受して無限ループ,ここでは省略したがデータの送受信はforkした子プロセスが行い,親は待つループを続行 fd = accept(listen_socket, (struct sockaddr *)&src_addr, &len);// リクエストを受け取る、それまで待つ(停止) recv(fd, buf, buflen,....); // データを受け取る send(fd, buf, buflen,....); // データを送信する } ``` --- class: img-right,compact # 参考: WWWサーバのソースコード(C言語) ![height480px](images/httpd-internal.png) - [micro_httpd](http://www.acme.com/software/micro_httpd/) - 300行程度でWWWサーバの本質的なところが分かります! - ただlistenする部分は無いので、すべてが分かるわけではありません - 図中の(2)部分だけをC言語で書くとどうなるか?を理解するなら300行で十分ということです。 - 図中の(1)(3)部分はOS(Unix)の機能を利用しています (inetdから起動する前提です。 詳細はUnixオペレーティングシステムの話になるので, 割愛します) --- class: col-2,compact # 参考: WWWサーバのソースコード(C言語) - [mini_httpd](http://www.acme.com/software/mini_httpd/) - 4600行でWWWサーバとしての基本機能はすべて備えています - コードリーディングの素材としては、これが適切でしょう - [thttpd](https://www.acme.com/software/thttpd/) - シンプルで小さく移植性のあるセキュアなサーバ - 11000行なので、なんとか読めるでしょう - WWWサーバとして必要十分な機能がありますが、 拡張性を求めるなら apache <wbr> - 実用的にはapacheとnginxですが大きすぎて勉強の素材としては不適切でしょう - [apache](https://httpd.apache.org/) - NCSA httpd に起源をもつ最も古いWWWサーバ - 2.x系はスレッド駆動型の設計 - モジュールによる拡張機能が豊富 - [nginx](https://www.nginx.org/) (エンジンエックスと発音) - 非同期通信型の設計 - [リバースプロキシサーバ](https://exercises-aws.fml.org/ja/appendix/www/nginx/) (OSの演習で使っています) --- class: compact # 参考: listenしてhello worldを返す (Go言語) ``` package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, req *http.Request) { // "変数 型宣言"の順に書く流派 fmt.Fprintf(w, "hello world") } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) } ``` - 参考: [Go言語でWWWサーバをつくる](https://exercises-aws.fml.org/ja/appendix/golang/) (秋学期のAWS構築ガイドより) --- class: compact # 参考: listenする (言語Python3) ``` #!/usr/bin/env python3 import http.server import socketserver port = 8080 handler = http.server.SimpleHTTPRequestHandler with socketserver.TCPServer(("", port), handler) as httpd: httpd.serve_forever() ``` - 8080/tcp で listen している WWW サーバ - 接続を受け付けますが、何も返事を返してくれません;-) - それにしてもブラックボックス過ぎて裏側が何だか分からないですね;_; - 演習? hello worldとか返すようにhandlerを書いてみるのもよいものですね? - 参考: [PythonでWWWサーバを作る](https://exercises-aws.fml.org/ja/appendix/python/) (秋学期のAWS構築ガイドより)