ネコと和解せよ

uipのフローコントロール

uipのフローコントロールを実装してる時のメモです。

http://www.sics.se/~adam/uip/uip-1.0-refman/ のUtilizing TCP Flow Controlによると、uipはフローコントロールができます。

でもこれ、受信を待たせて送信するときに少し注意する事があって、uip_stopで受信を待機させた後に、uipハンドラを抜けずにuip_sendを実行すると、死にます。

受信を待機させて再送信する場合には、以下の手順を取る必要があります。

  1. uip_stopで受信ウインドウを停止
  2. 一度uipに制御を返す←
  3. uip_sendを実行
  4. uip_restartを実行
  5. 一度uipに制御を返す←
  6. 受信再開

テストプログラム

uipのハンドラと、C#の検証用クライアント。クライアントはサーバに接続すると、メッセージを1秒置きに送ります。サーバは初めの10個を受信するとフローコントロールを開始して、10回ポーリングを待ちます。10回のポーリングが終了したら、受信を再開します。

以下のように、受信メッセージの番号が連続していれば、正常に稼働しています。

サーバ
static int c=0;
void
httpd_appcall(void)
{
  struct httpd_state *s = (struct httpd_state *)&(uip_conn->appstate);

  if(uip_closed() || uip_aborted() || uip_timedout()) {
  } else if(uip_connected()) {
	  c=0;
	  uip_send("Welcome\n",8);
  } else if(uip_newdata()){
		  uip_send(uip_appdata,uip_datalen());
		  if(c==10){
			  uip_stop();
		  }

		  c++;

  }else if(uip_stopped(uip_conn) && uip_poll()) {
	  if(c>10 && c<19){
		  uip_send("WAIT\n",5);
	  }
	  if(c==20){
//ここでもuip_sendできる。
		  uip_restart();
	  }
	  c++;
  } else {
//    uip_abort();
  }
}
クライアント
namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            //文字コードを指定する
            System.Text.Encoding enc = System.Text.Encoding.UTF8;

            //サーバーのホスト名とポート番号
            string host = "192.168.128.201";
            int port = 80;

            //TcpClientを作成し、サーバーと接続する
            System.Net.Sockets.TcpClient tcp =
                new System.Net.Sockets.TcpClient(host, port);
            Console.WriteLine("サーバーと接続しました。");

            //NetworkStreamを取得する
            System.Net.Sockets.NetworkStream ns = tcp.GetStream();
            int cs=0;
            for(;;)
            {
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                byte[] resBytes = new byte[256];
                int resSize;
                //データの一部を受信する
                if (ns.DataAvailable)
                {
                    resSize = ns.Read(resBytes, 0, resBytes.Length);
                    ms.Write(resBytes, 0, resSize);
                    string resMsg = enc.GetString(ms.ToArray());
                    Console.WriteLine(resMsg);
                }
                //Readが0を返した時はサーバーが切断したと判断
                byte[] sendBytes = enc.GetBytes("SEND"+cs);
                //データを送信する
                ns.Write(sendBytes, 0, sendBytes.Length);

                cs++;
                Thread.Sleep(1000);
            }
            //閉じる
            ns.Close();
            tcp.Close();
            Console.WriteLine("切断しました。");

            Console.ReadLine();
        }
    }
}