This is Webservice SDK for mbed. LPCXpresso1769/LPC1768/FRDM-K64F/LPC4088

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

libMiMic(MiMic library for mbed)は、WebService機能を提供するSDKです。 mbedでWebAPIに対応したネットワークデバイスを簡単に作ることが出来ます。

libMiMicはMiMic projectで開発しています。MiMic projectについてはこちらをご覧ください。 http://nyatla.jp/mimic/wp/

構成

libMiMicはmbedRTOS上で動作し、ユーザアプリケーションにAPIを提供します。コアAPIはC言語で記述されていますが、使用頻度の高いものについてはmbed向けのC++APIが準備されています。

/media/uploads/nyatla/libmimic-sdk.png

※libMiMicはmbedの標準イーサネットドライバをしようしていません。

標準イーサネットドライバと同時に使用することはできません。

  • MiMicIP - IPv4スタックです。レテンシとメモリ消費量を抑えたuipベースのライブラリです。
  • ARP/ICMP/UDP/TCP - 基礎的なソケットAPIを提供します。APIは独自です。
  • HTTP/1.1 Server - HTTP/1.1に対応したサーバです。マルチセッション・Chunked・持続性接続に対応しています。
  • HTTP Modules - HTTP/1.1の機能モジュールです。以下のモジュールがあります。
    • ROM file provider - ROMに格納したファイルイメージを公開します。
    • File system provider - mbedファイルシステムを公開します。
    • Onchip configuration - プログラムフラッシュを利用して設定を保存します。
    • MiMicVM processor - RPCリクエスト(MiMicVM)を処理します。
    • FileUpload - ファイルアップロードを受け取ります。
    • URL decoder - HTTPリクエストを解析します。
    • UPnP handler -UPnPメッセージを処理します。
    • WebSocket - Websocketサーバです。
  • mDNS - マルチキャストDNSサービスです。
  • UPnP - UPnP/1.0の機能を提供します。UPnP handlerと協調して動作します。(現在はデバイス探索(SSDP)・デスクリプション(Description)のみ実装してあります。)
  • DHCP/APIPA - ゼロコンフィギュレーション用のモジュールです。
  • HTTP/1.1 Client
  • mbed C++ class library - mbed向けのC++CPIです。C言語のものより簡単です。

対応機種

  • mbed(mbed LPC1768)
  • LPCXpresso1769

プログラム

Import programMiMicRemoteMCU-for-Mbed

MiMic RemoteMCU for mbed. This program provides MCU control API over REST API. It can control MCU from Javascript,PHP or any HTTP rest client directly. And, The application has self development environment.

Import programMbedFileServer

The program publishes files at local directory and SD filesystem. It is a full-fledged webServer somewhat.

サンプル

Import programMiMicSimpleHttpd

This is a simplest HTTP server made ​​of libMiMic. It will echo back a request path.

Import programUPnPBasicDevice

Simplest UPnP basic device example. This program to run UPnP basic device on the mbed.

Import programWebSocketSample

MiMicSDK Websocket module sample program.

Import programHttpClientSamlpe

A http client sample program.

Import programTcpSocketClientSamlpe

MiMicSDK Tcp client socket sample program.

Import programUdpSocketSamlpe

Udp socket sample program. This program will send back the received packet.

チュートリアル

English

libMiMic(MiMic library for mbed) is SDK which provides Webservice functions. It can be created networking device easily using mbed.

See more MiMic information, See MiMic project website. http://nyatla.jp/mimic/wp/

Structure

libMiMic run on mbed RTOS and provides networking API to user application. This library has C++ class API for the mbed, and low-level C language API.

/media/uploads/nyatla/libmimic-sdk.png

For WebService general, it can be written in a simple C + + API.

libMiMic does not have the standard Ethernet driver of mbed. It is not possible that will be used with the standard Ethernet driver.

  • MiMicIP - IPv4 protocol stack. This is based uip which is reduced memory and latency.
  • ARP / ICMP / UDP / TCP - Those are provide basic IP protocols.
  • HTTP/1.1 Server - The Http server compatible HTTP/1.1. It supports multi-session, chunked transport, persistent connection.
  • HTTP Modules - There are addon-module for HTTP server. The following modules.
    • ROM file module - Publish the file images in ROM.
    • File system module - Publish thefiles in mbed file system.
    • Onchip configuration module - To save the network settings to the program flash via REST.
    • MiMicVM module - To handle the (MiMicVM) RPC request.
    • FileUpload module - Accept a file via HTTP POST.
    • URL dedoce module - A versatility URL decoder.
    • UPnP handle module - To handle UPnP messages.
    • UPnP - This provides UPnP/1.0 device functions. It works together with UPnP handler.
    • Websocket - websocket (version13) server
  • mDNS Service - DNS-SD protocol server.
  • UPnP - This provides UPnP/1.0 device functions which works with UPnP handler. (You have been implemented (SSDP) ? description only (Description) device search now.) It is a module zero configuration for - DHCP / APIPA. mbed C + + class library - C of mbed for + + is the CPI. It is simple than that of the C language.
  • DHCP/APIPA - It support zero-cpnfigulation.
  • mbed C++ class library. Almost APIs for Web applications are available.
  • HTTP/1.1 Client

Supported target

  • mbed(mbed LPC1768)
  • LPCXpresso1769

Application

Import programMiMicRemoteMCU-for-Mbed

MiMic RemoteMCU for mbed. This program provides MCU control API over REST API. It can control MCU from Javascript,PHP or any HTTP rest client directly. And, The application has self development environment.

Import programMbedFileServer

The program publishes files at local directory and SD filesystem. It is a full-fledged webServer somewhat.

Sample

Import programMiMicSimpleHttpd

This is a simplest HTTP server made ​​of libMiMic. It will echo back a request path.

Import programUPnPBasicDevice

Simplest UPnP basic device example. This program to run UPnP basic device on the mbed.

Import programWebSocketSample

MiMicSDK Websocket module sample program.

Import programHttpClientSamlpe

A http client sample program.

Import programTcpSocketClientSamlpe

MiMicSDK Tcp client socket sample program.

Import programUdpSocketSamlpe

Udp socket sample program. This program will send back the received packet.

Tutorial

core/uip/NyLPC_cTcpSocket.c

Committer:
nyatla
Date:
2013-09-13
Revision:
57:bc4330dfa62f
Parent:
43:a182f2b5ff41
Child:
58:03b89038b21a

File content as of revision 57:bc4330dfa62f:

/*********************************************************************************
 * 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

/**
 * TCPのRTOの最大値。
 * ms単位である。
 * defaultは64SEC
 */
#define UIP_IP_RTO_MAX_RTO 64000
/**
 * TCPのRTOの初期値。
 * ms単位である。
 * 伝送路の特性に合わせて調整すること。
 */
#define UIP_TCP_RTO_INITIAL 3000

/**
 * CONNECTION時のRTO
 */
#define UIP_TCP_RTO_CONNECTION_INITIAL 200

/**
 * 下限値
 */
#define UIP_TCP_RTO_MINIMUM 100


/**
 * 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

//#define lockResource(i_inst) NyLPC_cMutex_lock(&((i_inst)->_smutex))
//#define unlockResource(i_inst) NyLPC_cMutex_unlock(&((i_inst)->_smutex))
#define lockResource(i_inst) NyLPC_cMutex_lock(NyLPC_cIPv4_getSockMutex(((i_inst)->_super._parent_ipv4)))
#define unlockResource(i_inst) NyLPC_cMutex_unlock(NyLPC_cIPv4_getSockMutex(((i_inst)->_super._parent_ipv4)))

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;
    //ロック解除
    unlockResource(i_inst);
    //セーブしたバッファを開放
    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_TCP_RTO_MINIMUM){
        new_rto=UIP_TCP_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){
            //一時的なアンロック
            unlockResource(i_inst);
            //タスクスイッチ
            NyLPC_cThread_yield();
            //ロック
            lockResource(i_inst);
            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;
    NyLPC_cBaseSocket_initialize(&(i_inst->_super),NyLPC_TcBaseSocket_TYPEID_TCP_SOCK);
    //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)));//個別Mutex
//  i_inst->_smutex=NyLPC_cIPv4_getSockMutex(&(srv->_tcpv4));//共有Mutex
    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->_super));
}
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->_super))){
        //削除失敗、それは死を意味する。
        NyLPC_Abort();
    }
    //開放漏れの保険
    if(i_inst->txbuf.rp!=i_inst->txbuf.wp){
        lockResource(i_inst);
        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));
    NyLPC_cBaseSocket_finalize(&(i_inst->_super));
    return;
}



NyLPC_TBool NyLPC_cTcpSocket_listenSyn(NyLPC_TcTcpSocket_t* i_inst,const struct NyLPC_TTcpSocketSynParam* i_lq,NyLPC_TUInt16 i_lport)
{
//  NyLPC_Assert(NyLPC_cMutex_isLocked(i_inst->_smutex));
    lockResource(i_inst);
    //ソケットが無効であること。
    if(i_inst->tcpstateflags==UIP_CLOSED)
    {
        //localipとdefault_mmsは別枠で設定
        /* Fill in the necessary fields for the new connection. */
        i_inst->uip_connr.current_rto32 = UIP_TCP_RTO_INITIAL;
        i_inst->uip_connr.lport = i_lport;
        i_inst->uip_connr.rport = i_lq->rport;
        i_inst->uip_connr.ripaddr=i_lq->srcaddr;
        i_inst->uip_connr.snd_nxt32=iss32;
        /* rcv_nxt should be the seqno from the incoming packet + 1. */
        i_inst->uip_connr.rcv_nxt32= i_lq->rcv_nxt32;
        //MSSの設定
        i_inst->uip_connr.peer_mss=(i_lq->mss!=0)?i_lq->mss:i_inst->uip_connr.default_mss;
        i_inst->uip_connr.peer_win=0;
        NyLPC_cFifoBuffer_clear(&(i_inst->rxbuf));
        //ここでステータスがかわる。
        i_inst->tcpstateflags = UIP_SYN_RCVD;
        //前回のデータが残っていた場合の保険
        if(i_inst->txbuf.rp!=i_inst->txbuf.wp){
            resetTxQWithUnlock(i_inst);
        }else{
            unlockResource(i_inst);
        }
        return NyLPC_TBool_TRUE;
    }
    unlockResource(i_inst);
    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;
    lockResource(i_inst);
    while(!NyLPC_cStopwatch_isExpired(i_timer)){
        //パケットが送信中か調べる。
        if(!isPacketAcked(i_inst,i_sq)){
            //まだある場合は、タスクスイッチを繰り返して消失を待つ。
            unlockResource(i_inst);
            NyLPC_cThread_yield();
            lockResource(i_inst);
            continue;
        }
        //なくなった場合は、原因を調べる。
        f=i_inst->tcpstateflags;
        unlockResource(i_inst);
        return (f==UIP_CLOSED)?NyLPC_TBool_FALSE:NyLPC_TBool_TRUE;
    }
    unlockResource(i_inst);
    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;
    //送信バッファを取得
    //@bug バッファが取れるまで通信がブロックするの。ここはなんとかしないと。
    for(;;){
        buf=NyLPC_cUipService_allocTxBuf(i_len+(SIZE_OF_IPv4_TCPIP_HEADER),&s);
        if(buf!=NULL){
            break;
        }
        //タイムアウト確認
        if(NyLPC_cStopwatch_isExpired(i_timer)){
            return -1;
        }
    };
    lockResource(i_inst);
    //ペイロードがある場合のみ、相手のwindowサイズが0以上になるのを待つ。
    if(i_len>0){
        while(i_inst->uip_connr.peer_win==0){
            unlockResource(i_inst);
            //時間切れならエラー。
            if(NyLPC_cStopwatch_isExpired(i_timer)){
                return -1;
            }
            NyLPC_cThread_yield();
            lockResource(i_inst);
        }
    }
    //送信キューの取得
    txq=getTxQ(i_inst,i_timer);
    //送信キューが取れなかった。
    if(txq==NULL){
        //シーケンス番号をロールバックできないので、エラーとする。
        unlockResource(i_inst);
        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);
    unlockResource(i_inst);
    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バイトくらい。
    //@bug バッファが取れるまで通信がブロックするの。ここはなんとかしないと。
    do{
        buf=NyLPC_cUipService_allocTxBuf((SIZE_OF_IPv4_TCPIP_HEADER)+5,&s);
    }while(buf==NULL);
    lockResource(i_inst);
    NyLPC_cIPv4Payload_setTxBuf(&ipv4,buf);
    i_inst->uip_connr.snd_nxt32++;
    setPacket(i_inst,&ipv4,TCP_RST|TCP_ACK,buf,0);
    unlockResource(i_inst);

    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
 */

NyLPC_TBool NyLPC_cTcpSocket_connect(NyLPC_TcTcpSocket_t* i_inst,struct NyLPC_TIPv4Addr* i_addr,NyLPC_TUInt16 i_peer_port,NyLPC_TUInt32 i_wait_in_msec)
{
    volatile NyLPC_TUInt8 f;
    NyLPC_TUInt32 sq;
    NyLPC_TcStopwatch_t sw;
    NyLPC_TUInt16 lport;
    lockResource(i_inst);
    //ソケットが無効であること。
    if(i_inst->tcpstateflags!=UIP_CLOSED)
    {
        NyLPC_OnErrorGoto(Error);
    }
    //ポート番号の取得(lockResourceが他のソケットと共有なので、重複ポートの割当は起こりえない。でもちょっと注意して)
    lport=NyLPC_htons(NyLPC_cIPv4_getNewPortNumber(i_inst->_super._parent_ipv4));
    if(lport==0){
        NyLPC_OnErrorGoto(Error);
    }
    //connectの為の準備

    //localipとdefault_mmsは別枠で設定
    /* Fill in the necessary fields for the new connection. */
    i_inst->uip_connr.current_rto32 = UIP_TCP_RTO_CONNECTION_INITIAL;//RTOを短くしてARP発行時の再接続短縮を期待する。
    i_inst->uip_connr.lport = lport;
    i_inst->uip_connr.rport = NyLPC_htons(i_peer_port);
    i_inst->uip_connr.ripaddr=*i_addr;
    i_inst->uip_connr.snd_nxt32=iss32;//should be random
    /* rcv_nxt should be the seqno from the incoming packet + 1. */
    i_inst->uip_connr.rcv_nxt32=0;
    //MSSの設定
    i_inst->uip_connr.peer_mss=i_inst->uip_connr.default_mss;
    i_inst->uip_connr.peer_win=1;//periodicの再送信を期待するために相手のWindowサイズは1と仮定する。
    NyLPC_cFifoBuffer_clear(&(i_inst->rxbuf));
    //ここでステータスがかわる。
    i_inst->tcpstateflags = UIP_SYN_SENT;
    //前回のデータが残っていた場合の保険
    if(i_inst->txbuf.rp!=i_inst->txbuf.wp){
        resetTxQWithUnlock(i_inst);
    }else{
        unlockResource(i_inst);
    }

    NyLPC_cStopwatch_initialize(&sw);

    NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
    if(sendWithRetransmit(i_inst,TCP_SYN,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;
        }
    }
    //ロックして、強制的なステータス遷移
    lockResource(i_inst);
    f=i_inst->tcpstateflags;
    if(f!=UIP_CLOSED){
        //もし、強制CLOSE遷移であれば、RSTも送信。
        i_inst->tcpstateflags=UIP_CLOSED;
        unlockResource(i_inst);
        sendRst(i_inst);
    }else{
        unlockResource(i_inst);
    }
    return NyLPC_TBool_FALSE;
Error:
    unlockResource(i_inst);
    return NyLPC_TBool_FALSE;
}

/**
 * この関数は、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;
        }
    }
    //ロックして、強制的なステータス遷移
    lockResource(i_inst);
    f=i_inst->tcpstateflags;
    if(f!=UIP_CLOSED){
        //もし、強制CLOSE遷移であれば、RSTも送信。
        i_inst->tcpstateflags=UIP_CLOSED;
        unlockResource(i_inst);
        sendRst(i_inst);
    }else{
        unlockResource(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
        lockResource(i_inst);
        st=i_inst->tcpstateflags;
        rlen=NyLPC_cFifoBuffer_getLength(&(i_inst->rxbuf));
        *o_buf_ptr=NyLPC_cFifoBuffer_getPtr(&(i_inst->rxbuf));
        //MUTEX UNLOCK
        unlockResource(i_inst);

        //バッファが空の場合は、ステータスチェック。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送信バッファの取得
    //@bug バッファが取れるまで通信がブロックするの。ここはなんとかしないと。
    do{
        buf=NyLPC_cUipService_allocTxBuf((SIZE_OF_IPv4_TCPIP_HEADER)+5,&s);
    }while(buf==NULL);

    //MUTEX LOCK
    lockResource(i_inst);

    //受信バッファを読み出しシーク
    NyLPC_cFifoBuffer_pop(&(i_inst->rxbuf),i_seek);
    //ACKパケットの生成
    NyLPC_cIPv4Payload_setTxBuf(&ipv4payload,buf);
    setPacket(i_inst,&ipv4payload,TCP_ACK,buf,0);
    unlockResource(i_inst);
    //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);

    //送信バッファを取得
    //@bug バッファが取れるまで通信がブロックするの。ここはなんとかしないと。
    for(;;){
        //ESTABLISHED以外に非同期遷移
        if(i_inst->tcpstateflags!=UIP_ESTABLISHED){
            NyLPC_cStopwatch_finalize(&sw);
            return NULL;
        }
        buf=NyLPC_cUipService_allocTxBuf(i_hint+(SIZE_OF_IPv4_TCPIP_HEADER),&s);
        if(buf!=NULL){
            break;
        }
        //タイムアウト時もエラー
        if(NyLPC_cStopwatch_isExpired(&sw)){
            NyLPC_cStopwatch_finalize(&sw);
            return NULL;
        }
    }

//@todo 前段処理と順番を入れ替えて、要求サイズとpeerのwinのうち、小さいほうを割り当てたほうが良くない?
//ここで相手のwin待ちをする理由は、相手に確実に受け取れるサイズを決定する為。
    lockResource(i_inst);
    //ペイロードがある場合のみ、相手のwindowサイズが0以上になるのを待つ。
    while(i_inst->uip_connr.peer_win==0){
        unlockResource(i_inst);
        //ESTABLISHED以外に非同期遷移 orタイムアウト確認
        if(NyLPC_cStopwatch_isExpired(&sw)||(i_inst->tcpstateflags!=UIP_ESTABLISHED)){
            NyLPC_cUipService_releaseTxBuf(buf);
            NyLPC_cStopwatch_finalize(&sw);
            return NULL;
        }
        NyLPC_cThread_yield();
        lockResource(i_inst);
    }
    //送信バッファを基準とした送信サイズを計算
    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;
    }
    unlockResource(i_inst);
    //バッファサイズ確定。
    *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){
        NyLPC_cTcpSocket_releaseSendBuf(i_inst,i_buf_ptr);
        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;
    lockResource(i_inst);
    //送信キューの取得
    txq=getTxQ(i_inst,&sw);
    //送信キューが取れなかった。
    if(txq==NULL){
        //シーケンス番号をロールバックできないので、エラーとする。
        unlockResource(i_inst);
        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);
    unlockResource(i_inst);
    NyLPC_cUipService_sendIPv4Tx(buf);
    NyLPC_cStopwatch_finalize(&sw);
    return NyLPC_TBool_TRUE;
}

/**
 * See header file.
 */
NyLPC_TInt32 NyLPC_cTcpSocket_send(NyLPC_TcTcpSocket_t* i_inst,const void* i_buf_ptr,NyLPC_TInt32 i_len,NyLPC_TUInt32 i_wait_in_msec)
{
    NyLPC_TInt16 hint;
    NyLPC_TUInt16 s;
    void* buf;
    if(i_len<1){
        return 0;
    }
    hint=(i_len>32767)?32767:i_len;
    buf=NyLPC_cTcpSocket_allocSendBuf(i_inst,hint,&s,i_wait_in_msec);
    if(buf==NULL){
        return -1;
    }
    //送信サイズの計算
    s=((NyLPC_TInt32)s<i_len)?s:(NyLPC_TUInt16)i_len;
    memcpy(buf,i_buf_ptr,s);
    if(!NyLPC_cTcpSocket_psend(i_inst,buf,s,i_wait_in_msec)){
        NyLPC_cTcpSocket_releaseSendBuf(i_inst,buf);
        return -1;//error
    }
    return s;
}


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);
    lockResource(i_inst);

    f=i_inst->tcpstateflags;
    //ステータスチェック
    switch(f)
    {
    case UIP_CLOSED:
        //閉じている。
        goto ReturnWithUnlock;
    case UIP_ESTABLISHED:
        //アクティブクローズ。
        i_inst->tcpstateflags=UIP_FIN_WAIT_1;
        //送信のために一旦解除
        unlockResource(i_inst);
        //FINの送信
        if(sendWithRetransmit(i_inst,TCP_FIN|TCP_ACK,NULL,0,&sw,&sq)==0){
            //ちょっと待つ。
            NyLPC_cThread_yield();
            //TXの消去待ち
            if(waitForTxRemove(i_inst,sq,&sw)){
                //再ロック
                lockResource(i_inst);
                //タイムアウトするか、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:
                        //一時的なアンロック
                        unlockResource(i_inst);
                        NyLPC_cThread_yield();
                        lockResource(i_inst);
                    default:
                        break;
                    }
                }
                unlockResource(i_inst);
            }
        }
        break;
    case UIP_CLOSE_WAIT:
        //LAST_ACKへ遷移
        i_inst->tcpstateflags=UIP_LAST_ACK;
        //送信のために一旦解除
        unlockResource(i_inst);
        if(sendWithRetransmit(i_inst,TCP_FIN|TCP_ACK,NULL,0,&sw,&sq)==0){
            //ちょっと待つ。
            NyLPC_cThread_yield();
            //TXの消去待ち
            if(waitForTxRemove(i_inst,sq,&sw)){
                //再ロック
                lockResource(i_inst);
                //TX消去後にCLOSEDに遷移していればOK
                if(i_inst->tcpstateflags==UIP_CLOSED)
                {
                    NyLPC_Assert(i_inst->txbuf.rp==i_inst->txbuf.wp);
                    goto ReturnWithUnlock;
                }
                unlockResource(i_inst);
            }
        }
        //エラー。RSTで切断。
        break;
    default:
        unlockResource(i_inst);
        NyLPC_Warning();
        break;
    }
//  if(i_inst->_smutex._lock_count>0){
//      NyLPC_Warning();
//  }
    //このパスに到達するのは、FIN送信/ACKに成功したにも拘らず、規定時間内にCLOSEDに遷移しなかった場合。
    //コネクションを強制遷移して、RST
    lockResource(i_inst);
    f=i_inst->tcpstateflags;
    if(f!=UIP_CLOSED){
        //もし、強制CLOSE遷移であれば、RSTも送信。
        i_inst->tcpstateflags=UIP_CLOSED;
        unlockResource(i_inst);
        sendRst(i_inst);
    }else{
        unlockResource(i_inst);
    }
    NyLPC_cStopwatch_finalize(&sw);
    return;
ReturnWithUnlock:
    unlockResource(i_inst);
    NyLPC_cStopwatch_finalize(&sw);
    return;
}

/**
 * uipサービスタスクが実行する関数です。
 * 定期的に実行する関数。最低でも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
    lockResource(i_inst);
    if(i_inst->tcpstateflags==UIP_CLOSED)
    {
        //CLOSEDなら、バッファ開放。
        resetTxQWithUnlock(i_inst);
    }else if(i_inst->txbuf.rp==i_inst->txbuf.wp){
        //再送信パケットがなければ何もしないよ。
        unlockResource(i_inst);
    }else if(i_inst->uip_connr.peer_win==0){
        //peer_winが0の場合は何もしない。
        unlockResource(i_inst);
    }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)));
                }
                unlockResource(i_inst);
            }
        }else{
            unlockResource(i_inst);
        }
    }
    NyLPC_cStopwatch_finalize(&sw);
    return;
}
/**
 * uipサービスタスクが実行する関数です。
 * サービスの開始を通知します。
 */
void NyLPC_cTcpSocket_startService(NyLPC_TcTcpSocket_t* i_inst,const NyLPC_TcIPv4Config_t* i_config)
{
    NyLPC_Assert(i_inst->tcpstateflags==UIP_CLOSED);//閉じてなければおかしい。
    i_inst->uip_connr.lipaddr=&(i_config->ip_addr);
    i_inst->uip_connr.default_mss=i_config->default_mss;
    //NyLPC_cTcpSocket_setSynPayload関数でも実行するけど、IFのリセット時なのでここでもやる。
    NyLPC_cFifoBuffer_clear(&(i_inst->rxbuf));
    return;
}
/**
 * uipサービスタスクが実行する関数です。
 * サービスの停止を通知します。
 */
void NyLPC_cTcpSocket_stopService(NyLPC_TcTcpSocket_t* i_inst)
{
    lockResource(i_inst);
    if(i_inst->tcpstateflags==UIP_CLOSED)
    {
        unlockResource(i_inst);
    }else{
        i_inst->tcpstateflags=UIP_CLOSED;
        resetTxQWithUnlock(i_inst);
        sendRst(i_inst);
    }
    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;

    //インスタンスをロックする。
    lockResource(i_inst);

    //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;//@bug じゃないのこれ。peer_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;
    case UIP_TIME_WAIT:
        //最終ACKを送り続ける。
        break;
    case UIP_SYN_SENT:
        //connect関数実行中しか起動しないステータス
        if(num_of_noack==0){
            i_inst->tcpstateflags=UIP_ESTABLISHED;
            i_inst->uip_connr.rcv_nxt32=NyLPC_ntohl(o_ipp->payload.tcp->seqno32)+1;
        }else{
            //それ以外のパケットはドロップする。
            break;//goto DROP;
        }
        //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;
    }
    unlockResource(i_inst);
    //取り外したTXメモリの開放
    for(i=0;i<s;i++){
        //取り外したTXメモリを開放
        NyLPC_cUipService_releaseTxBuf(dlist[i]);
    }
NyLPC_Trace();
    return ret;
DROP:
    //ACKしたパケットを送信キューから削除
    unlockResource(i_inst);
NyLPC_Trace();
    return NyLPC_TBool_FALSE;
}