Ryo Iizuka / libMiMic

Dependents:   MbedFileServer_1768MiniDK2 RedWireBridge IssueDebug_gcc MiMicRemoteMCU-for-Mbed ... more

Revision:
12:efe841863fc8
Parent:
2:b96c1e90d120
Child:
20:3b0b444b4deb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/uip/NyLPC_cTcpSocket.c	Sat Apr 20 05:03:57 2013 +0000
@@ -0,0 +1,1188 @@
+/*********************************************************************************
+ * PROJECT: MiMic
+ * --------------------------------------------------------------------------------
+ *
+ * This file is part of MiMic
+ * Copyright (C)2011 Ryo Iizuka
+ *
+ * MiMic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * For further information please contact.
+ *  http://nyatla.jp/
+ *  <airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>
+ *
+ *********************************************************************************/
+#include "NyLPC_cTcpSocket_protected.h"
+#include "NyLPC_stdlib.h"
+#include "NyLPC_cUipService_protected.h"
+
+
+static NyLPC_TUInt32 iss32=3939;
+#define SIZE_OF_IPv4_TCPIP_HEADER 40
+
+
+/**
+ * for Debug
+ * RTOの情報をログ領域に取る。
+ */
+#ifdef RTO_LOG
+    NyLPC_TUInt32 rto_log[256];
+    int rto_log_st=0;
+    #define DEBUG_RTO_LOG(i_inst) if(rto_log_st<256){rto_log[rto_log_st++]=i_inst->uip_connr.current_rto32;};
+#else
+    #define DEBUG_RTO_LOG(i_inst)
+#endif
+
+static void sendRst(NyLPC_TcTcpSocket_t* i_inst);
+
+
+/**
+ * ソケットステータスを元に、IPパケットを構成します。
+ * この関数は、ロック状態でコールしてください。
+ */
+static void setPacket(const NyLPC_TcTcpSocket_t* i_inst,NyLPC_TcIPv4Payload_t* i_payload,NyLPC_TUInt8 i_tcpf,const void* i_buf,NyLPC_TUInt16 i_len)
+{
+    void* buf;
+    switch(i_tcpf){
+    case TCP_PSH|TCP_ACK:
+        buf=NyLPC_cIPv4Payload_initTcpTx(i_payload,0x05,((UIP_TCPH_LEN) / 4),i_len);
+        NyLPC_cIPv4Payload_setTcpTxHeaderByConnection(i_payload,&(i_inst->uip_connr),TCP_ACK|TCP_PSH);
+        //bufの書き込み
+        memcpy(buf,i_buf,i_len);
+        break;
+    case TCP_ACK:
+    case TCP_FIN|TCP_ACK:
+    case TCP_RST|TCP_ACK:
+        NyLPC_cIPv4Payload_initTcpTx(i_payload,0x05,((UIP_TCPH_LEN) / 4),0);
+        NyLPC_cIPv4Payload_setTcpTxHeaderByConnection(i_payload,&(i_inst->uip_connr),i_tcpf);
+        break;
+    case TCP_SYN|TCP_ACK:
+        NyLPC_cIPv4Payload_initTcpTx(i_payload,0x05,((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4),0);
+        NyLPC_cIPv4Payload_setTcpTxHeaderByConnection(i_payload,&(i_inst->uip_connr),i_tcpf);
+        //MSSの設定(OPTION領域のアドレス0)
+        NyLPC_TTcpHeader_setMmsOpt((NyLPC_TUInt8*)(i_payload->payload.tcp+1),i_inst->uip_connr.default_mss);
+        break;
+    case TCP_SYN:
+        NyLPC_cIPv4Payload_initTcpTx(i_payload,0x05,((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4),0);
+        NyLPC_cIPv4Payload_setTcpTxHeaderByConnection(i_payload,&(i_inst->uip_connr),i_tcpf);
+        //MSSの設定(OPTION領域のアドレス0)
+        NyLPC_TTcpHeader_setMmsOpt((NyLPC_TUInt8*)(i_payload->payload.tcp+1),i_inst->uip_connr.default_mss);
+        break;
+    default:
+        NyLPC_Abort();
+    }
+    NyLPC_cIPv4Payload_setTcpWnd(i_payload,NyLPC_cFifoBuffer_getSpace(&(i_inst->rxbuf)));
+    NyLPC_cIPv4Payload_closeTcpTxPacket(i_payload);
+    return;
+}
+/**
+ * 指定した送信パケットがACK済であるか調べる。
+ */
+static NyLPC_TBool isPacketAcked(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq)
+{
+    int rp;
+    struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
+    rp=i_inst->txbuf.rp;
+    while(rp!=i_inst->txbuf.wp){
+        if(q[rp].ackno==i_sq){
+            return NyLPC_TBool_FALSE;
+        }
+        rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
+    }
+    return NyLPC_TBool_TRUE;
+}
+/**
+ * 送信キューからi_sq以前に送信したパケットを除外して、残り個数を返却する。
+ */
+static int getNumOfSending(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq)
+{
+    int rp,n;
+    struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
+    rp=i_inst->txbuf.rp;
+    n=0;
+    while(rp!=i_inst->txbuf.wp){
+        if(q[rp].ackno==i_sq){
+            return n;
+        }
+        n++;
+        rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
+    }
+    return n;
+}
+/**
+ * この関数は、コネクションをリセットします。
+ * ロック状態でコールしてください。
+ * 関数は、現在バッファにある再送信待ちデータを開放します。
+ */
+static void resetTxQWithUnlock(NyLPC_TcTcpSocket_t* i_inst)
+{
+    int i,l;
+    struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
+    void* dlist[NyLPC_TcTcpSocket_NUMBER_OF_TXQ];
+
+    l=0;
+    while(i_inst->txbuf.rp!=i_inst->txbuf.wp){
+        dlist[l]=NyLPC_cIPv4Payload_detachBuf(&(q[i_inst->txbuf.rp].data));
+        l++;
+        i_inst->txbuf.rp=(i_inst->txbuf.rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
+    }
+    i_inst->txbuf.rp=i_inst->txbuf.wp=0;
+    //ロック解除
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    //セーブしたバッファを開放
+    for(i=0;i<l;i++){
+        NyLPC_cUipService_releaseTxBuf(dlist[i]);
+    }
+    return;
+}
+/**
+ * TXバッファの再送パケットのACK番号を更新します。
+ * ロックして実行してください。
+ * @param i_ackno
+ * ネットワークオーダーのACK番号
+ */
+static void updateTxAck(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_ackno)
+{
+    NyLPC_TUInt8 rp;
+    struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
+    NyLPC_ArgAssert(i_inst!=NULL);
+    rp=i_inst->txbuf.rp;
+    while(rp!=i_inst->txbuf.wp){
+        NyLPC_cIPv4Payload_updateAckNo(&(q[rp].data),i_ackno);
+        rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
+    }
+}
+
+/**
+ * RTOの予測関数
+ */
+static void estimateRTO(NyLPC_TcTcpSocket_t* i_inst,int s,int n)
+{
+    NyLPC_TcStopwatch_t sw;
+    NyLPC_TUInt32 cr_rtt_min,cr_rtt_max,sk_rto,new_rto,w;
+    int i;
+    struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
+    NyLPC_cStopwatch_initialize(&sw);
+
+    sk_rto=i_inst->uip_connr.current_rto32;
+    //ACKされたパケットの個数は?
+    switch(n){
+    case 1:
+        NyLPC_cStopwatch_set(&sw,q[s].tick_of_sent);
+        cr_rtt_min=NyLPC_cStopwatch_elapseInMsec(&sw);
+        if(sk_rto<cr_rtt_min){
+            //現在のRTOよりも大きい→再送があった。(再送の理由が回線遅延によるものかわからないので、基本RTOを25%増やす。)
+            new_rto=sk_rto*10/8;
+        }else if(sk_rto/4<cr_rtt_min){
+            //現在のRTOの1/4< n < 現在のRTO 想定内の変動。1/8
+            new_rto=(sk_rto+(cr_rtt_min*3*7))/8;
+        }else{
+            //現在の1/4以下。RTOを再計算。 RTOが大きすぎるので再計算。(計測値を優先した現在値との平均値)
+            new_rto=(sk_rto+(cr_rtt_min*3*3))/4;
+        }
+        break;
+    default:
+        //複数のパケットなら、最大と最小の時刻を得る。
+        NyLPC_cStopwatch_set(&sw,q[s].tick_of_sent);
+        cr_rtt_min=cr_rtt_max=NyLPC_cStopwatch_elapseInMsec(&sw);
+        for(i=1;i<n;i++){
+            NyLPC_cStopwatch_set(&sw,q[(s+i)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ].tick_of_sent);
+            w=NyLPC_cStopwatch_elapseInMsec(&sw);
+            if(cr_rtt_min>w){
+                cr_rtt_min=w;
+            }
+            if(cr_rtt_max<w){
+                cr_rtt_max=w;
+            }
+        }
+        if(sk_rto<cr_rtt_min && sk_rto<cr_rtt_max){
+            //最大値,最小値とも現在のRTTより大きい→低速な回線を検出。
+            new_rto=cr_rtt_max*10/8;//最大経過時間の25%増しの時間を設定。
+        }else if(sk_rto/4<cr_rtt_min){
+            //現在のRTOの1/4< n < 現在のRTO 想定範囲内。1/8の加重平均で速度計算。
+            new_rto=(sk_rto+(cr_rtt_min*3*7))/8;
+        }else{
+            //現在の1/4以下。RTOが大きすぎるので再計算。(計測値を優先した加重平均)
+            new_rto=(sk_rto+(cr_rtt_min*3*3))/4;
+        }
+        break;
+    }
+    NyLPC_cStopwatch_finalize(&sw);
+    if(new_rto<UIP_IP_RTO_MINIMUM){
+        new_rto=UIP_IP_RTO_MINIMUM;
+    }
+    i_inst->uip_connr.current_rto32=new_rto;
+}
+
+/**
+ * TXキューから、入力されたシーケンス番号より前のパケットを除外します。
+ * リングバッファのrp->wp-1までをチェックして、sqに等しいi_sq以前のパケットバッファをo_dlistへ返します。
+ *
+ */
+static int updateTxQByIndex(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq,void* o_dlist[])
+{
+    int rp,n;
+    struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
+    //ロック状態なう
+    rp=i_inst->txbuf.rp;
+    n=0;
+    //This is debug
+    DEBUG_RTO_LOG(i_inst);
+
+    while(rp!=i_inst->txbuf.wp){
+        o_dlist[n]=NyLPC_cIPv4Payload_getBuf(&(q[rp].data));
+        if(q[rp].ackno==i_sq){
+            //i_inst->txbuf.rp->rpのパケットのRTOからbaseRTOの値を再計算。
+            estimateRTO(i_inst,i_inst->txbuf.rp,n+1);
+            i_inst->txbuf.rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
+            return n+1;
+        }
+        n++;
+        rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
+    }
+    return 0;
+}
+
+
+
+/**
+ * 空きキューを1個返します。
+ * 空きキューの
+ */
+static struct NyLPC_TcTcpSocket_TxQItem* getTxQ(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TcStopwatch_t* i_timer)
+{
+    int i;
+    struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
+    while(!NyLPC_cStopwatch_isExpired(i_timer)){
+        //クローズドに遷移してしまったら、エラーである。
+        if(i_inst->tcpstateflags==UIP_CLOSED){
+            return NULL;
+        }
+        //キューの空きをチェック。wp+1==rpなら、キューがいっぱい。rp==wpなら、キューが空。
+        if(((i_inst->txbuf.wp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ)==i_inst->txbuf.rp){
+            //一時的なアンロック
+            NyLPC_cMutex_unlock(&(i_inst->_smutex));
+            //タスクスイッチ
+            NyLPC_cThread_yield();
+            //ロック
+            NyLPC_cMutex_lock(&(i_inst->_smutex));
+            continue;
+        }
+        i=i_inst->txbuf.wp;
+        i_inst->txbuf.wp=(i+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
+        return &(q[i]);
+    }
+    //失敗。タイムアウト。
+    return NULL;
+}
+
+
+
+
+
+
+/**********************************************************************
+ * public 関数
+ **********************************************************************/
+
+
+NyLPC_TBool NyLPC_cTcpSocket_initialize(NyLPC_TcTcpSocket_t* i_inst,void* i_rbuf,NyLPC_TUInt16 i_rbuf_len)
+{
+    int i;
+    NyLPC_TcUipService_t* srv=_NyLPC_TcUipService_inst;
+    //uipサービスは初期化済であること。
+    NyLPC_Assert(NyLPC_TcUipService_isInitService());
+
+    NyLPC_cFifoBuffer_initialize(&(i_inst->rxbuf),i_rbuf,i_rbuf_len);
+    NyLPC_AbortIfNot(NyLPC_cMutex_initialize(&(i_inst->_smutex)));
+    i_inst->tcpstateflags=UIP_CLOSED;
+    i_inst->txbuf.rp=i_inst->txbuf.wp=0;
+    for(i=0;i<NyLPC_TcTcpSocket_NUMBER_OF_TXQ;i++){
+        NyLPC_cIPv4Payload_initialize(&(i_inst->txbuf.txq[i].data));
+    }
+    //管理リストへ登録。
+    return NyLPC_cIPv4_addSocket(&(srv->_tcpv4),i_inst);
+}
+void NyLPC_cTcpSocket_finalize(NyLPC_TcTcpSocket_t* i_inst)
+{
+    int i;
+    NyLPC_TcUipService_t* srv=_NyLPC_TcUipService_inst;
+    NyLPC_Assert(NyLPC_TcUipService_isInitService());
+    //uipサービスは初期化済であること。
+    if(!NyLPC_cIPv4_removeSocket(&(srv->_tcpv4),i_inst)){
+        //削除失敗、それは死を意味する。
+        NyLPC_Abort();
+    }
+    //開放漏れの保険
+    if(i_inst->txbuf.rp!=i_inst->txbuf.wp){
+        NyLPC_cMutex_lock(&(i_inst->_smutex));
+        resetTxQWithUnlock(i_inst);
+    }
+    for(i=0;i<NyLPC_TcTcpSocket_NUMBER_OF_TXQ;i++){
+        NyLPC_cIPv4Payload_finalize(&(i_inst->txbuf.txq[i].data));
+    }
+    NyLPC_cFifoBuffer_finalize(&(i_inst->rxbuf));
+    NyLPC_cMutex_finalize(&(i_inst->_smutex));
+
+    return;
+}
+
+/**
+ * この関数は、cIPv4Tcpが呼び出すシステムAPIです。
+ * uipコアタスクが実行します。
+ * ソケットを、SYNパケットで初期化して、UIP_SYN_RECV状態にします。
+ * @return
+ * 遷移に成功すると、TRUEを返します。
+ */
+NyLPC_TBool NyLPC_cTcpSocket_setSynPayload(NyLPC_TcTcpSocket_t* i_inst,const NyLPC_TcIPv4Config_t* i_config,const NyLPC_TcIPv4Payload_t* i_ipp)
+{
+    NyLPC_TUInt16 tmp16;
+    //ソケットが無効であること。
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+    if(i_inst->tcpstateflags==UIP_CLOSED)
+    {
+        /* Fill in the necessary fields for the new connection. */
+        i_inst->uip_connr.current_rto32 = UIP_IP_RTOP_INITIAL;
+        i_inst->uip_connr.lport = i_ipp->payload.tcp->destport;
+        i_inst->uip_connr.rport = i_ipp->payload.tcp->srcport;
+        i_inst->uip_connr.ripaddr=i_ipp->header->srcipaddr;
+        i_inst->uip_connr.snd_nxt32=iss32;
+        i_inst->uip_connr.lipaddr=&(i_config->ip_addr);
+        /* rcv_nxt should be the seqno from the incoming packet + 1. */
+        i_inst->uip_connr.rcv_nxt32= NyLPC_ntohl(i_ipp->payload.tcp->seqno32)+1;
+        //MSSの設定
+        i_inst->uip_connr.default_mss=i_inst->uip_connr.peer_mss=i_config->default_mss;
+        if(NyLPC_TTcpHeader_getTcpMmsOpt(i_ipp->payload.tcp,&tmp16)){
+            i_inst->uip_connr.peer_mss=tmp16;
+        }
+        i_inst->uip_connr.peer_win=0;
+        NyLPC_cFifoBuffer_clear(&(i_inst->rxbuf));
+        //前回のデータが残っていた場合の保険
+        if(i_inst->txbuf.rp!=i_inst->txbuf.wp){
+            resetTxQWithUnlock(i_inst);
+        }else{
+            NyLPC_cMutex_unlock(&(i_inst->_smutex));
+        }
+        //ここでステータスがかわる。
+        i_inst->tcpstateflags = UIP_SYN_RCVD;
+        return NyLPC_TBool_TRUE;
+    }
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    return NyLPC_TBool_FALSE;
+}
+/**
+ * sq番のTxがキューから消え去るのを待ちます。
+ * この関数は、アンロック状態でコールしてください。
+ * <div>
+ * パケットがキューからなくなる条件は、以下の2つです。
+ * <ul>
+ * <li>ACKを受信してパケットキューが更新された。</li>
+ * <li>RSTを受信して(CLOSEDに遷移して)、キューがクリアされた。</li>
+ * <li>送信タイムアウトで関数が(CLOSEDに遷移させて)キューをクリアした。</li>
+ * </ul>
+ * </div>
+ * @param i_wait_msec
+ * @return
+ * 1番目の条件でパケットが消失したときのみ、TRUEを返します。
+ * 失敗した場合、TCPステータスがCLOSEDでなければ、RSTを送信してステータスをCLOSEDにします。
+ */
+static NyLPC_TBool waitForTxRemove(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq,NyLPC_TcStopwatch_t* i_timer)
+{
+    NyLPC_TUInt8 f;
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+    while(!NyLPC_cStopwatch_isExpired(i_timer)){
+        //パケットが送信中か調べる。
+        if(!isPacketAcked(i_inst,i_sq)){
+            //まだある場合は、タスクスイッチを繰り返して消失を待つ。
+            NyLPC_cMutex_unlock(&(i_inst->_smutex));
+            NyLPC_cThread_yield();
+            NyLPC_cMutex_lock(&(i_inst->_smutex));
+            continue;
+        }
+        //なくなった場合は、原因を調べる。
+        f=i_inst->tcpstateflags;
+        NyLPC_cMutex_unlock(&(i_inst->_smutex));
+        return (f==UIP_CLOSED)?NyLPC_TBool_FALSE:NyLPC_TBool_TRUE;
+    }
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    return NyLPC_TBool_FALSE;
+}
+
+
+
+
+/**
+ * 再送信処理をセットして、パケットを送信します。
+ * この関数は「アンロック状態で」実行してください。
+ * @param i_len
+ * 送信データサイズを指定します。
+ * この番号は、シーケンス番号の加算値ではありませんので、注意をしてください。
+ * @return
+ * <ul>
+ * <li>n=-1:送信キューへの投入に失敗した。</li>
+ * <li>n>=0:nバイトのデータを送信キューへの投入することに成功した。</li>
+ * </ul>
+ * 送信キューに失敗する理由は2つあります。1つは、TXバッファがフルでタイムアウト。もうひとつは、非同期なコネクリョンのリセットです。
+ * 失敗した場合、TCPステータスがCLOSEDでなければ、RSTを送信してステータスをCLOSEDにします。
+ */
+static NyLPC_TInt32 sendWithRetransmit(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt8 i_tcpf,const void* i_buf,NyLPC_TUInt16 i_len,NyLPC_TcStopwatch_t* i_timer,NyLPC_TUInt32* o_ack)
+{
+    struct NyLPC_TcTcpSocket_TxQItem* txq;
+    NyLPC_TUInt16 s;
+    void* buf;
+    NyLPC_TUInt32 next_ack;
+
+
+    //送信バッファを取得
+    buf=NyLPC_cUipService_allocTxBuf(i_len+(SIZE_OF_IPv4_TCPIP_HEADER),&s);
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+    //ペイロードがある場合のみ、相手のwindowサイズが0以上になるのを待つ。
+    if(i_len>0){
+        while(i_inst->uip_connr.peer_win==0){
+            NyLPC_cMutex_unlock(&(i_inst->_smutex));
+            //時間切れならエラー。
+            if(NyLPC_cStopwatch_isExpired(i_timer)){
+                return -1;
+            }
+            NyLPC_cThread_yield();
+            NyLPC_cMutex_lock(&(i_inst->_smutex));
+        }
+    }
+    //送信キューの取得
+    txq=getTxQ(i_inst,i_timer);
+    //送信キューが取れなかった。
+    if(txq==NULL){
+        //シーケンス番号をロールバックできないので、エラーとする。
+        NyLPC_cMutex_unlock(&(i_inst->_smutex));
+        NyLPC_cUipService_releaseTxBuf(buf);
+        return -1;
+    }
+    //IPv4ペイロードの書き込み
+    NyLPC_cIPv4Payload_setTxBuf(&(txq->data),buf);
+
+    //送信バッファを基準とした送信サイズを計算
+    s-=SIZE_OF_IPv4_TCPIP_HEADER;
+    //送信サイズよりMMSが小さければ、送信サイズを修正
+    if(i_inst->uip_connr.peer_mss<s){
+        s=i_inst->uip_connr.peer_mss;
+    }
+    //送信サイズよりpeerのウインドウサイズが小さければ修正
+    if(i_inst->uip_connr.peer_win<s){
+        s=i_inst->uip_connr.peer_win;
+    }
+    //送信サイズより、データサイズが小さければ、送信サイズを修正
+    if(i_len<s){
+        s=i_len;
+    }
+    //ACK番号の計算
+    next_ack=i_inst->uip_connr.snd_nxt32+s+((i_tcpf&(TCP_FIN|TCP_SYN))!=0x00?1:0);
+    txq->rto32=i_inst->uip_connr.current_rto32;
+    txq->tick_of_sent=NyLPC_cStopwatch_now();
+
+    //パケットの書き込み
+    setPacket(i_inst,&(txq->data),i_tcpf,i_buf,s);
+    //シーケンス番号の更新
+    i_inst->uip_connr.snd_nxt32=next_ack;
+    //Peerのウインドウサイズを更新
+    i_inst->uip_connr.peer_win-=s;
+    //ACK番号の返却
+    *o_ack=txq->ackno=NyLPC_HTONL(next_ack);
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    NyLPC_cUipService_sendIPv4Tx(buf);
+    return s;
+}
+/**
+ * RSTを1フレームだけ送信します。
+ * この関数は、クローズドステータスのソケットにしてからコールします。
+ * この関数は、アンロック状態でコールしてね。
+ */
+static void sendRst(NyLPC_TcTcpSocket_t* i_inst)
+{
+    NyLPC_TcIPv4Payload_t ipv4;
+    NyLPC_TUInt16 s;
+    void* buf;
+
+    NyLPC_Assert(i_inst->tcpstateflags==UIP_CLOSED);
+    //ペイロードライタの初期化
+    NyLPC_cIPv4Payload_initialize(&ipv4);
+
+    //IPヘッダ+10バイトくらい。
+    buf=NyLPC_cUipService_allocTxBuf((SIZE_OF_IPv4_TCPIP_HEADER)+5,&s);
+
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+    NyLPC_cIPv4Payload_setTxBuf(&ipv4,buf);
+    i_inst->uip_connr.snd_nxt32++;
+    setPacket(i_inst,&ipv4,TCP_RST|TCP_ACK,buf,0);
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+
+    NyLPC_cUipService_sendIPv4Tx(buf);
+    NyLPC_cUipService_releaseTxBuf(buf);
+    NyLPC_cIPv4Payload_finalize(&ipv4);
+    return;
+}
+
+
+
+/**
+ * 受信データを排他処理してバッファに書き込む。
+ * 十分な空き領域がない場合、失敗する。
+ * この関数は、ロックして実行してください。
+ */
+static NyLPC_TBool addRecvData(NyLPC_TcTcpSocket_t* i_inst,void* i_data,NyLPC_TUInt16 i_data_size)
+{
+    //受信データサイズを確認
+    if(NyLPC_cFifoBuffer_getSpace(&(i_inst->rxbuf))>=i_data_size){
+        //バッファに格納可能なら、格納。
+        NyLPC_cFifoBuffer_push(&(i_inst->rxbuf),i_data,i_data_size);
+    }else{
+        //エラー:ドロップする。
+        return NyLPC_TBool_FALSE;
+    }
+    return NyLPC_TBool_TRUE;
+}
+
+
+
+
+/**
+ * Public function
+ */
+
+/**
+ * この関数は、UIP_SYN_RCVDステータスのソケットを、ESTABLISHEDへ遷移させます。
+ * cTcpListener_listen関数を通過したインスタンスに実行してください。
+ * この関数は、アプリケーションが呼び出します。
+ * @return
+ *
+ */
+NyLPC_TBool NyLPC_cTcpSocket_accept(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_wait_in_msec)
+{
+    volatile NyLPC_TUInt8 f;
+    NyLPC_TUInt32 sq;
+    NyLPC_TcStopwatch_t sw;
+
+    NyLPC_cStopwatch_initialize(&sw);
+    //ステータスチェック
+    f=i_inst->tcpstateflags;
+    switch(f)
+    {
+    case UIP_ESTABLISHED:
+        return NyLPC_TBool_TRUE;
+    case UIP_SYN_RCVD:
+        //処理対象
+        break;
+    default:
+        return NyLPC_TBool_FALSE;
+    }
+    NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
+    if(sendWithRetransmit(i_inst,TCP_SYN|TCP_ACK,NULL,0,&sw,&sq)==0){
+        //ちょっと待つ。
+        NyLPC_cThread_yield();
+        //キューにあるTXが消えるのを待つ。
+        if(waitForTxRemove(i_inst,sq,&sw)){
+            //ACK受信に成功して、TXが消失
+            NyLPC_cStopwatch_finalize(&sw);
+            return NyLPC_TBool_TRUE;
+        }
+    }
+    //ロックして、強制的なステータス遷移
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+    f=i_inst->tcpstateflags;
+    if(f!=UIP_CLOSED){
+        i_inst->tcpstateflags=UIP_CLOSED;
+    }
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    //もし、強制CLOSE遷移であれば、RSTも送信。
+    if(f!=UIP_CLOSED){
+        sendRst(i_inst);
+    }
+    return NyLPC_TBool_FALSE;
+}
+
+
+/**
+ * この関数は、ソケットの受信バッファの読み取り位置と、読み出せるデータサイズを返却します。
+ * 関数はポインターを返却するだけで、バッファの読み取り位置をシークしません。
+ * シークするにはNyLPC_cTcpSocket_pseekを使います。
+ */
+NyLPC_TInt32 NyLPC_cTcpSocket_precv(NyLPC_TcTcpSocket_t* i_inst,const void** o_buf_ptr,NyLPC_TUInt32 i_wait_msec)
+{
+    volatile NyLPC_TUInt8 st;
+    NyLPC_TUInt16 rlen;
+    //タイマを生成
+    NyLPC_TcStopwatch_t sw;
+    NyLPC_cStopwatch_initialize(&sw);
+
+    //ESTABLISHED以外の場合は、エラー。
+    NyLPC_cStopwatch_setNow(&sw);
+    while(NyLPC_cStopwatch_elapseInMsec(&sw)<i_wait_msec)
+    {
+        //読み出しバッファ情報のコピー
+
+        //MUTEX LOCK
+        NyLPC_cMutex_lock(&(i_inst->_smutex));
+        st=i_inst->tcpstateflags;
+        rlen=NyLPC_cFifoBuffer_getLength(&(i_inst->rxbuf));
+        *o_buf_ptr=NyLPC_cFifoBuffer_getPtr(&(i_inst->rxbuf));
+        //MUTEX UNLOCK
+        NyLPC_cMutex_unlock(&(i_inst->_smutex));
+
+        //バッファが空の場合は、ステータスチェック。ESTABLISHEDでなければ、エラー(PASVCLOSE等の場合)
+        switch(st){
+        case UIP_ESTABLISHED:
+            if(rlen>0){
+                //バッファにパケットがあれば返却
+                NyLPC_cStopwatch_finalize(&sw);
+                return rlen;
+            }
+            break;
+        case UIP_CLOSE_WAIT:
+            if(rlen>0){
+                //バッファにパケットがあれば返却
+                NyLPC_cStopwatch_finalize(&sw);
+                return rlen;
+            }
+            //引き続きエラー処理
+        default:
+            //他の場合はエラー
+            NyLPC_cStopwatch_finalize(&sw);
+            return -1;
+        }
+        //タスクスイッチ
+        NyLPC_cThread_yield();
+    };
+    //規定時間内に受信が成功しなかった。
+    NyLPC_cStopwatch_finalize(&sw);
+    return 0;
+}
+/**
+ * 受信バッファをシークします。
+ * シーク後に、遅延ACKを送出します。
+ */
+void NyLPC_cTcpSocket_pseek(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt16 i_seek)
+{
+    NyLPC_TcIPv4Payload_t ipv4payload;
+    void* buf;
+    NyLPC_TUInt16 s;
+    NyLPC_ArgAssert(i_seek<=NyLPC_cFifoBuffer_getLength(&(i_inst->rxbuf)));
+    if(i_seek==0){
+        return;
+    }
+    //ペイロードライタの初期化
+    NyLPC_cIPv4Payload_initialize(&ipv4payload);
+
+    //ACK送信バッファの取得
+    buf=NyLPC_cUipService_allocTxBuf((SIZE_OF_IPv4_TCPIP_HEADER)+5,&s);
+
+    //MUTEX LOCK
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+
+    //受信バッファを読み出しシーク
+    NyLPC_cFifoBuffer_pop(&(i_inst->rxbuf),i_seek);
+    //ACKパケットの生成
+    NyLPC_cIPv4Payload_setTxBuf(&ipv4payload,buf);
+    setPacket(i_inst,&ipv4payload,TCP_ACK,buf,0);
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    //ACK送信
+    NyLPC_cUipService_sendIPv4Tx(buf);
+    NyLPC_cUipService_releaseTxBuf(buf);
+    //ペイロードライタの破棄
+    NyLPC_cIPv4Payload_finalize(&ipv4payload);
+}
+
+/**
+ * See header file.
+ */
+void* NyLPC_cTcpSocket_allocSendBuf(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt16 i_hint,NyLPC_TUInt16* o_buf_size,NyLPC_TUInt32 i_wait_in_msec)
+{
+    NyLPC_TUInt16 s;
+    void* buf;
+    NyLPC_TcStopwatch_t sw;
+
+    NyLPC_cStopwatch_initialize(&sw);
+    NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
+
+    //送信バッファを取得
+    buf=NyLPC_cUipService_allocTxBuf(i_hint+(SIZE_OF_IPv4_TCPIP_HEADER),&s);
+//ここ、要求サイズとpeerのwinのうち、小さいほうを割り当てたほうが良くない?
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+    //ペイロードがある場合のみ、相手のwindowサイズが0以上になるのを待つ。
+    while(i_inst->uip_connr.peer_win==0){
+        NyLPC_cMutex_unlock(&(i_inst->_smutex));
+        //時間切れならエラー。
+        if(NyLPC_cStopwatch_isExpired(&sw)){
+            NyLPC_cStopwatch_finalize(&sw);
+            return NULL;
+        }
+        NyLPC_cThread_yield();
+        NyLPC_cMutex_lock(&(i_inst->_smutex));
+    }
+    //送信バッファを基準とした送信サイズを計算
+    s-=SIZE_OF_IPv4_TCPIP_HEADER;
+    //送信サイズよりMMSが小さければ、送信サイズを修正
+    if(i_inst->uip_connr.peer_mss<s){
+        s=i_inst->uip_connr.peer_mss;
+    }
+    //送信サイズよりpeerのウインドウサイズが小さければ修正
+    if(i_inst->uip_connr.peer_win<s){
+        s=i_inst->uip_connr.peer_win;
+    }
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    //バッファサイズ確定。
+    *o_buf_size=s;
+    NyLPC_cStopwatch_finalize(&sw);
+    return (NyLPC_TUInt8*)buf+SIZE_OF_IPv4_TCPIP_HEADER;
+}
+/**
+ * See Header file.
+ */
+void NyLPC_cTcpSocket_releaseSendBuf(NyLPC_TcTcpSocket_t* i_inst,void* i_buf_ptr)
+{
+    NyLPC_cUipService_releaseTxBuf((NyLPC_TUInt8*)i_buf_ptr-SIZE_OF_IPv4_TCPIP_HEADER);
+}
+
+
+/**
+ * 事前にAllocしたTxパケットを送信します。
+ * このAPIはゼロコピー送信をサポートするためのものです。
+ * @param i_buf_ptr
+ * allocSendBufで取得したメモリを指定します。
+ * @return
+ * 関数が失敗した場合、i_buf_ptrは「開放されません。」
+ */
+NyLPC_TBool NyLPC_cTcpSocket_psend(NyLPC_TcTcpSocket_t* i_inst,void* i_buf_ptr,int i_len,NyLPC_TUInt32 i_wait_in_msec)
+{
+    struct NyLPC_TcTcpSocket_TxQItem* txq;
+    void* buf;
+    NyLPC_TcStopwatch_t sw;
+
+    //ESTABLISHEDでなければエラー
+    if(i_inst->tcpstateflags!=UIP_ESTABLISHED){
+        //ESTABLISHEDでなければエラー
+        return NyLPC_TBool_FALSE;
+    }
+    //送信データ0ならエラー
+    if(i_len<1){
+        return NyLPC_TBool_TRUE;
+    }
+    NyLPC_cStopwatch_initialize(&sw);
+    NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
+
+    //先頭ポインタは、i_buf-sizeof(SIZE_OF_IPv4_TCPIP_HEADER)固定
+    buf=(NyLPC_TUInt8*)i_buf_ptr-SIZE_OF_IPv4_TCPIP_HEADER;
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+    //送信キューの取得
+    txq=getTxQ(i_inst,&sw);
+    //送信キューが取れなかった。
+    if(txq==NULL){
+        //シーケンス番号をロールバックできないので、エラーとする。
+        NyLPC_cMutex_unlock(&(i_inst->_smutex));
+        NyLPC_cStopwatch_finalize(&sw);
+        return NyLPC_TBool_FALSE;
+    }
+    //ここから先はi_bufの所有権はインスタンスになってる。
+
+    //IPv4ペイロードの書き込み
+    NyLPC_cIPv4Payload_setTxBuf(&(txq->data),buf);
+    //allocをした時点でwin,mssは考慮されているので、そのままそうしんしる。
+    //ACK番号の計算
+    txq->rto32=i_inst->uip_connr.current_rto32;
+    txq->tick_of_sent=NyLPC_cStopwatch_now();
+    //パケットヘッダの生成
+    NyLPC_cIPv4Payload_initTcpTx(&(txq->data),0x05,((UIP_TCPH_LEN) / 4),i_len);
+    NyLPC_cIPv4Payload_setTcpTxHeaderByConnection(&(txq->data),&(i_inst->uip_connr),TCP_ACK|TCP_PSH);
+    NyLPC_cIPv4Payload_setTcpWnd(&(txq->data),NyLPC_cFifoBuffer_getSpace(&(i_inst->rxbuf)));
+    NyLPC_cIPv4Payload_closeTcpTxPacket(&(txq->data));
+    //シーケンス番号の更新
+    i_inst->uip_connr.snd_nxt32=i_inst->uip_connr.snd_nxt32+i_len;
+    //Peerのウインドウサイズを更新
+    i_inst->uip_connr.peer_win-=i_len;
+    //ACK番号の返却
+    txq->ackno=NyLPC_HTONL(i_inst->uip_connr.snd_nxt32);
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    NyLPC_cUipService_sendIPv4Tx(buf);
+    NyLPC_cStopwatch_finalize(&sw);
+    return NyLPC_TBool_TRUE;
+}
+
+/**
+ * See header file.
+ */
+NyLPC_TInt16 NyLPC_cTcpSocket_send(NyLPC_TcTcpSocket_t* i_inst,const void* i_buf_ptr,int i_len,NyLPC_TUInt32 i_wait_in_msec)
+{
+    NyLPC_TUInt32 sq;
+    NyLPC_TInt32 r;
+    NyLPC_TcStopwatch_t sw;
+    //ESTABLISHEDでなければエラー
+    if(i_inst->tcpstateflags!=UIP_ESTABLISHED){
+        //ESTABLISHEDでなければエラー
+        return -1;
+    }
+    if(i_len<1){
+        return 0;
+    }
+    NyLPC_cStopwatch_initialize(&sw);
+    NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
+    //PSHACKを送信する。
+    r=sendWithRetransmit(i_inst,TCP_PSH|TCP_ACK,i_buf_ptr,i_len,&sw,&sq);
+    NyLPC_cStopwatch_finalize(&sw);
+    //失敗時も何もしない?強制CLOSEしなくて平気?
+    return r;
+}
+
+
+void NyLPC_cTcpSocket_close(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_wait_in_msec)
+{
+    NyLPC_TcStopwatch_t sw;
+    volatile NyLPC_TUInt8 f;
+    NyLPC_TUInt32 sq;
+    NyLPC_cStopwatch_initialize(&sw);
+    NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+
+    f=i_inst->tcpstateflags;
+    //ステータスチェック
+    switch(f)
+    {
+    case UIP_CLOSED:
+        //閉じている。
+        goto ReturnWithUnlock;
+    case UIP_ESTABLISHED:
+        //アクティブクローズ。
+        i_inst->tcpstateflags=UIP_FIN_WAIT_1;
+        //送信のために一旦解除
+        NyLPC_cMutex_unlock(&(i_inst->_smutex));
+        //FINの送信
+        if(sendWithRetransmit(i_inst,TCP_FIN|TCP_ACK,NULL,0,&sw,&sq)==0){
+            //ちょっと待つ。
+            NyLPC_cThread_yield();
+            //TXの消去待ち
+            if(waitForTxRemove(i_inst,sq,&sw)){
+                //再ロック
+                NyLPC_cMutex_lock(&(i_inst->_smutex));
+                //タイムアウトするか、UIP_CLOSED、もしくはTIME_WAITに遷移するのを待つ。(遷移はRxprocで自動的に実行。)
+                while(!NyLPC_cStopwatch_isExpired(&sw)){
+                    switch(i_inst->tcpstateflags)
+                    {
+                    case UIP_TIME_WAIT:
+                        i_inst->tcpstateflags=UIP_CLOSED;
+                    case UIP_CLOSED:
+                        NyLPC_Assert(i_inst->txbuf.rp==i_inst->txbuf.wp);
+                        //成功。
+                        goto ReturnWithUnlock;
+                    case UIP_FIN_WAIT_1:
+                    case UIP_FIN_WAIT_2:
+                    case UIP_CLOSING:
+                        //一時的なアンロック
+                        NyLPC_cMutex_unlock(&(i_inst->_smutex));
+                        NyLPC_cThread_yield();
+                        NyLPC_cMutex_lock(&(i_inst->_smutex));
+                    default:
+                        break;
+                    }
+                }
+                NyLPC_cMutex_unlock(&(i_inst->_smutex));
+            }
+        }
+        break;
+    case UIP_CLOSE_WAIT:
+        //LAST_ACKへ遷移
+        i_inst->tcpstateflags=UIP_LAST_ACK;
+        //送信のために一旦解除
+        NyLPC_cMutex_unlock(&(i_inst->_smutex));
+        if(sendWithRetransmit(i_inst,TCP_FIN|TCP_ACK,NULL,0,&sw,&sq)==0){
+            //ちょっと待つ。
+            NyLPC_cThread_yield();
+            //TXの消去待ち
+            if(waitForTxRemove(i_inst,sq,&sw)){
+                //再ロック
+                NyLPC_cMutex_lock(&(i_inst->_smutex));
+                //TX消去後にCLOSEDに遷移していればOK
+                if(i_inst->tcpstateflags==UIP_CLOSED)
+                {
+                    NyLPC_Assert(i_inst->txbuf.rp==i_inst->txbuf.wp);
+                    goto ReturnWithUnlock;
+                }
+                NyLPC_cMutex_unlock(&(i_inst->_smutex));
+            }
+        }
+        //エラー。RSTで切断。
+        break;
+    default:
+        NyLPC_cMutex_unlock(&(i_inst->_smutex));
+        NyLPC_Warning();
+        break;
+    }
+    if(i_inst->_smutex._lock_count>0){
+        NyLPC_Warning();
+    }
+    //このパスに到達するのは、FIN送信/ACKに成功したにも拘らず、規定時間内にCLOSEDに遷移しなかった場合。
+    //コネクションを強制遷移して、RST
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+    f=i_inst->tcpstateflags;
+    if(f!=UIP_CLOSED){
+        i_inst->tcpstateflags=UIP_CLOSED;
+    }
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    //もし、強制CLOSE遷移であれば、RSTも送信。
+    if(f!=UIP_CLOSED){
+        sendRst(i_inst);
+    }
+    NyLPC_cStopwatch_finalize(&sw);
+    return;
+ReturnWithUnlock:
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    NyLPC_cStopwatch_finalize(&sw);
+    return;
+}
+
+/**
+ * 定期的に実行する関数。最低でも1s単位で実行してください。
+ * 動作
+ */
+void NyLPC_cTcpSocket_periodic(
+    NyLPC_TcTcpSocket_t* i_inst)
+{
+    int i;
+    struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
+    NyLPC_TcStopwatch_t sw;
+    NyLPC_TUInt32 now;
+    int rp;
+    NyLPC_cStopwatch_initialize(&sw);
+    now=NyLPC_cStopwatch_now();
+    //MUTEX LOCK
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+    if(i_inst->tcpstateflags==UIP_CLOSED)
+    {
+        //CLOSEDなら、バッファ開放。
+        resetTxQWithUnlock(i_inst);
+    }else if(i_inst->txbuf.rp==i_inst->txbuf.wp){
+        //再送信パケットがなければ何もしないよ。
+        NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    }else if(i_inst->uip_connr.peer_win==0){
+        //peer_winが0の場合は何もしない。
+        NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    }else{
+        //再送信処理
+        rp=i_inst->txbuf.rp;
+        NyLPC_cStopwatch_set(&sw,q[rp].tick_of_sent);
+        if(NyLPC_cStopwatch_elapseInMsec(&sw)>q[rp].rto32){
+            //最古のパケットの送信時間をチェックして、タイムアウトが発生したら、再送時間と送信時刻をセット
+            //最古パケットRTOを2倍。
+            q[rp].rto32*=2;
+            for(i=rp;i!=i_inst->txbuf.wp;i=(i+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ){
+                q[i].tick_of_sent=now;
+            }
+            if(q[rp].rto32>UIP_IP_RTO_MAX_RTO){
+                //最古のRTOが64秒を超えたら、CLOSED
+                i_inst->tcpstateflags =UIP_CLOSED;
+                resetTxQWithUnlock(i_inst);
+                sendRst(i_inst);
+            }else{
+                //規定時間内なら、再送処理
+                for(i=rp;i!=i_inst->txbuf.wp;i=(i+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ){
+                    NyLPC_cUipService_sendIPv4Tx(NyLPC_cIPv4Payload_getBuf(&(q[i].data)));
+                }
+                NyLPC_cMutex_unlock(&(i_inst->_smutex));
+            }
+        }else{
+            NyLPC_cMutex_unlock(&(i_inst->_smutex));
+        }
+    }
+    NyLPC_cStopwatch_finalize(&sw);
+    return;
+}
+
+
+/**
+ * この関数は、rxパケットを処理して、ソケットの状態を更新します。
+ * uipサービスタスクが実行する関数です。
+ * o_ippのペイロードに、応答ペイロードを設定することがあります。
+ * この関数はNyLPC_cTcpSocket_periodicと排他実行すること。
+ */
+NyLPC_TBool NyLPC_cTcpSocket_parseRx(
+    NyLPC_TcTcpSocket_t* i_inst,
+    NyLPC_TcIPv4Payload_t* o_ipp)
+{
+    int i,s;
+    NyLPC_TUInt16 tmp16;
+    NyLPC_TUInt16 data_size;
+    NyLPC_TUInt8 in_tcpflag=o_ipp->payload.tcp->flags;
+    void* tcp_data_offset;
+    NyLPC_TBool is_new_packet;
+    int num_of_noack;
+    void* dlist[NyLPC_TcTcpSocket_NUMBER_OF_TXQ];
+    NyLPC_TBool ret;
+
+    //パラメータの計算
+
+    tmp16=NyLPC_TTcpHeader_getHeaderLength(o_ipp->payload.tcp);
+    //TCPペイロードの長さは、IPパケットの長さ-(IPヘッダ+TCPヘッダ)
+    data_size=NyLPC_TIPv4Header_getPacketLength(o_ipp->header)-NyLPC_TIPv4Header_getHeaderLength(o_ipp->header)-tmp16;
+    //TCPデータオフセット
+    tcp_data_offset=o_ipp->payload.rawbuf+tmp16;
+
+    //インスタンスをロックする。
+    NyLPC_cMutex_lock(&(i_inst->_smutex));
+
+    //RSTのチェック。RST受信時は、状態にかかわらず、CLOSEDステータスに移行する。
+    if (in_tcpflag & TCP_RST)
+    {
+        i_inst->tcpstateflags =UIP_CLOSED;
+        goto DROP;
+    }
+
+
+    is_new_packet=NyLPC_ntohl(o_ipp->payload.tcp->seqno32)==i_inst->uip_connr.rcv_nxt32;
+
+
+    //OPTIONの反映
+
+    //MSSの取得
+    if(NyLPC_TTcpHeader_getTcpMmsOpt(o_ipp->payload.tcp,&tmp16)){
+        //取得で着たら更新
+        i_inst->uip_connr.peer_mss=tmp16;
+    }
+    //受信パケットを元に、未ACKパケットの数を計算
+    num_of_noack=getNumOfSending(i_inst,o_ipp->payload.tcp->ackno32);//i_inst->txbuf.num_of_txq;
+
+    //ステータス毎のACK応答
+    switch(i_inst->tcpstateflags)
+    {
+    case UIP_SYN_RCVD:
+        //ACKを受信したら、ESTABLISHEDへ。
+        //すべてのパケットをACKしたかで判定。()
+        if(num_of_noack==0){
+            i_inst->tcpstateflags=UIP_ESTABLISHED;
+        }else{
+            //それ以外のパケットはドロップする。
+            break;//goto DROP;
+        }
+        //新しいパケットがなければ、無応答
+        if(!is_new_packet){
+            break;//goto DROP;
+        }
+        //引き続き、ESTABLISHEDの処理へ。
+    case UIP_ESTABLISHED:
+        if(data_size>0){
+            if(is_new_packet){
+                if(addRecvData(i_inst,tcp_data_offset,data_size)){
+                    //通常のACK返却
+                    i_inst->uip_connr.rcv_nxt32+=data_size;
+                }else{
+                    //失敗したときは必要に応じて単純ACK
+                }
+            }
+        }
+        //MSSとWNDの更新
+        i_inst->uip_connr.peer_mss = i_inst->uip_connr.default_mss;
+        //どちらにしろ、ACK送信
+        if(is_new_packet && (in_tcpflag & TCP_FIN)){
+            //FINがあるときは、ステータスをCLOSE_WAITへセットして、ACKを返す。
+            i_inst->tcpstateflags = UIP_CLOSE_WAIT;
+            i_inst->uip_connr.rcv_nxt32++;
+        }
+        break;
+    case UIP_CLOSE_WAIT:
+        //必要に応じたACK応答
+        break;
+    case UIP_LAST_ACK:
+        //ACK(by FIN)が得られたなら、CLOSEDへ。
+        if(num_of_noack==0){
+            i_inst->tcpstateflags=UIP_CLOSED;
+        }
+        //必要に応じたACK応答
+        break;
+    case UIP_FIN_WAIT_1:
+        //FIN受信->CLOSINGへ
+        if(is_new_packet && (in_tcpflag & TCP_FIN)){
+            i_inst->uip_connr.rcv_nxt32++;
+            if(num_of_noack==0){
+                //FINとACKを受信
+                i_inst->tcpstateflags=UIP_TIME_WAIT;
+            }else{
+                //FINのみ
+                i_inst->tcpstateflags=UIP_CLOSING;
+            }
+        }else if(num_of_noack==0){
+            //ACKのみ
+            i_inst->tcpstateflags=UIP_FIN_WAIT_2;
+        }
+        //必要に応じたACK応答
+        break;
+    case UIP_FIN_WAIT_2:
+        //FIN受信->TIME_WAITへ(pureACK)
+        if(is_new_packet && (in_tcpflag & TCP_FIN)){
+            i_inst->uip_connr.rcv_nxt32++;
+            i_inst->tcpstateflags=UIP_TIME_WAIT;
+        }
+        break;
+    case UIP_CLOSING:
+        //ACK受信したら、TIME_WAITへ
+        if(num_of_noack==0){
+            i_inst->tcpstateflags=UIP_TIME_WAIT;
+        }
+        break;
+    case UIP_CLOSED:
+        //何もできない。何もしない。
+        break;//goto DROP;
+    case UIP_TIME_WAIT:
+        //最終ACKを送り続ける。
+        break;
+    default:
+        goto DROP;
+    }
+    //ウインドウサイズを更新
+    i_inst->uip_connr.peer_win=NyLPC_ntohs(o_ipp->payload.tcp->wnd16);
+
+    //送信キューから、Peerが受信したデータを削除する。
+    if(in_tcpflag & TCP_ACK){
+        //再送パケットキューから送信済みのデータを回収(後で開放)
+        NyLPC_Trace();
+        s=updateTxQByIndex(i_inst,o_ipp->payload.tcp->ackno32,dlist);
+        NyLPC_Trace();
+    }else{
+        s=0;
+    }
+    //新しいパケットがきた場合は、再送キューのACKを更新する。
+    if(is_new_packet){
+        //再送キューのACKを更新
+        updateTxAck(i_inst,NyLPC_htonl(i_inst->uip_connr.rcv_nxt32));
+    }
+
+    //送信キューのない
+    ret=NyLPC_TBool_FALSE;
+    if(((in_tcpflag&(TCP_FIN|TCP_SYN))!=0x00) ||
+        ((!is_new_packet) && (data_size>0)))
+    {
+        setPacket(i_inst,o_ipp,TCP_ACK,NULL,0);
+        ret=NyLPC_TBool_TRUE;
+    }else{
+        ret=NyLPC_TBool_FALSE;
+    }
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+    //取り外したTXメモリの開放
+    for(i=0;i<s;i++){
+        //取り外したTXメモリを開放
+        NyLPC_cUipService_releaseTxBuf(dlist[i]);
+    }
+NyLPC_Trace();
+    return ret;
+DROP:
+    //ACKしたパケットを送信キューから削除
+    NyLPC_cMutex_unlock(&(i_inst->_smutex));
+NyLPC_Trace();
+    return NyLPC_TBool_FALSE;
+}
+