uipのフローコントロールを実装してる時のメモです。
http://www.sics.se/~adam/uip/uip-1.0-refman/ のUtilizing TCP Flow Controlによると、uipはフローコントロールができます。
でもこれ、受信を待たせて送信するときに少し注意する事があって、uip_stopで受信を待機させた後に、uipハンドラを抜けずにuip_sendを実行すると、死にます。
受信を待機させて再送信する場合には、以下の手順を取る必要があります。
- uip_stopで受信ウインドウを停止
- 一度uipに制御を返す←
- uip_sendを実行
- uip_restartを実行
- 一度uipに制御を返す←
- 受信再開
テストプログラム
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(); } } }