More Related Content
Similar to サーバーを作ろう (毎週のハンズオン勉強会の資料)
Similar to サーバーを作ろう (毎週のハンズオン勉強会の資料) (20)
サーバーを作ろう (毎週のハンズオン勉強会の資料)
- 2. 目次 勉強しよう TCP/IP Socket API クライアントとサーバー 設計しよう サーバーの多重化 ウェブプログラマが何故サーバーを作るの?
- 3. TCP/IP ストリーム型の通信プロトコル 送ったら、送った順に届く 順序を持った可変長バイト列(文字列に変換しやすい!)として送ったり、受け取ったりできるのでプログラミングしやすい ちゃんと届けられる 相手が通信可能な状態かどうか分かる 「相手が通信可能な状態じゃなかったら」という処理を書ける 届いたかどうか分かる 「届かなかったら」という処理を書ける
- 4. TCP/IP 送ったら送った順に届く $fp = stream_socket_client('tcp://www.nicovideo.jp:80'); fwrite($fp, "GET / HTTP/1.0"); fwrite($fp, "Host: www.nicovideo.jp"); echo stream_get_contents($fp); fclose($fp);
- 5. 送ったら送った順に届く HTML がそのまま読めるよ www.nicovideo.jp 送ったら 送った順に届く PHP Web Server HTTP/1.1 200 OK <html>…</html> HTTP/1.1 200 OK <html>…</html> TCP/IP TCP/IP
- 6. TCP/IP ちゃんと届けられる $fp = stream_socket_client('tcp://www.nicovideo.jp:80', $errno, $errstr); if ($fp=== false) { throw new Exception($errstr); } if (fwrite($fp, "GET / HTTP/1.0") === false) { throw new Exception($php_errormsg); } if (fwrite($fp, "Host: www.nicovideo.jp") === false) { throw new Exception($php_errormsg); } echo stream_get_contents($fp); fclose($fp);
- 10. ファイル操作の関数が使える ソケットから読む ファイルから読む $fp = stream_socket_client( 'tcp://www.nicovideo.jp:80'); fwrite($fp, "GET / HTTP/1.0"); fwrite($fp, "Host: www.nicovideo.jp"); while (fgets($fp) !== "") { } $content = stream_get_contents($fp); if (!mb_check_encoding($content, 'UTF-8')) { throw new Exception('Invalid encoding!'); } fclose($fp); libxml_use_internal_errors(true); $doc = new DOMDocument(); $doc->loadHTML($content); libxml_clear_errors(); echo $doc->getElementsByTagName('title') ->item(0)->textContent; $fp = fopen('test.html', 'r'); $content = stream_get_contents($fp); if (!mb_check_encoding($content, 'UTF-8')) { throw new Exception('Invalid encoding!'); } fclose($fp); libxml_use_internal_errors(true); $doc = new DOMDocument(); $doc->loadHTML($content); libxml_clear_errors(); echo $doc->getElementsByTagName('title') ->item(0)->textContent; まったく同じコードを使える
- 13. 作ってみよう Mecabで形態素解析して、結果を返すサーバーと、そのクライアントを作る Mecabのインストール sudoapt-get install mecab # mecabプログラム sudoapt-get install libmecab-dev # mecab.so を作るのに必要 sudoapt-get install mecab-naist-jdic mecab-jumandic-utf8 # mecabの UTF-8 の辞書 sudoapt-get install build-essentials # php拡張をコンパイルするためのツール群 sudopear channel-discover pecl.opendogs.org sudopear remote-list -c opendogs sudopear install opendogs/mecab-beta # 以下のファイルに extension=mecab.so を追加 sudovim /etc/php5/cli/php.ini
- 14. クライアント側のコード 繋ぐだけ エラー処理とかは各自勉強してね $fp = stream_socket_client('tcp://127.0.0.1:1111'); fwrite($fp, json_encode($argv[1]) . ""); $list = json_decode(fgets($fp, 1024)); var_dump($list); fclose($fp);
- 15. サーバー側のコード 接続を待って、 接続を確立する エラー処理とかは各自勉強してね $server = stream_socket_server('tcp://127.0.0.1:1111'); while (true) { $fp = stream_socket_accept($server); while (!feof($fp)) { $str = fgets($fp, 1024); fwrite($fp, json_encode(mecab_split(json_decode($str))) . ""); } fclose($fp); } fclose($server);
- 17. フォークによる多重化 プロセスを分身させる。 メモリは COW で節約されるけど、スレッドと比べてメモリを食う 処理ごとに値が独立してるので、プログラミングが楽 $server = stream_socket_server('tcp://127.0.0.1:1111'); while (true) { $fp = stream_socket_accept($server); if (!pcntl_fork()) { while (!feof($fp)) { $str = fgets($fp, 1024); fwrite($fp, json_encode(mecab_split(json_decode($str))) . ""); } fclose($fp); exit; } } fclose($server);
- 19. イベントループによる多重化 フォークや、スレッドと比べて軽量 $server = stream_socket_server('tcp://127.0.0.1:1111'); stream_set_blocking($server, 0); $base = event_base_new(); $event = event_new(); event_set($event, $server, EV_READ | EV_PERSIST, 'ev_accept', $base); event_base_set($event, $base); event_add($event); event_base_loop($base); $id = 0; $fps = array(); $bufs = array();
- 20. イベントループによる多重化 function ev_accept($server, $flag, $base) { global $id; global $fps; global $bufs; $fp = stream_socket_accept($server); stream_set_blocking($fp, 0); $id++; $buf = event_buffer_new($fp, 'ev_read', NULL, 'ev_error', $id); event_buffer_base_set($buf, $base); event_buffer_timeout_set($buf, 30, 30); event_buffer_watermark_set($buf, EV_READ, 0, 0xffffff); event_buffer_priority_set($buf, 10); event_buffer_enable($buf, EV_READ | EV_PERSIST); $fps[$id] = $fp; $bufs[$id] = $buf; }
- 21. イベントループによる多重化 function ev_error($buf, $error, $id) { global $id; global $fps; global $bufs; event_buffer_disable($bufs[$id], EV_READ | EV_WRITE); event_buffer_free($bufs[$id]); fclose($fps[$id]); unset($fps[$id], $bufs[$id]); } function ev_read($buf, $id) { global $id; global $fps; global $bufs; while ($str = event_buffer_read($buf, 1024)) { $fp = $fps[$id]; fwrite($fp, json_encode(mecab_split(json_decode($str))) . ""); } }