uIPのイベント

uIPで簡単なHTTPクライアントを書いてみる。
大げさなものではなくGETしてそれを画面に出力するだけ。


まずはuIPのドキュメント通りに、各状態に合わせたハンドラを呼んでやる

void NetServer::DoEvent()
{
    if (uip_aborted())
    {
        AbortedHandler();
    }
    if (uip_timedout())
    {
        TimeoutHandler();
    }
    if (uip_closed())
    {
        ClosedHandler();
    }
    if (uip_connected())
    {
        ConnectedHandler();
    }
    if (uip_acked())
    {
        AckedHandler();
    }
    if (uip_newdata())
    {
        NewDataHandler();
    }
    if (uip_rexmit() || uip_newdata() || uip_acked() || uip_acked() || uip_connected() || uip_poll())
    {
        SendDataHandler();
    }


次にそれぞれのハンドラを定義してやります。
今回は実験なので、効率とか誰がメモリ解放するのかとか、エラーのときは?とかは一切考えていません。

void NetServer::ConnectedHandler()
{
    printf("%s\n", __FUNCTION__);
    struct server_state* s = (struct server_state *)uip_conn->appstate;
    const char* message = "GET /file HTTP/1.0\r\nServer:192.168.100.2\r\n\r\n";
    s->message = new char[strlen(message) + 1];
    strcpy(s->message, message);
    s->length = strlen(message) + 1;
}

void NetServer::AckedHandler()
{
    printf("%s\n", __FUNCTION__);
    struct server_state* s = (struct server_state *)uip_conn->appstate;
    s->length -= uip_conn->len;
    s->message += uip_conn->len;
    if(s->length == 0)
    {
        printf("send done\n");
    }
}

void NetServer::NewDataHandler()
{
    printf("%s\n", __FUNCTION__);
    for (dword i = 0; i < GetDataLength(); i++)
    {
        printf("%c", (char)uip_appdata[i]);
    }
}

void NetServer::SendDataHandler()
{
    printf("%s\n", __FUNCTION__);
    struct server_state* s = (struct server_state *)uip_conn->appstate;
    if (s->length != 0)
    {
        uip_send((volatile u8_t*)s->message, s->length);
    }
}


フローとしては
1. 接続が完了すると ConnectedHandlerが呼ばれる
2. GETを発行するために、GET文字列と長さを格納
3. 送信
4. ackedが返ってきてまだ未送信分がある場合や、再送の必要があれば再送信します。
5.uip_newdata()が1だったらデータが返ってきています。


これを踏まえたうえで次のエントリーへ。

NetServer実装の悩み

uIPを利用してNetServerを実装する。そのNetServerはC#のようなネットワークAPIを提供する。
そういう形を目指しているのですが、一つ前のエントリーの通りこれはかなり難しそう。
一つ前のエントリーで実現したことは

  • NetServer内にそのままコードを直書き
  • あて先はひとつの(IPアドレス, port)
  • 複雑な状態遷移がない

という感じなのですが
これからネットワークAPI到達までには

  • 複数のIPアドレス、portのコネクションの保持
  • uIPのようなイベントモデル⇔C#のようなネットワークAPIの乖離を埋める実装方法の検討


と問題は山積みのような気がします。
こういうときは誰かとホワイトボードに図を描きながら相談したいなぁ。
この日記上やWikiで相談に乗ってくれる人がいるだけ幸せかもしれませんが。

NetServerの送信がうまくいかない件

WinPcapを利用したデバイスドライバがread時にブロックしているのがだめだということが分かった。
pcap_open_liveの引数でタイムアウトを1msにして、readでタイムアウト時は0を返すようにしたら送信がうまくいくようになった。


その後、べた書きであれこれ整理したら、簡単なWebクライアントができた。
これは、サーバー内に実装されたべた書きなので、サーバーがAPIを提供しこれと同じ機能のものが、サーバー外で実装できなければならない。


uIPのイベントループはuip_flagsの状態を調べるマクロ(uip_connected, uip_ackedなど)をうまく使ってイベント処理をしなければいけないんだけれども、任意のクライアントに対してどうやってこれを開放すべきなんだろうか。
考え中。


次のステップとしては

  • uip_xxxのイベントを意識したコードを書く。(サーバー内クライアント)
  • イベント処理の流れが完全に把握できたら任意のクライアントに開放する仕組みを考える

という感じで。