2011/10/31

Recent entries from same category

  1. RapidJSON や simdjson よりも速いC言語から使えるJSONライブラリ「yyjson」
  2. コメントも扱える高機能な C++ 向け JSON パーサ「jsoncpp」
  3. C++ で flask ライクなウェブサーバ「clask」書いた。
  4. C++ 用 SQLite3 ORM 「sqlite_orm」が便利。
  5. zsh で PATH に相対パスを含んだ場合にコマンドが補完できないのは意図的かどうか。

node.jsのスライドを見てて、プラットフォームの違いを吸収するnode.jsの核でもある非同期通信ライブラリuvを今日知ったので試してみた。
joyent/libuv - GitHub

platform layer for node.js

https://github.com/joyent/libuv
libev/libeventをwindowsでもちゃんと使いたいという所から出てきたライブラリ。
#include <iostream>
#include <uv/uv.h>

int
main() {
  int r;
  uv_tcp_t tcp;
  struct sockaddr_in server_addr;

  server_addr = uv_ip4_addr("127.0.0.1"80);
  r = uv_tcp_init(uv_default_loop(), &tcp);
  uv_connect_t connect_req;

  r = uv_tcp_connect(&connect_req, &tcp, server_addr,
    [](uv_connect_t *req, int status) {
      std::cout << "connected" << std::endl;

      int r;
      uv_buf_t buf[1];
      uv_stream_t* tcp = req->handle;
      char *req_message = "GET / HTTP/1.0\r\n\r\n";

      buf[0].len = strlen(req_message);
      buf[0].base = req_message;

      uv_write_t write_req;
      r = uv_write(&write_req, tcp, buf, 1,
        [](uv_write_t* req, int status) {
          int r;
          uv_stream_t* tcp = req->handle;

          std::cout << "written" << std::endl;
          r = uv_read_start((uv_stream_t*)tcp,
            [](uv_handle_t* handle, size_t size) -> uv_buf_t {
              std::cout << "alloc " << size << std::endl;
              uv_buf_t buf;
              buf.base = (char*)malloc(size);
              buf.len = size;
              return buf;
            },
            [](uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) {
              std::cout << "read" << std::endl;

              if (nread < 0) {
                if (buf.base) {
                  free(buf.base);
                }
                uv_close((uv_handle_t*) tcp,
                  [](uv_handle_t* handle) {
                    std::cout << "closed" << std::endl;
                  }
                );
                return;
              }
              std::cout << buf.base << std::endl;
               
              free(buf.base);
              return;
            }
          );
        }
      );
    }
  );

  std::cout << "main" << std::endl;
  uv_run(uv_default_loop());
}
コールバック関数用意するのがめんどくさくなったのでlambdaで。 g++ -std=c++0x foo.cxx -lws2_32
みたいにしてビルドします。
ポート80のサーバに接続して応答を貰うだけ。コールバックベースなので、イベントループで処理待機する事になる。よって結果は main
connected
written
alloc 65536
read
HTTP/1.1 200 OK
Date: Mon, 31 Oct 2011 04:19:52 GMT
Server: Apache/2.2.17 (Win32) mod_fcgid/2.3.6
Last-Modified: Sat, 20 Nov 2004 06:16:26 GMT
ETag: "2000000011333-2c-3e94a902f4280"
Accept-Ranges: bytes
Content-Length: 44
Connection: close
Content-Type: text/html

<html><body><h1>It works!</h1></body></html>
alloc 65536
read
closed

こんな感じになります。1リクエストに対して1つのコールバックが必要になる訳で、若干手間な気がしたのはきっと僕がオッサンになったせいだと思う。まぁWindows特有の手続きやハックが要らないのは良いと思う。

あと、これを実際に生かそうと思うと、処理部もストリーミングに対応する必要があり、例えばリクエストの断片を受信したとしても完了部が来るまで蓄え続ける必要がある点はuvを使っても変わらない訳で、libcurlで良くね?と思ってしまったのも、きっと僕がオッサンになったせいだと思う。


追記
サーバ処理書くならlibcurlでは出来ないので、libuvやpicoev使うといいですね。
Posted at by