前の月 / 次の月 / 最新

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

2009-08-27 Thu

マルチスレッド [D言語]

気がついたら std.thread から core.thread になってた。

ThreadGroup て言う機能が増えていた。
複数のスレッドをグループ化して扱うもの。

サンプルを書いてみた。
クロージャも一緒に使ってみる。

import core.thread;

import std.stdio;

auto func(string name,long w)
{
    return (){
        for(int i = 0;i < 10;i++)
        {
            writefln("%s = %d",name,i);
            Thread.sleep( w );
        }
    };
}

void main()
{
    ThreadGroup tg = new ThreadGroup;

    tg.create(func("foo",10_000_000)); //1秒毎にカウント すぐに実行

    tg.add(new Thread(func("bar",5_000_000))); //0.5秒毎にカウント
    tg.add(new Thread(func("baz",5_000_000))); //0.5秒毎にカウント

    writeln("thread start");
    foreach(t;tg)
    {
        if(!t.isRunning) t.start;//未実行のスレッドを起動
    }

    writeln("thread all join");
    tg.joinAll; //すべてのスレッドの終了を待つ

    writeln("thread end");
}

2009-08-21 Fri

chunk をデコード [D言語]

手抜き

byte[] decodeChunked(byte[] src)
{
    byte[] d;
    size_t spos,epos,cpos,chunkSize;
    int i;
    while(1)
    {
        cpos = (cast(string)src[spos..$]).indexOf("\r\n") + 2; //チャンクサイズが書いてある行を取得

        epos = spos + cpos;
        if(src.length < epos) break;

        chunkSize = strtol((cast(string)src[spos..epos]).ptr,null,16); //チャンクサイズを取得

        d ~= src[epos..epos+chunkSize]; //チャンクサイズ分追加

        spos = epos+chunkSize+2;
        if(src.length < spos) break;
    }
    return d;
}

strtol のエラー処理
chunk extension
trailer
の処理が抜けている。

2009-08-13 Thu

HTTP クライアントを書いた [D言語]

HTTP/0.9 用のクライアントを書いた。
サイトによってはレスポンスヘッダが帰ってくる。

次は、1.0を実装してみよう。

private import std.socket;
private import std.string;
private import std.regex;
private import std.conv;

struct URI
{
    string scheme;
    string host;
    string port;
    string path;
    string query;
    string fragment;

    this(string uri)
    {
        auto r = regex(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?","g");
        foreach (m; match(uri, r))
        {
            if(m.pmatch[2].startIdx != -1) scheme = m.input[m.pmatch[2].startIdx .. m.pmatch[2].endIdx];
            if(m.pmatch[4].startIdx != -1)
            {
                int len = m.input[m.pmatch[4].startIdx .. m.pmatch[4].endIdx].indexOf(":");
                if(len != -1)
                {
                    host = m.input[m.pmatch[4].startIdx .. m.pmatch[4].startIdx + len];
                    port = m.input[m.pmatch[4].startIdx + len + 1 .. m.pmatch[4].endIdx];
                }else{
                    host = m.input[m.pmatch[4].startIdx .. m.pmatch[4].endIdx];
                }
            }
            if(m.pmatch[5].startIdx != -1) path = m.input[m.pmatch[5].startIdx .. m.pmatch[5].endIdx];
            if(m.pmatch[7].startIdx != -1) query = m.input[m.pmatch[7].startIdx .. m.pmatch[7].endIdx];
            if(m.pmatch[9].startIdx != -1) fragment = m.input[m.pmatch[9].startIdx .. m.pmatch[9].endIdx];
        }
    }

    const string toString()
    {
        string s;

        if(scheme) s ~= scheme ~ ":";
        if(host) s ~= "//" ~ host;
        if(port) s ~= ":" ~ port;
        if(path)
        {
             s ~= path;
        }else{
             s ~= "/";
        }
        if(query) s ~= "?" ~ query;
        if(fragment) s ~= "#" ~ fragment;

        return s;
    }
}

class HttpClient
{
  private:
    TcpSocket socket;
    string httpVersion;

  public:
    this(string httpVersion)
    {
        switch(httpVersion)
        {
            case "HTTP/0.9":
                this.httpVersion = httpVersion;
                break;
            default:
                new Exception("HTTPVersion not supported");
        }
    }

    byte[] get(in string uri)
    {
        switch(httpVersion)
        {
            case "HTTP/0.9":
                URI u = URI(uri);
                ushort port = 80;
                if(u.port) port = to!(ushort)(u.port);
                auto ia = new InternetAddress(u.host,port);
                socket = new TcpSocket(ia);
                string req = "GET " ~ u.path ~ "\r\n";
                socket.send(req);

                int read;
                byte[] recv;
                byte[1024] buf;

                while((read = socket.receive(buf)) > 0)
                {
                    recv ~= buf[0 .. read];
                }
                return recv;
            default:
                new Exception("HTTPVersion not supported");
        }
    }

    byte[] get(in string uri,in string proxy)
    {
        switch(httpVersion)
        {
            case "HTTP/0.9":
                URI u = URI(uri);
                URI p = URI(proxy);
                if(p.port) port = to!(ushort)(p.port);
                auto ia = new InternetAddress(p.host,port);
                socket = new TcpSocket(ia);

                string req = "GET " ~ u.toString ~ "\r\n";
                int sent;
                sent = socket.send(req);
                if(sent == -1)
                {
                    throw new Exception("error socket send");
                }

                int read;
                byte[] recv;
                byte[1024] buf;

                while((read = socket.receive(buf)) > 0)
                {
                    recv ~= buf[0 .. read];
                }
                if(read == -1)
                {
                    throw new Exception("error socket recive");
                }
                return recv;
            default:
                new Exception("HTTPVersion not supported");
        }
    }
}

使い方

auto client = new HttpClient("HTTP/0.9");
byte[] recv = client.get("http://example.com/");
write(cast(char[])recv);

2009-08-12 Wed

HTTP/1.1 でクライアントの受信: [D言語]

ノンブロッキングの recv でも 0 が帰るまで待機してはいけない。
blocking(false) を指定する。

2009-08-11 Tue

ノンブロッキングモード [D言語]

HTTP/1.1 のコネクション張りっぱなしにするには、
ノンブロッキングモードのソケットを使うのか

2009-08-10 Mon

std.regex の captures が残念な動作をする [D言語]

Captures captures();
括弧で指定したサブマッチの情報を、
ランダムアクセスレンジとして取得します。
第一要素はマッチ全体を表します。

Example:
foreach (m; match("abracadabra", "(.)a(.)"))
{
    foreach (c; m.captures)
        write(c, ';');
    writeln();
}
// writes:
// rac;r;c;
// dab;d;b;

rfc3986 の付録 B. 正規表現による URI 参照の解析を使うと、

string uri = "http://www.ics.uci.edu/pub/ietf/uri/#Related";
auto r = r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?";
foreach (m; match(uri, r))
{
    int i = 0;
    foreach (c; m.captures)
    {
        writefln("$%d = %s",i++,c);
    }
}

こんな風に書いたら、

$0 = http://www.ics.uci.edu/pub/ietf/uri/#Related
$1 = http:
$2 = http
$3 = //www.ics.uci.edu
$4 = www.ics.uci.edu
$5 = /pub/ietf/uri/
core.exception.RangeError@std.regex(1667): Range violation

std.regex を見たら、
captures は サブマッチがすべてマッチしているのを前提にしているようだ。
Captures 構造体の front 関数の定義

Range front()
{
    return input[matches[0].startIdx .. matches[0].endIdx];// startIdx endIdx が -1 になる可能性がある。
}

マッチしていないサブマッチも出力するなら、
captures でなく pmatch を使って書く必要がある。

string uri = "http://www.ics.uci.edu/pub/ietf/uri/#Related";
auto r = r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?";
foreach (m; match(uri, r))
{
    foreach (i,c; m.pmatch)
    {
        if(c.startIdx != -1)
        {
            writefln("$%d = %s",i,m.input[c.startIdx..c.endIdx]);
        }else{
            writefln("$%d = <undefined>",i);
        }
    }
}

出力結果

$0 = http://www.ics.uci.edu/pub/ietf/uri/#Related
$1 = http:
$2 = http
$3 = //www.ics.uci.edu
$4 = www.ics.uci.edu
$5 = /pub/ietf/uri/
$6 = <undefined>
$7 = <undefined>
$8 = #Related
$9 = Related

captures はマッチしたものだけを返すなら、
Captures 構造体の front 関数の定義はこうか?

Range front()
{
    if(matches[0].startIdx == -1) popFront;
    return input[matches[0].startIdx .. matches[0].endIdx];
}

Captures 構造体の length 関数の定義はこうか?

size_t length()
{
    size_t count
    foreach (m;matches)
    {
        if(m.startIdx != -1) count++;
    }
    return count;
}

過去ログ

2011 : 01 02 03 04 05 06 07 08 09 10 11 12
2010 : 01 02 03 04 05 06 07 08 09 10 11 12
2009 : 01 02 03 04 05 06 07 08 09 10 11 12
2008 : 01 02 03 04 05 06 07 08 09 10 11 12
2007 : 01 02 03 04 05 06 07 08 09 10 11 12