最初の投稿はSocketプログラミング! 最初のネタは、Socketプログラミングをしてみよう!というネタです。 通常、我々はライブラリやパッケージを使ってWebアプリケーションを実装します。 例えばアプリケーションから任意のHTTPリクエストするような処理を実装する際は、TCPクライアントの処理を意識せずにライブラリを呼び出すだけで実装できたりします。それでもいいっちゃいいのですが、あえて自前で実装してみようという試みです。 こうすることで、普段我々があまり意識していないTCP/IPの世界を意識するきっかけとなるのではと考えております。ということで、まずはSocket関数を用いてHTTPクライアントを書いてみることにします。 書いてみたきっかけ PHPやRuby等で何気に使っているHTTPクライアント。 PHPだとfopen()cURLでHTTPリクエストできたり、Rubyだとopen-uriのopen()等でHTTPリクエストができちゃいます。 ある日、自社Web API "FlaMixer" と連携する部分をPHPで書く機会があった。前述のfopen()やcURLのオプション指定だけでは連携が困難だったので、モジュールの使い方であれこれと悩むくらいなら自分で書いちゃえって事で書いてみました。これがきっかけでSocket関数を使ったコーディングを経験し、他の若手メンバにも経験してもらおう思いました。 ということで、言語はなんでもいいからHTTPクライアントを書いてみよう~ まずは言いだしっぺの若手amo-kのコード:
<?php
/*
 * TCPソケットを使ったHTTPクライアント
 *
 * 1.コマンドラインよりURL(絶対PATH)を取得
 * 2.HTTP GETリクエスト
 * 3.サーバからのメッセージボディを標準出力
 */
$url = $argv[1];
$port = 80;

// URLより、ホスト、ポート取得
if (!preg_match("/http:\/\/([^\/]+)(\/.*)/", $url, $match))
{
    echo "URLが不正です\n";
    exit;
}
$host = $match[1];
if (count($array = split(":", $host)) > 1)
{
    $host = $array[0];
    $port = $array[1];
}
$path   = $match[2];

$http_request_method = "GET";
$httpResponseHeader  = "";
$httpResponseBody    = "";

if (($socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false)
{
    echo sprintf("ソケット生成失敗: %s\n", socket_strerror(socket_last_error()));
}
if (($res = socket_connect($socket, $host, $port)) === false)
{
    echo sprintf("ソケット接続失敗: %s%s\n", $res, socket_strerror(socket_last_error($socket)));
}

$httpRequest  = "$http_request_method $path HTTP/1.1\r\n";
$httpRequest .= "Host: $host\r\n";
$httpRequest .= "Connection: Close\r\n\r\n";

// HTTPリクエスト
socket_write($socket, $httpRequest, 1024);

// レスポンヘッダ取得
do
{
    $httpResponseHeader .= socket_read($socket, 8);
}
while (strpos($httpResponseHeader, "\r\n\r\n") === false);

// メッセージボディ取得
do
{
    $buf = socket_read($socket, 1024);
    $httpResponseBody .= $buf;
}
while ($buf);

// ソケットクローズ
socket_close($socket);

// メッセージボディ出力
echo $httpResponseBody;
?>
指摘いただきましたので、今度はもう少し丁寧に。 以下、該当部分のみの修正版です。 ついでにメッセージボディ取得部分も丁寧に書き直しました。 (別に乱暴に書いていたわけではないのですがw コメントありがとうございます! > とおりすがりさん
// レスポンヘッダ取得
do
{
    if (($buf = socket_read($socket, 1024, PHP_NORMAL_READ)) === false)
    {
        echo sprintf("レスポンスヘッダ取得失敗: %s\n", socket_strerror(socket_last_error($socket)));
        exit;
    }
    $httpResponseHeader .= $buf;
}
while (strpos($httpResponseHeader, "\r\n\r\n") === false);

// メッセージボディ取得
do
{
    if (($buf = socket_read($socket, 1024)) === false)
    {
        echo sprintf("メッセージボディ取得失敗: %s\n", socket_strerror(socket_last_error($socket)));
        exit;
    }
    $httpResponseBody .= $buf;
}
while ($buf);