uIP TCP/IPについて

先ごろ(だいぶ前ですが)ルネサスサイトから uIP TCP/IP Protocol Stack Demostration Document number:R01AN0169EU0100 Ver 1.01 NOTEs: * The demostration directory structure is explained in the application note. * This application note supports RX62N and the demostration project runs on RSK+RX62N. UIPのサンプルを入手しましてSH7670へ移植しました、その結果とても良好な結果を得ましたので報告します 自前のソースでTCP/IPを実装して稼動させていましたがよく応答ができなくなったりしていました いろいろな遷移状態でわけがわからないような(たぶんプロトコルが十分でない)遷移もありました UIPにすると必ず応答して、応答不良などまだ経験がありません、 ただこのUIP長い(大きい)データ転送には弱いんです、”どうしてこんなに時間がかかるの”というぐらい なにか説明ではACKが200mセコンドウェイト方式を採用しているからだとか これの速度はもっと速くはならないものでしょうか? IWIPなど聞きますが、UIPと比べてどうなのでしょうか、IWIPのほうがいいのでしょうか?
  • R_httpd();とhttp_callbackを手当てしてやってみると、

    void R_httpd(void)でcase HTTPD_CLOSED:の

    if(ercd != E_WBLK){でwhile (1);にはまります

    なにが足りないのかな?

    そこだけ違うCPUに抜き出してやっているので

    マニュアル等は詳しいものはありませんでした。

  • もしかしたら、リアルタイムOS用のブロッキングコール用のソース?

    ちょっと調べてみます

  • とりあえず、while (1);をreturn;にしてやりました、


  • IKUZOさん

    こんにちは、シェルティです。

    下記回答します。

    > R_httpd();とhttp_callbackだけを手当てしてやればよいのでしょうか?DNSも含めないといけないのでしょうか?

    手当はもう少し必要です。DNSは不要です。

    手当①:config_tcpudp.c の手当て
    config_tcpudp.c のTCP通信端点設定(tcp_ccep構造体)とTCP受付口(tcp_crep構造体)を見直しましょう。
    R20AN0314JJ0100のサンプルはRXマイコンの各種ボード用にWebサーバ、FTPサーバ、
    DNSクライアント、DHCPクライアントを実装したものですね。
    https://www.renesas.com/ja-jp/software/D6000403.html
    ⇒RX65Nの発売に合わせて更新されたようです。

    上記サンプルだと以下のようになっています。
    それぞれ1行1IDとなります。

    通信端点IDは各APIの第1引数に、受付口IDはtcp_acp_cep()の第2引数に指定します。
    通信端点IDはシステム上複数動作しているアプリを識別するための番号、
    受付口IDはtcp_acp_cep()で待ち受けるポート番号を識別するための番号ですね。
    ゼロになっているところは未実装の設定フィールドです。
    例外として通信端点の1個目のメンバーでEtherのチャネル番号を指定できるようです。

    たとえば、下記設定のシステムでtcp_acp_cep(5, 5, ...); と指定すると、21番ポートで待ち受けて、
    接続が完了するとftp_callback()が呼び出される、という寸法ですね。

    T_TCP_CREP tcp_crep[] =
    {
        { 0x0000, { 0, 21 }}, /* 受付口ID=1 */
        { 0x0000, { 0, 20 }}, /* 受付口ID=2 */
        { 0x0000, { 0, 21 }}, /* 受付口ID=3 */
        { 0x0000, { 0, 20 }}, /* 受付口ID=4 */
        { 0x0000, { 0, 21 }}, /* 受付口ID=5 */
        { 0x0000, { 0, 20 }}, /* 受付口ID=6 */
     { 0x0000, { 0, 80 }}, /* 受付口ID=7 */
     { 0x0000, { 0, 80 }}, /* 受付口ID=8 */
     { 0x0000, { 0, 80 }}, /* 受付口ID=9 */
     { 0x0000, { 0, 80 }}, /* 受付口ID=10 */
    }

    T_TCP_CCEP tcp_ccep[] =
    {
     { 0, 0, 0, 0, 128, ftp_callback },   /* 通信端点ID=1 */
     { 0, 0, 0, 0, 1460, ftp_data_callback }, /* 通信端点ID=2 */
     { 0, 0, 0, 0, 128, ftp_callback },   /* 通信端点ID=3 */
     { 0, 0, 0, 0, 1460, ftp_data_callback }, /* 通信端点ID=4 */
     { 0, 0, 0, 0, 128, ftp_callback },   /* 通信端点ID=5 */
     { 0, 0, 0, 0, 1460, ftp_data_callback }, /* 通信端点ID=6 */
     { 0, 0, 0, 0, 1460, http_callback },  /* 通信端点ID=7 */
     { 0, 0, 0, 0, 1460, http_callback },  /* 通信端点ID=8 */
     { 0, 0, 0, 0, 1460, http_callback },  /* 通信端点ID=9 */
     { 0, 0, 0, 0, 1460, http_callback },  /* 通信端点ID=10 */
    }

    手当②:FTPサーバとWebサーバのコンフィグの手当て
    次にFTPサーバとWebサーバのコンフィグの手当てをします。
    コンフィグファイルは、r_t4_http_server_rx_config.h、r_t4_ftp_server_rx_config.hです。
    それぞれHTTP_START_TCP_CEP、FTP_START_TCP_CEPというのがあります。
    デフォルトだとどちらも"0"になっています。
    HTTP_START_TCP_CEPを6、FTP_START_TCP_CEPを0とすると良いでしょう。
    (通信端点は1からスタートなのに、FTPサーバとWebサーバの設定は0からスタート、混乱してしまいます)

    > if(ercd != E_WBLK){でwhile (1);にはまりますなにが足りないのかな?

    おそらくですが、FTPサーバとWebサーバとで同じ通信端点に対しtcp_acp_cep()を発行してしまい、
    E_QOVRが戻ってきているものと思われます。上記調整で解消するのではないでしょうか。

    > もしかしたら、リアルタイムOS用のブロッキングコール用のソース?

    これは今のところ関係ないと思います。

    > とりあえず、while (1);をreturn;にしてやりました、

    この対処だとウェブサーバは動作しないと思います。

    以上です

  • シェルティさん

    いつもありがとうございます、

    助かりました、以下のように

    const T_TCP_CCEP tcp_ccep[] =

    {

    //1.属性 ,

    //2.送信ウィンドウの先頭アドレス,

    //3.送信ウィンドウのサイズ,

    //4.受信ウィンドウの先頭アドレス,

    //5.受信ウィンドウのサイズ,

    //6.コールバックルーチンアドレス(関数名)

    //  コールバック機能を使用しない場合には、NULL(=0)を設定

    { 0, 0, 0, 0, 128, ftp_callback },//ID=1 ->FTP_START_TCP_CEP 0

    { 0, 0, 0, 0, 1460, ftp_data_callback },//ID=2

    { 0, 0, 0, 0, 128, ftp_callback },//ID=3

    { 0, 0, 0, 0, 1460, ftp_data_callback },//ID=4

    { 0, 0, 0, 0, 128, ftp_callback },//ID=5

    { 0, 0, 0, 0, 1460, ftp_data_callback },//ID=6

    { 0, 0, 0, 0, 1460, http_callback },//ID=7 ->HTTP_START_TCP_CEP 6

    { 0, 0, 0, 0, 1460, http_callback },//ID=8

    { 0, 0, 0, 0, 1460, http_callback },//ID=9

    { 0, 0, 0, 0, 1460, http_callback },//ID=10

    { 0, 0, 0, 0, 128, t4_callback },//ID=11 ->TELNET_START_TCP_CEP 10

    { 0, 0, 0, 0, 128, t4_callback },//ID=12

    { 0, 0, 0, 0, 128, t4_callback },//ID=13

    };

    このようにしましたら、FTP、HTTP、TELNETで支障なくcallback を呼び出すことができました

    引き続きやってみます。

  • お世話になります

    t4_callback内のTFN_TCP_ACP_CEP:は確立済みと思いまして、tcp_snd_dat(cepid,をやってtelnetターミナル画面には文字が表示されます、その後case TFN_TCP_SND_DAT:からlen=(int)tcp_rcv_dat(を実行しますと、E_QOVR 、E_WBLK、E_WBLK等で受信に失敗します、アドバイスいただけませんか?

  • tcp_snd_dat(cepid,をやってtelnetターミナル画面には文字が表示されます、そこをbreak;しますとE_QOVR は出ません、後E_WBLKノンブロッキングコール受付ですがこれは何を意味するのか?

  • E_WBLK len=-83 E_WBLK len=-83 これは正常なt4_callbackでもこれを返すようです、正常であればデータ数を返すとなっていますがtelnetの画面はエコーしているのですが、tcp_rcv_datの返り値はE_WBLKです、送信の数値を見るとercdはrercd=1です、ということは返り値で判断せずrercdでデータ数を判断できるということのように理解したほうが良さそうです。

  • t4_callbackですが受信から始めないといけないみたいで、最初に、tcp_snd_dat(をやってしまいますとその後telnet応答しなくなります

    tcp_rcv_datを先にしますと(telnetタミナル送信から)しますとその後やり取りができるようです、telnetの場合先にtcp_snd_datをやりたいのでこれをどうするか思案中です。

  • IKUZOさん

    シェルティです、こんにちは。

    下記回答します。

    > t4_callback内のTFN_TCP_ACP_CEP:は確立済みと思いまして、
    > tcp_snd_dat(cepid,をやってtelnetターミナル画面には文字が表示されます、
    > その後case TFN_TCP_SND_DAT:からlen=(int)tcp_rcv_dat(を実行しますと、
    > E_QOVR 、E_WBLK、E_WBLK等で受信に失敗します、アドバイスいただけませんか?

    →続きのレスで解決しておられますね。
     ご推察の通り、tcp_snd_dat()の後にbreak;が必要です。
     1回のコールバックで発行できるAPIは通信端点毎に1個だけです。
     なぜならば、このTCP/IPは同一通信端点に対しリクエストを
     キューイングできないからです。

    > tcp_snd_dat(cepid,をやってtelnetターミナル画面には文字が表示されます、
    > そこをbreak;しますとE_QOVR は出ません、後E_WBLKノンブロッキングコール受付ですがこれは何を意味するのか?

    →E_WBLKは「ノンブロッキングコールを受け付けた」という意味です。

    > E_WBLK len=-83 E_WBLK len=-83 これは正常なt4_callbackでもこれを返すようです、
    > 正常であればデータ数を返すとなっていますがtelnetの画面はエコーしているのですが、
    > tcp_rcv_datの返り値はE_WBLKです、送信の数値を見るとercdはrercd=1です、
    > ということは返り値で判断せずrercdでデータ数を判断できるということのように理解したほうが良さそうです。

    →ノンブロッキングコールの場合コールバック関数の第3引数のポインタの先に受信APIの実行結果(受信データ長)が入っています。
     APIをノンブロッキングコールした瞬間はデータ受信はしておらず、先に説明した通り、
     「ノンブロッキングコールを受け付けた」という意味のエラーコードE_WBLKが戻ってくるわけですね。
     少し注意が要るのが、TCP/IPはストリーム型通信といってストリームの終わりはアプリが判断しないと
     いけないことです。これはTCP/IPに10バイト送れと命令してもTCP/IPや通信経路上の通信機器がその場の都合で
     データを分割することがあるためです。このため、アプリは希望するデータ長、または、使用するプロトコルで
     規定された終了コードが来るまで受信データをバッファリングし、受信終了条件を満たすかどうかを
     受信コールバック毎に判定する必要があります。
     
    > t4_callbackですが受信から始めないといけないみたいで、最初に、
    > tcp_snd_dat(をやってしまいますとその後telnet応答しなくなります
    > tcp_rcv_datを先にしますと(telnetタミナル送信から)しますとその後やり取りができるようです、
    > telnetの場合先にtcp_snd_datをやりたいのでこれをどうするか思案中です。

    →t4_callback()内に以下switch-caseをbreak;付きで用意しましょう。
     これでmain()や他の通信端点と非同期に延々と通信を行う循環機構が完成します。
     この骨組みを維持すればあとは送信データを加工したり、受信データに応じた処理をしたり、
     自由に作れます。
     
     TFN_TCP_ACP_CEP:
       tcp_snd_dat(...);
       break;
     TFN_TCP_SND_DAT:
       tcp_rcv_dat(...);
       break;
     TFN_TCP_RCV_DAT:
       tcp_cls_cep(...);
       break;
     TFN_TCP_CLS_CEP:
       tcp_acp_cep(...);
       break;

     接続完了コールバック(TFN_TCP_ACP_CEP)→送信(tcp_snd_dat)→
     送信完了コールバック(TFN_TCP_SND_DAT)→受信(tcp_rcv_dat)→
     受信完了コールバック(TFN_TCP_RCV_DAT)→切断(tcp_cls_cep)→
     切断完了コールバック(TFN_TCP_CLS_CEP)→接続待ち(tcp_acp_cep)→
     <繰り返し>

     先に述べた受信のバッファリングは今後手当していってください。
     ウェブサーバのコードがこのあたりのバッファリングをどのようにしているか
     参考にできると思います。
     
    以上です

  • シェルティさん

    いつもお世話になります

    いろいろやりました、一つのことが課題でして

    tcp_snd_datと tcp_rcv_datはcallbackの中で使用可能ということで

    このことはtcp_snd_datのすぐ後にtcp_rcv_datする

    tcp_rcv_datのすぐ後にtcp_snd_datということで良くわかります

    tcp_snd_datで1Byte送信これを続けさまに発信するなどうまくいきます

    tcp_rcv_datの場合は相手方がデータが入らない場合は無理です

    ということでcallbackの中で全てを処理すれば

    telnetは即可能であると感じています

    ただtelnetを構成する場合、

    現在使用しているインタープリタに結合させたいと思っています

    telnetから入力したコマンドラインをインタープリタに送信して

    インタープリタから出力される文字列をtelnetに出力したいと思います

    この場合callback外の箇所からtelnet送信を起動しないといけません

    uIPの場合ですと、パケットがなくてもcallbackが常に一定間隔で呼ばれており

    その呼ばれたタイミングでデータがある場合はtelnetに出力できます

    t4ですとcallbackイベントがないと飛んできません

    外部からtelnet出力を起動できますでしょうか?

    インタープリタをcallback中に作りこめばできることはわかりますが、

    そうなるとソフト作成が大変なことになるので

    アドバイスでもいただけましたら、幸いです。

  • httpサーバーの方は画面が一応出ました、ファイルリストがブラウザーに表示されました、とりあえずご報告まで。

  • IKUZOさん

    こんにちは、シェルティです。

    なるほど、IKUZOさんが実現されたいことと合致するか少し不安ですが、
    callback処理外から送信することは可能です。

    callbackではtcp_rcv_dat()を呼び出しておいて、
    インタプリター側が送信したくなったら、インタプリター側で
    tcp_rcv_dat()をキャンセルして、キャンセル完了のcallbackで
    tcp_snd_dat()を呼び出し、tcp_snd_dat()の完了で再度tcp_rcv_dat()を
    呼び出す、というものです。

    キャンセルして送信している間も、TCP/IPライブラリ内部にある程度は
    (config_tcpudp.c のtcp_ccep構造体に設定した受信ウィンドウ分は)
    バッファリングされますので、キャンセル/送信中に
    受信データをこぼすこともありません。

    この動作を実現しているサンプルがありますので参考にしてみてください。

    https://www.renesas.com/mw/t4
      ->画面下のほうのTCP/IPサンプルプログラム
        ->画面左のほうのサンプルプログラム
          ->RXファミリ 組み込み用TCP/IP M3S-T4-Tinyを用いたサンプルプログラム Firmware Integration Technology
            ->解凍データの奥底にある「tcp_nonblocking_cancel_sample」フォルダのecho_srv.c

    また、ファイルリストがブラウザに表示されたとのことで、よかったです。       

    ネットワークはいろいろ難儀なところが多いので動き始めるとうれしさも大きいですね。

    以上です

  • シェルティさん

    いつもありがとうございます

    インタプリター側でtcp_rcv_dat()をキャンセルして、キャンセル完了のcallbackでtcp_snd_dat()を呼び出すとのことですが

    キャンセル完了のcallbackというのが良くわからなくて、

    サンプルではstrstrで<CRLF> checkをやってから送信しているので、この場所でコマンド解析というようなことですが

    「インタプリター側でtcp_rcv_dat()をキャンセルして」具体的にはどのようにできますか?

  • サンプルコードによると、受信データが1秒以内に受信できない場合はキャンセルを発令するという意味のように取れますが

    それで、受信のところtcp_rcv_dat(で停止することがないと受け取れますが、たぶんそうですよね、

    その考え方でやってみたいと思います、

    やってみましたがTFN_TCP_RCV_DAT:には1秒毎には来てくれませんね。

    やはり外部から「インタプリター側でtcp_rcv_dat()をキャンセルして」やるひつようがあるようです。

    シェルティさん、すみません、コードミスってました、今一度確認します。

    シェルティさん、解りましたTFN_TCP_RCV_DAT:が外部から送信する窓口ですね、この線でやってみます。