1 /*********************************************************************************
\r
3 * --------------------------------------------------------------------------------
\r
5 * This file is part of MiMic
\r
6 * Copyright (C)2011 Ryo Iizuka
\r
8 * MiMic is free software: you can redistribute it and/or modify
\r
9 * it under the terms of the GNU Lesser General Public License as published
\r
10 * by the Free Software Foundation, either version 3 of the License, or
\r
11 * (at your option) any later version.
\r
13 * This program is distributed in the hope that it will be useful,
\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
16 * GNU General Public License for more details.
\r
18 * You should have received a copy of the GNU Lesser General Public License
\r
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
\r
21 * For further information please contact.
\r
23 * <airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>
\r
25 *********************************************************************************/
\r
26 #include "NyLPC_cTcpSocket_protected.h"
\r
27 #include "NyLPC_stdlib.h"
\r
28 #include "NyLPC_cUipService_protected.h"
\r
31 static NyLPC_TUInt32 iss32=3939;
\r
32 #define SIZE_OF_IPv4_TCPIP_HEADER 40
\r
39 #define UIP_IP_RTO_MAX_RTO 64000
\r
43 * 伝送路の特性に合わせて調整すること。
\r
45 #define UIP_TCP_RTO_INITIAL 3000
\r
50 #define UIP_TCP_RTO_CONNECTION_INITIAL 200
\r
55 #define UIP_TCP_RTO_MINIMUM 100
\r
63 NyLPC_TUInt32 rto_log[256];
\r
65 #define DEBUG_RTO_LOG(i_inst) if(rto_log_st<256){rto_log[rto_log_st++]=i_inst->uip_connr.current_rto32;};
\r
67 #define DEBUG_RTO_LOG(i_inst)
\r
70 //#define lockResource(i_inst) NyLPC_cMutex_lock(&((i_inst)->_smutex))
\r
71 //#define unlockResource(i_inst) NyLPC_cMutex_unlock(&((i_inst)->_smutex))
\r
72 #define lockResource(i_inst) NyLPC_cMutex_lock(NyLPC_cIPv4_getSockMutex(((i_inst)->_super._parent_ipv4)))
\r
73 #define unlockResource(i_inst) NyLPC_cMutex_unlock(NyLPC_cIPv4_getSockMutex(((i_inst)->_super._parent_ipv4)))
\r
75 static void sendRst(NyLPC_TcTcpSocket_t* i_inst);
\r
80 ////////////////////////////////////////////////////////////////////////////////////////////////////
\r
84 ////////////////////////////////////////////////////////////////////////////////////////////////////
\r
88 * TCPヘッダに値をセットする。checksum,wndは0初期化する。
\r
90 static void setTcpTxHeader(struct NyLPC_TTcpHeader* i_struct,NyLPC_TUInt8 i_flag,const struct uip_conn* i_conn)
\r
92 i_struct->flags = i_flag;
\r
93 //sorce & destination port
\r
94 i_struct->srcport = i_conn->lport;
\r
95 i_struct->destport = i_conn->rport;
\r
97 i_struct->ackno32 = NyLPC_htonl(i_conn->rcv_nxt32);
\r
99 i_struct->seqno32 = NyLPC_htonl(i_conn->snd_nxt32);
\r
100 //uip_func_tcp_send_noconn(BUF);
\r
101 i_struct->urgp[0] = i_struct->urgp[1] = 0;
\r
102 i_struct->tcpchksum= 0;
\r
105 static void setTxPacket(const NyLPC_TcTcpSocket_t* i_inst,void* i_tx_buf,NyLPC_TUInt8 i_tcpf,const void* i_buf,NyLPC_TUInt16 i_len)
\r
107 struct NyLPC_TIPv4Header* iph;
\r
108 struct NyLPC_TTcpHeader* tcph;
\r
109 NyLPC_TUInt8 iph_word=0x05;
\r
110 NyLPC_TUInt8 tcph_word=(UIP_TCPH_LEN) / 4;
\r
112 iph=(struct NyLPC_TIPv4Header*)i_tx_buf;
\r
113 iph->vhl=0x40|(0x0f&iph_word);
\r
114 iph->destipaddr=i_inst->uip_connr.ripaddr;
\r
115 iph->srcipaddr =*(i_inst->uip_connr.lipaddr);
\r
116 NyLPC_TIPv4Header_writeTxIpHeader(iph,UIP_PROTO_TCP);
\r
118 tcph=(struct NyLPC_TTcpHeader*)(((NyLPC_TUInt8*)i_tx_buf)+NyLPC_TIPv4Header_getHeaderLength(iph));
\r
122 if((TCP_SYN & i_tcpf)){
\r
123 tcph_word+=((TCP_OPT_MSS_LEN) / 4);
\r
124 NyLPC_TTcpHeader_setMmsOpt(((NyLPC_TUInt8*)(tcph+1)),i_inst->uip_connr.default_mss);
\r
126 tcph->tcpoffset=(tcph_word<<4);
\r
127 setTcpTxHeader(tcph,i_tcpf,&(i_inst->uip_connr));
\r
129 //最終的なパケットサイズと必要ならペイロードを書き込み
\r
131 iph->len16=NyLPC_htons(i_len+(iph_word+tcph_word)*4);
\r
132 memcpy(((NyLPC_TUInt8*)i_tx_buf)+((iph_word+tcph_word)*4),i_buf,i_len);
\r
134 iph->len16=NyLPC_htons((iph_word+tcph_word)*4);
\r
137 tcph->wnd16=NyLPC_htons(NyLPC_cFifoBuffer_getSpace(&(i_inst->rxbuf)));
\r
139 tcph->tcpchksum=~(NyLPC_TIPv4Header_makeTcpChecksum(iph));
\r
140 iph->ipchksum = ~(NyLPC_TIPv4Header_makeIpChecksum(iph));
\r
145 * IP/TCPヘッダが40バイト固定として、i_tx_buf+40の位置にあるペイロードに対するIP/TCPヘッダを書き込みます。
\r
147 static void setTxPacketHeader(const NyLPC_TcTcpSocket_t* i_inst,void* i_tx_buf,NyLPC_TUInt8 i_tcpf,NyLPC_TUInt16 i_len)
\r
149 struct NyLPC_TIPv4Header* iph;
\r
150 struct NyLPC_TTcpHeader* tcph;
\r
151 NyLPC_TUInt8 iph_word=0x05;
\r
152 NyLPC_TUInt8 tcph_word=(UIP_TCPH_LEN) / 4;
\r
154 iph=(struct NyLPC_TIPv4Header*)i_tx_buf;
\r
155 iph->vhl=0x40|(0x0f&iph_word);
\r
156 iph->destipaddr=i_inst->uip_connr.ripaddr;
\r
157 iph->srcipaddr =*(i_inst->uip_connr.lipaddr);
\r
158 NyLPC_TIPv4Header_writeTxIpHeader(iph,UIP_PROTO_TCP);
\r
161 tcph=(struct NyLPC_TTcpHeader*)(((NyLPC_TUInt8*)i_tx_buf)+NyLPC_TIPv4Header_getHeaderLength(iph));
\r
162 tcph->tcpoffset=(tcph_word<<4);
\r
163 setTcpTxHeader(tcph,i_tcpf,&(i_inst->uip_connr));
\r
165 //最終的なパケットサイズと必要ならペイロードを書き込み
\r
166 iph->len16=NyLPC_htons(i_len+(iph_word+tcph_word)*4);
\r
168 tcph->wnd16=NyLPC_htons(NyLPC_cFifoBuffer_getSpace(&(i_inst->rxbuf)));
\r
170 tcph->tcpchksum=~(NyLPC_TIPv4Header_makeTcpChecksum(iph));
\r
171 iph->ipchksum = ~(NyLPC_TIPv4Header_makeIpChecksum(iph));
\r
179 ////////////////////////////////////////////////////////////////////////////////////////////////////
\r
181 // Mainclass::private
\r
183 ////////////////////////////////////////////////////////////////////////////////////////////////////
\r
190 static void updateAckNo(void* i_tx_buf,NyLPC_TUInt32 i_ackno)
\r
192 struct NyLPC_TIPv4Header* iph=(struct NyLPC_TIPv4Header*)i_tx_buf;
\r
193 struct NyLPC_TTcpHeader* tcph=(struct NyLPC_TTcpHeader*)(((NyLPC_TUInt8*)i_tx_buf)+NyLPC_TIPv4Header_getHeaderLength(iph));
\r
201 old_ack.l=i_inst->payload.tcp->ackno32;//古いACK番号
\r
202 new_ack.l=i_ackno;//新しいACK番号
\r
203 v1=NyLPC_ntohs(~(i_inst->payload.tcp->tcpchksum));//1の補数を取って、ホストオーダーに戻す。
\r
205 v1=sub16c(v1,(old_ack.b[0]<<8)+old_ack.b[1]);
\r
206 v1=sub16c(v1,(old_ack.b[2]<<8)+old_ack.b[3]);
\r
208 v1=add16c(v1,(new_ack.b[0]<<8)+new_ack.b[1]);
\r
209 v1=add16c(v1,(new_ack.b[2]<<8)+new_ack.b[3]);
\r
210 v1=~NyLPC_htons(v1);*/
\r
212 tcph->ackno32=i_ackno;
\r
214 tcph->tcpchksum = 0;
\r
216 tcph->tcpchksum = ~(NyLPC_TIPv4Header_makeTcpChecksum(iph));
\r
220 if((i_inst->payload.tcp->tcpchksum!=v1)){
\r
228 * 指定した送信パケットがACK済であるか調べる。
\r
230 static NyLPC_TBool isPacketAcked(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq)
\r
233 struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
\r
234 rp=i_inst->txbuf.rp;
\r
235 while(rp!=i_inst->txbuf.wp){
\r
236 if(q[rp].ackno==i_sq){
\r
237 return NyLPC_TBool_FALSE;
\r
239 rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
\r
241 return NyLPC_TBool_TRUE;
\r
244 * 送信キューからi_sq以前に送信したパケットを除外して、残り個数を返却する。
\r
246 static int getNumOfSending(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq)
\r
249 struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
\r
250 rp=i_inst->txbuf.rp;
\r
252 while(rp!=i_inst->txbuf.wp){
\r
253 if(q[rp].ackno==i_sq){
\r
257 rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
\r
262 * この関数は、コネクションをリセットします。
\r
264 * 関数は、現在バッファにある再送信待ちデータを開放します。
\r
266 static void resetTxQWithUnlock(NyLPC_TcTcpSocket_t* i_inst)
\r
269 struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
\r
270 void* dlist[NyLPC_TcTcpSocket_NUMBER_OF_TXQ];
\r
273 while(i_inst->txbuf.rp!=i_inst->txbuf.wp){
\r
274 dlist[l]=q[i_inst->txbuf.rp].packet;
\r
276 i_inst->txbuf.rp=(i_inst->txbuf.rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
\r
278 i_inst->txbuf.rp=i_inst->txbuf.wp=0;
\r
280 unlockResource(i_inst);
\r
283 NyLPC_cUipService_releaseTxBuf(dlist[i]);
\r
288 * TXバッファの再送パケットのACK番号を更新します。
\r
293 static void updateTxAck(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_ackno)
\r
296 struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
\r
297 NyLPC_ArgAssert(i_inst!=NULL);
\r
298 rp=i_inst->txbuf.rp;
\r
299 while(rp!=i_inst->txbuf.wp){
\r
300 updateAckNo(q[rp].packet,i_ackno);
\r
301 rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
\r
308 static void estimateRTO(NyLPC_TcTcpSocket_t* i_inst,int s,int n)
\r
310 NyLPC_TcStopwatch_t sw;
\r
311 NyLPC_TUInt32 cr_rtt_min,cr_rtt_max,sk_rto,new_rto,w;
\r
313 struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
\r
314 NyLPC_cStopwatch_initialize(&sw);
\r
316 sk_rto=i_inst->uip_connr.current_rto32;
\r
320 NyLPC_cStopwatch_set(&sw,q[s].tick_of_sent);
\r
321 cr_rtt_min=NyLPC_cStopwatch_elapseInMsec(&sw);
\r
322 if(sk_rto<cr_rtt_min){
\r
323 //現在のRTOよりも大きい→再送があった。(再送の理由が回線遅延によるものかわからないので、基本RTOを25%増やす。)
\r
324 new_rto=sk_rto*10/8;
\r
325 }else if(sk_rto/4<cr_rtt_min){
\r
326 //現在のRTOの1/4< n < 現在のRTO 想定内の変動。1/8
\r
327 new_rto=(sk_rto+(cr_rtt_min*3*7))/8;
\r
329 //現在の1/4以下。RTOを再計算。 RTOが大きすぎるので再計算。(計測値を優先した現在値との平均値)
\r
330 new_rto=(sk_rto+(cr_rtt_min*3*3))/4;
\r
334 //複数のパケットなら、最大と最小の時刻を得る。
\r
335 NyLPC_cStopwatch_set(&sw,q[s].tick_of_sent);
\r
336 cr_rtt_min=cr_rtt_max=NyLPC_cStopwatch_elapseInMsec(&sw);
\r
338 NyLPC_cStopwatch_set(&sw,q[(s+i)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ].tick_of_sent);
\r
339 w=NyLPC_cStopwatch_elapseInMsec(&sw);
\r
347 if(sk_rto<cr_rtt_min && sk_rto<cr_rtt_max){
\r
348 //最大値,最小値とも現在のRTTより大きい→低速な回線を検出。
\r
349 new_rto=cr_rtt_max*10/8;//最大経過時間の25%増しの時間を設定。
\r
350 }else if(sk_rto/4<cr_rtt_min){
\r
351 //現在のRTOの1/4< n < 現在のRTO 想定範囲内。1/8の加重平均で速度計算。
\r
352 new_rto=(sk_rto+(cr_rtt_min*3*7))/8;
\r
354 //現在の1/4以下。RTOが大きすぎるので再計算。(計測値を優先した加重平均)
\r
355 new_rto=(sk_rto+(cr_rtt_min*3*3))/4;
\r
359 NyLPC_cStopwatch_finalize(&sw);
\r
360 if(new_rto<UIP_TCP_RTO_MINIMUM){
\r
361 new_rto=UIP_TCP_RTO_MINIMUM;
\r
363 i_inst->uip_connr.current_rto32=new_rto;
\r
367 * TXキューから、入力されたシーケンス番号より前のパケットを除外します。
\r
368 * リングバッファのrp->wp-1までをチェックして、sqに等しいi_sq以前のパケットバッファをo_dlistへ返します。
\r
371 static int updateTxQByIndex(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq,void* o_dlist[])
\r
374 struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
\r
376 rp=i_inst->txbuf.rp;
\r
379 DEBUG_RTO_LOG(i_inst);
\r
381 while(rp!=i_inst->txbuf.wp){
\r
382 o_dlist[n]=q[rp].packet;
\r
383 if(q[rp].ackno==i_sq){
\r
384 //i_inst->txbuf.rp->rpのパケットのRTOからbaseRTOの値を再計算。
\r
385 estimateRTO(i_inst,i_inst->txbuf.rp,n+1);
\r
386 i_inst->txbuf.rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
\r
390 rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
\r
401 static struct NyLPC_TcTcpSocket_TxQItem* getTxQ(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TcStopwatch_t* i_timer)
\r
404 struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
\r
406 //クローズドに遷移してしまったら、エラーである。
\r
407 if(i_inst->tcpstateflags==UIP_CLOSED){
\r
410 //キューの空きをチェック。wp+1==rpなら、キューがいっぱい。rp==wpなら、キューが空。
\r
411 if(((i_inst->txbuf.wp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ)==i_inst->txbuf.rp){
\r
413 unlockResource(i_inst);
\r
415 NyLPC_cThread_yield();
\r
417 lockResource(i_inst);
\r
420 i=i_inst->txbuf.wp;
\r
421 i_inst->txbuf.wp=(i+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
\r
423 }while(!NyLPC_cStopwatch_isExpired(i_timer));
\r
433 /**********************************************************************
\r
435 **********************************************************************/
\r
437 NyLPC_TBool NyLPC_cTcpSocket_initialize(NyLPC_TcTcpSocket_t* i_inst,void* i_rbuf,NyLPC_TUInt16 i_rbuf_len)
\r
440 NyLPC_TcUipService_t* srv=_NyLPC_TcUipService_inst;
\r
441 NyLPC_cBaseSocket_initialize(&(i_inst->_super),NyLPC_TcBaseSocket_TYPEID_TCP_SOCK);
\r
442 //uipサービスは初期化済であること。
\r
443 NyLPC_Assert(NyLPC_TcUipService_isInitService());
\r
445 NyLPC_cFifoBuffer_initialize(&(i_inst->rxbuf),i_rbuf,i_rbuf_len);
\r
446 // NyLPC_AbortIfNot(NyLPC_cMutex_initialize(&(i_inst->_smutex)));//個別Mutex
\r
447 // i_inst->_smutex=NyLPC_cIPv4_getSockMutex(&(srv->_tcpv4));//共有Mutex
\r
448 i_inst->tcpstateflags=UIP_CLOSED;
\r
449 i_inst->txbuf.rp=i_inst->txbuf.wp=0;
\r
450 for(i=0;i<NyLPC_TcTcpSocket_NUMBER_OF_TXQ;i++){
\r
451 i_inst->txbuf.txq[i].packet=NULL;
\r
454 return NyLPC_cIPv4_addSocket(&(srv->_tcpv4),&(i_inst->_super));
\r
456 void NyLPC_cTcpSocket_finalize(NyLPC_TcTcpSocket_t* i_inst)
\r
459 NyLPC_TcUipService_t* srv=_NyLPC_TcUipService_inst;
\r
460 NyLPC_Assert(NyLPC_TcUipService_isInitService());
\r
461 //uipサービスは初期化済であること。
\r
462 if(!NyLPC_cIPv4_removeSocket(&(srv->_tcpv4),&(i_inst->_super))){
\r
467 if(i_inst->txbuf.rp!=i_inst->txbuf.wp){
\r
468 lockResource(i_inst);
\r
469 resetTxQWithUnlock(i_inst);
\r
471 for(i=0;i<NyLPC_TcTcpSocket_NUMBER_OF_TXQ;i++){
\r
472 i_inst->txbuf.txq[i].packet=NULL;
\r
474 NyLPC_cFifoBuffer_finalize(&(i_inst->rxbuf));
\r
475 // NyLPC_cMutex_finalize(&(i_inst->_smutex));
\r
476 NyLPC_cBaseSocket_finalize(&(i_inst->_super));
\r
482 NyLPC_TBool NyLPC_cTcpSocket_listenSyn(NyLPC_TcTcpSocket_t* i_inst,const struct NyLPC_TTcpSocketSynParam* i_lq,NyLPC_TUInt16 i_lport)
\r
484 // NyLPC_Assert(NyLPC_cMutex_isLocked(i_inst->_smutex));
\r
485 lockResource(i_inst);
\r
487 if(i_inst->tcpstateflags==UIP_CLOSED)
\r
489 //localipとdefault_mmsは別枠で設定
\r
490 /* Fill in the necessary fields for the new connection. */
\r
491 i_inst->uip_connr.current_rto32 = UIP_TCP_RTO_INITIAL;
\r
492 i_inst->uip_connr.lport = i_lport;
\r
493 i_inst->uip_connr.rport = i_lq->rport;
\r
494 i_inst->uip_connr.ripaddr=i_lq->srcaddr;
\r
495 i_inst->uip_connr.snd_nxt32=iss32;
\r
496 /* rcv_nxt should be the seqno from the incoming packet + 1. */
\r
497 i_inst->uip_connr.rcv_nxt32= i_lq->rcv_nxt32;
\r
499 i_inst->uip_connr.peer_mss=(i_lq->mss!=0)?i_lq->mss:i_inst->uip_connr.default_mss;
\r
500 i_inst->uip_connr.peer_win=0;
\r
501 NyLPC_cFifoBuffer_clear(&(i_inst->rxbuf));
\r
503 i_inst->tcpstateflags = UIP_SYN_RCVD;
\r
504 //前回のデータが残っていた場合の保険
\r
505 if(i_inst->txbuf.rp!=i_inst->txbuf.wp){
\r
506 resetTxQWithUnlock(i_inst);
\r
508 unlockResource(i_inst);
\r
510 return NyLPC_TBool_TRUE;
\r
512 unlockResource(i_inst);
\r
513 return NyLPC_TBool_FALSE;
\r
518 * sq番のTxがキューから消え去るのを待ちます。
\r
519 * この関数は、アンロック状態でコールしてください。
\r
521 * パケットがキューからなくなる条件は、以下の2つです。
\r
523 * <li>ACKを受信してパケットキューが更新された。</li>
\r
524 * <li>RSTを受信して(CLOSEDに遷移して)、キューがクリアされた。</li>
\r
525 * <li>送信タイムアウトで関数が(CLOSEDに遷移させて)キューをクリアした。</li>
\r
528 * @param i_wait_msec
\r
530 * 1番目の条件でパケットが消失したときのみ、TRUEを返します。
\r
531 * 失敗した場合、TCPステータスがCLOSEDでなければ、RSTを送信してステータスをCLOSEDにします。
\r
533 static NyLPC_TBool waitForTxRemove(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq,NyLPC_TcStopwatch_t* i_timer)
\r
536 lockResource(i_inst);
\r
539 if(!isPacketAcked(i_inst,i_sq)){
\r
540 //まだある場合は、タスクスイッチを繰り返して消失を待つ。
\r
541 unlockResource(i_inst);
\r
542 NyLPC_cThread_yield();
\r
543 lockResource(i_inst);
\r
547 f=i_inst->tcpstateflags;
\r
548 unlockResource(i_inst);
\r
549 return (f==UIP_CLOSED)?NyLPC_TBool_FALSE:NyLPC_TBool_TRUE;
\r
550 }while(!NyLPC_cStopwatch_isExpired(i_timer));
\r
551 unlockResource(i_inst);
\r
552 return NyLPC_TBool_FALSE;
\r
557 * 再送信処理をセットして、パケットを送信します。
\r
558 * この関数は「アンロック状態で」実行してください。
\r
561 * この番号は、シーケンス番号の加算値ではありませんので、注意をしてください。
\r
564 * <li>n=-1:送信キューへの投入に失敗した。</li>
\r
565 * <li>n>=0:nバイトのデータを送信キューへの投入することに成功した。</li>
\r
567 * 送信キューに失敗する理由は2つあります。1つは、TXバッファがフルでタイムアウト。もうひとつは、非同期なコネクリョンのリセットです。
\r
568 * 失敗した場合、TCPステータスがCLOSEDでなければ、RSTを送信してステータスをCLOSEDにします。
\r
570 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)
\r
572 struct NyLPC_TcTcpSocket_TxQItem* txq;
\r
575 NyLPC_TUInt32 next_ack;
\r
577 //@bug オブションパケット送信時に4バイト足りないメモリ要求しない?問題になってないけど。
\r
579 buf=NyLPC_cUipService_allocTxBuf(i_len+(SIZE_OF_IPv4_TCPIP_HEADER),&s);
\r
584 if(NyLPC_cStopwatch_isExpired(i_timer)){
\r
588 lockResource(i_inst);
\r
589 //ペイロードがある場合のみ、相手のwindowサイズが0以上になるのを待つ。
\r
591 while(i_inst->uip_connr.peer_win==0){
\r
592 unlockResource(i_inst);
\r
594 if(NyLPC_cStopwatch_isExpired(i_timer)){
\r
597 NyLPC_cThread_yield();
\r
598 lockResource(i_inst);
\r
602 txq=getTxQ(i_inst,i_timer);
\r
605 //シーケンス番号をロールバックできないので、エラーとする。
\r
606 unlockResource(i_inst);
\r
607 NyLPC_cUipService_releaseTxBuf(buf);
\r
611 //送信バッファを基準とした送信サイズを計算
\r
612 s-=SIZE_OF_IPv4_TCPIP_HEADER;
\r
613 //送信サイズよりMMSが小さければ、送信サイズを修正
\r
614 if(i_inst->uip_connr.peer_mss<s){
\r
615 s=i_inst->uip_connr.peer_mss;
\r
617 //送信サイズよりpeerのウインドウサイズが小さければ修正
\r
618 if(i_inst->uip_connr.peer_win<s){
\r
619 s=i_inst->uip_connr.peer_win;
\r
621 //送信サイズより、データサイズが小さければ、送信サイズを修正
\r
626 next_ack=i_inst->uip_connr.snd_nxt32+s+(((i_tcpf&(TCP_FIN|TCP_SYN))!=0x00)?1:0);
\r
627 txq->rto32=i_inst->uip_connr.current_rto32;
\r
628 txq->tick_of_sent=NyLPC_cStopwatch_now();
\r
631 setTxPacket(i_inst,buf,i_tcpf,i_buf,s);
\r
635 i_inst->uip_connr.snd_nxt32=next_ack;
\r
637 i_inst->uip_connr.peer_win-=s;
\r
639 *o_ack=txq->ackno=NyLPC_HTONL(next_ack);
\r
640 unlockResource(i_inst);
\r
641 NyLPC_cUipService_sendIPv4Tx(buf);
\r
645 * RSTを1フレームだけ送信します。
\r
646 * この関数は、クローズドステータスのソケットにしてからコールします。
\r
647 * この関数は、アンロック状態でコールしてね。
649 static void sendRst(NyLPC_TcTcpSocket_t* i_inst)
\r
653 NyLPC_Assert(i_inst->tcpstateflags==UIP_CLOSED);
\r
656 //@bug バッファが取れるまで通信がブロックするの。ここはなんとかしないと。
\r
657 buf=NyLPC_cUipService_allocSysTxBuf();
\r
658 lockResource(i_inst);
\r
659 i_inst->uip_connr.snd_nxt32++;
\r
660 unlockResource(i_inst);
\r
661 setTxPacket(i_inst,buf,TCP_RST|TCP_ACK,NULL,0);
\r
662 NyLPC_cUipService_sendIPv4Tx(buf);
\r
663 NyLPC_cUipService_releaseTxBuf(buf);
\r
664 NyLPC_cIPv4Payload_finalize(&ipv4);
\r
672 * 十分な空き領域がない場合、失敗する。
\r
673 * この関数は、ロックして実行してください。
\r
675 static NyLPC_TBool addRecvData(NyLPC_TcTcpSocket_t* i_inst,const void* i_data,NyLPC_TUInt16 i_data_size)
\r
678 if(NyLPC_cFifoBuffer_getSpace(&(i_inst->rxbuf))>=i_data_size){
\r
680 NyLPC_cFifoBuffer_push(&(i_inst->rxbuf),i_data,i_data_size);
\r
683 return NyLPC_TBool_FALSE;
\r
686 return NyLPC_TBool_TRUE;
\r
696 NyLPC_TBool NyLPC_cTcpSocket_connect(NyLPC_TcTcpSocket_t* i_inst,const struct NyLPC_TIPv4Addr* i_addr,NyLPC_TUInt16 i_peer_port,NyLPC_TUInt32 i_wait_in_msec)
\r
698 volatile NyLPC_TUInt8 f;
\r
700 NyLPC_TcStopwatch_t sw;
\r
701 NyLPC_TUInt16 lport;
\r
702 lockResource(i_inst);
\r
704 if(i_inst->tcpstateflags!=UIP_CLOSED)
\r
706 NyLPC_OnErrorGoto(Error);
\r
708 //ポート番号の取得(lockResourceが他のソケットと共有なので、重複ポートの割当は起こりえない。でもちょっと注意して)
\r
709 lport=NyLPC_htons(NyLPC_cIPv4_getNewPortNumber(i_inst->_super._parent_ipv4));
\r
711 NyLPC_OnErrorGoto(Error);
\r
715 //localipとdefault_mmsは別枠で設定
\r
716 /* Fill in the necessary fields for the new connection. */
\r
717 i_inst->uip_connr.current_rto32 = UIP_TCP_RTO_CONNECTION_INITIAL;//RTOを短くしてARP発行時の再接続短縮を期待する。
\r
718 i_inst->uip_connr.lport = lport;
\r
719 i_inst->uip_connr.rport = NyLPC_htons(i_peer_port);
\r
720 i_inst->uip_connr.ripaddr=*i_addr;
\r
721 i_inst->uip_connr.snd_nxt32=iss32;//should be random
\r
722 /* rcv_nxt should be the seqno from the incoming packet + 1. */
\r
723 i_inst->uip_connr.rcv_nxt32=0;
\r
725 i_inst->uip_connr.peer_mss=i_inst->uip_connr.default_mss;
\r
726 i_inst->uip_connr.peer_win=1;//periodicの再送信を期待するために相手のWindowサイズは1と仮定する。
\r
727 NyLPC_cFifoBuffer_clear(&(i_inst->rxbuf));
\r
729 i_inst->tcpstateflags = UIP_SYN_SENT;
\r
730 //前回のデータが残っていた場合の保険
\r
731 if(i_inst->txbuf.rp!=i_inst->txbuf.wp){
\r
732 resetTxQWithUnlock(i_inst);
\r
734 unlockResource(i_inst);
\r
737 NyLPC_cStopwatch_initialize(&sw);
\r
739 NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
\r
740 if(sendWithRetransmit(i_inst,TCP_SYN,NULL,0,&sw,&sq)==0){
\r
742 NyLPC_cThread_yield();
\r
743 //キューにあるTXが消えるのを待つ。
\r
744 if(waitForTxRemove(i_inst,sq,&sw)){
\r
746 NyLPC_cStopwatch_finalize(&sw);
\r
747 return NyLPC_TBool_TRUE;
\r
750 //ロックして、強制的なステータス遷移
\r
751 lockResource(i_inst);
\r
752 f=i_inst->tcpstateflags;
\r
754 //もし、強制CLOSE遷移であれば、RSTも送信。
\r
755 i_inst->tcpstateflags=UIP_CLOSED;
\r
756 unlockResource(i_inst);
\r
759 unlockResource(i_inst);
\r
761 return NyLPC_TBool_FALSE;
\r
763 unlockResource(i_inst);
\r
764 return NyLPC_TBool_FALSE;
\r
768 * この関数は、UIP_SYN_RCVDステータスのソケットを、ESTABLISHEDへ遷移させます。
\r
769 * cTcpListener_listen関数を通過したインスタンスに実行してください。
\r
770 * この関数は、アプリケーションが呼び出します。
\r
774 NyLPC_TBool NyLPC_cTcpSocket_accept(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_wait_in_msec)
\r
776 volatile NyLPC_TUInt8 f;
\r
778 NyLPC_TcStopwatch_t sw;
\r
780 NyLPC_cStopwatch_initialize(&sw);
\r
782 f=i_inst->tcpstateflags;
\r
785 case UIP_ESTABLISHED:
\r
786 return NyLPC_TBool_TRUE;
\r
791 return NyLPC_TBool_FALSE;
\r
793 NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
\r
794 if(sendWithRetransmit(i_inst,TCP_SYN|TCP_ACK,NULL,0,&sw,&sq)==0){
\r
796 NyLPC_cThread_yield();
\r
797 //キューにあるTXが消えるのを待つ。
\r
798 if(waitForTxRemove(i_inst,sq,&sw)){
\r
800 NyLPC_cStopwatch_finalize(&sw);
\r
801 return NyLPC_TBool_TRUE;
\r
804 //ロックして、強制的なステータス遷移
\r
805 lockResource(i_inst);
\r
806 f=i_inst->tcpstateflags;
\r
808 //もし、強制CLOSE遷移であれば、RSTも送信。
\r
809 i_inst->tcpstateflags=UIP_CLOSED;
\r
810 unlockResource(i_inst);
\r
813 unlockResource(i_inst);
\r
815 return NyLPC_TBool_FALSE;
\r
820 * この関数は、ソケットの受信バッファの読み取り位置と、読み出せるデータサイズを返却します。
\r
821 * 関数はポインターを返却するだけで、バッファの読み取り位置をシークしません。
\r
822 * シークするにはNyLPC_cTcpSocket_pseekを使います。
\r
824 NyLPC_TInt32 NyLPC_cTcpSocket_precv(NyLPC_TcTcpSocket_t* i_inst,const void** o_buf_ptr,NyLPC_TUInt32 i_wait_msec)
\r
826 volatile NyLPC_TUInt8 st;
\r
827 NyLPC_TUInt16 rlen;
\r
829 NyLPC_TcStopwatch_t sw;
\r
830 NyLPC_cStopwatch_initialize(&sw);
\r
832 //ESTABLISHED以外の場合は、エラー。
\r
833 NyLPC_cStopwatch_setNow(&sw);
\r
837 lockResource(i_inst);
\r
838 st=i_inst->tcpstateflags;
\r
839 rlen=NyLPC_cFifoBuffer_getLength(&(i_inst->rxbuf));
\r
840 *o_buf_ptr=NyLPC_cFifoBuffer_getPtr(&(i_inst->rxbuf));
\r
842 unlockResource(i_inst);
\r
844 //バッファが空の場合は、ステータスチェック。ESTABLISHEDでなければ、エラー(PASVCLOSE等の場合)
\r
846 case UIP_ESTABLISHED:
\r
849 NyLPC_cStopwatch_finalize(&sw);
\r
853 case UIP_CLOSE_WAIT:
\r
856 NyLPC_cStopwatch_finalize(&sw);
\r
862 NyLPC_cStopwatch_finalize(&sw);
\r
866 NyLPC_cThread_yield();
\r
867 }while(NyLPC_cStopwatch_elapseInMsec(&sw)<i_wait_msec);
\r
868 //規定時間内に受信が成功しなかった。
\r
869 NyLPC_cStopwatch_finalize(&sw);
\r
876 void NyLPC_cTcpSocket_pseek(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt16 i_seek)
\r
879 NyLPC_ArgAssert(i_seek<=NyLPC_cFifoBuffer_getLength(&(i_inst->rxbuf)));
\r
885 buf=NyLPC_cUipService_allocSysTxBuf();
\r
888 lockResource(i_inst);
\r
891 NyLPC_cFifoBuffer_pop(&(i_inst->rxbuf),i_seek);
\r
893 setTxPacket(i_inst,buf,TCP_ACK,NULL,0);
\r
894 unlockResource(i_inst);
\r
896 NyLPC_cUipService_sendIPv4Tx(buf);
\r
897 NyLPC_cUipService_releaseTxBuf(buf);
\r
904 void* NyLPC_cTcpSocket_allocSendBuf(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt16 i_hint,NyLPC_TUInt16* o_buf_size,NyLPC_TUInt32 i_wait_in_msec)
\r
908 NyLPC_TcStopwatch_t sw;
\r
910 NyLPC_cStopwatch_initialize(&sw);
\r
911 NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
\r
914 //@bug バッファが取れるまで通信がブロックするの。ここはなんとかしないと。
\r
916 //ESTABLISHED以外に非同期遷移
\r
917 if(i_inst->tcpstateflags!=UIP_ESTABLISHED){
\r
918 NyLPC_cStopwatch_finalize(&sw);
\r
921 buf=NyLPC_cUipService_allocTxBuf(i_hint+(SIZE_OF_IPv4_TCPIP_HEADER),&s);
\r
926 if(NyLPC_cStopwatch_isExpired(&sw)){
\r
927 NyLPC_cStopwatch_finalize(&sw);
\r
932 //@todo 前段処理と順番を入れ替えて、要求サイズとpeerのwinのうち、小さいほうを割り当てたほうが良くない?
\r
933 //ここで相手のwin待ちをする理由は、相手に確実に受け取れるサイズを決定する為。
\r
934 lockResource(i_inst);
\r
935 //ペイロードがある場合のみ、相手のwindowサイズが0以上になるのを待つ。
\r
936 while(i_inst->uip_connr.peer_win==0){
\r
937 unlockResource(i_inst);
\r
938 //ESTABLISHED以外に非同期遷移 orタイムアウト確認
\r
939 if(NyLPC_cStopwatch_isExpired(&sw)||(i_inst->tcpstateflags!=UIP_ESTABLISHED)){
\r
940 NyLPC_cUipService_releaseTxBuf(buf);
\r
941 NyLPC_cStopwatch_finalize(&sw);
\r
944 NyLPC_cThread_yield();
\r
945 lockResource(i_inst);
\r
947 //送信バッファを基準とした送信サイズを計算
\r
948 s-=SIZE_OF_IPv4_TCPIP_HEADER;
\r
949 //送信サイズよりMMSが小さければ、送信サイズを修正
\r
950 if(i_inst->uip_connr.peer_mss<s){
\r
951 s=i_inst->uip_connr.peer_mss;
\r
953 //送信サイズよりpeerのウインドウサイズが小さければ修正
\r
954 if(i_inst->uip_connr.peer_win<s){
\r
955 s=i_inst->uip_connr.peer_win;
\r
957 unlockResource(i_inst);
\r
960 NyLPC_cStopwatch_finalize(&sw);
\r
961 return (NyLPC_TUInt8*)buf+SIZE_OF_IPv4_TCPIP_HEADER;
\r
966 void NyLPC_cTcpSocket_releaseSendBuf(NyLPC_TcTcpSocket_t* i_inst,void* i_buf_ptr)
\r
968 NyLPC_cUipService_releaseTxBuf((NyLPC_TUInt8*)i_buf_ptr-SIZE_OF_IPv4_TCPIP_HEADER);
\r
973 * 事前にAllocしたTxパケットを送信します。
\r
974 * このAPIはゼロコピー送信をサポートするためのものです。
\r
976 * allocSendBufで取得したメモリを指定します。
\r
978 * 関数が失敗した場合、i_buf_ptrは「開放されません。」
\r
980 NyLPC_TBool NyLPC_cTcpSocket_psend(NyLPC_TcTcpSocket_t* i_inst,void* i_buf_ptr,int i_len,NyLPC_TUInt32 i_wait_in_msec)
\r
982 struct NyLPC_TcTcpSocket_TxQItem* txq;
\r
984 NyLPC_TcStopwatch_t sw;
\r
985 //ESTABLISHEDでなければエラー
\r
986 if(i_inst->tcpstateflags!=UIP_ESTABLISHED){
\r
987 //ESTABLISHEDでなければエラー
\r
988 return NyLPC_TBool_FALSE;
\r
992 NyLPC_cTcpSocket_releaseSendBuf(i_inst,i_buf_ptr);
\r
993 return NyLPC_TBool_TRUE;
\r
995 NyLPC_cStopwatch_initialize(&sw);
\r
996 NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
\r
998 //先頭ポインタは、i_buf-sizeof(SIZE_OF_IPv4_TCPIP_HEADER)固定
\r
999 buf=(NyLPC_TUInt8*)i_buf_ptr-SIZE_OF_IPv4_TCPIP_HEADER;
\r
1000 lockResource(i_inst);
\r
1002 txq=getTxQ(i_inst,&sw);
\r
1005 //シーケンス番号をロールバックできないので、エラーとする。
\r
1006 unlockResource(i_inst);
\r
1007 NyLPC_cStopwatch_finalize(&sw);
\r
1008 return NyLPC_TBool_FALSE;
\r
1010 //ここから先はi_bufの所有権はインスタンスになってる。
\r
1014 //allocをした時点でwin,mssは考慮されているので、そのままそうしんしる。
\r
1017 txq->rto32=i_inst->uip_connr.current_rto32;
\r
1018 txq->tick_of_sent=NyLPC_cStopwatch_now();
\r
1019 //パケットヘッダの生成(ヘッダ長はpreadで定義した値(4+6)*4=40です。)
\r
1020 setTxPacketHeader(i_inst,buf,TCP_ACK|TCP_PSH,i_len);
\r
1024 i_inst->uip_connr.snd_nxt32=i_inst->uip_connr.snd_nxt32+i_len;
\r
1025 //Peerのウインドウサイズを更新
\r
1026 i_inst->uip_connr.peer_win-=i_len;
\r
1028 txq->ackno=NyLPC_HTONL(i_inst->uip_connr.snd_nxt32);
\r
1029 unlockResource(i_inst);
\r
1030 NyLPC_cUipService_sendIPv4Tx(buf);
\r
1031 NyLPC_cStopwatch_finalize(&sw);
\r
1032 return NyLPC_TBool_TRUE;
\r
1036 * See header file.
\r
1038 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)
\r
1040 NyLPC_TInt16 hint;
\r
1046 hint=(i_len>32767)?32767:i_len;
\r
1047 buf=NyLPC_cTcpSocket_allocSendBuf(i_inst,hint,&s,i_wait_in_msec);
\r
1052 s=((NyLPC_TInt32)s<i_len)?s:(NyLPC_TUInt16)i_len;
\r
1053 memcpy(buf,i_buf_ptr,s);
\r
1054 if(!NyLPC_cTcpSocket_psend(i_inst,buf,s,i_wait_in_msec)){
\r
1055 NyLPC_cTcpSocket_releaseSendBuf(i_inst,buf);
\r
1062 void NyLPC_cTcpSocket_close(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_wait_in_msec)
\r
1064 NyLPC_TcStopwatch_t sw;
\r
1065 volatile NyLPC_TUInt8 f;
\r
1067 NyLPC_cStopwatch_initialize(&sw);
\r
1068 NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
\r
1069 lockResource(i_inst);
\r
1071 f=i_inst->tcpstateflags;
\r
1077 goto ReturnWithUnlock;
\r
1078 case UIP_ESTABLISHED:
\r
1080 i_inst->tcpstateflags=UIP_FIN_WAIT_1;
\r
1082 unlockResource(i_inst);
\r
1084 if(sendWithRetransmit(i_inst,TCP_FIN|TCP_ACK,NULL,0,&sw,&sq)==0){
\r
1086 NyLPC_cThread_yield();
\r
1088 if(waitForTxRemove(i_inst,sq,&sw)){
\r
1090 lockResource(i_inst);
\r
1091 //タイムアウトするか、UIP_CLOSED、もしくはTIME_WAITに遷移するのを待つ。(遷移はRxprocで自動的に実行。)
\r
1093 switch(i_inst->tcpstateflags)
\r
1095 case UIP_TIME_WAIT:
\r
1096 i_inst->tcpstateflags=UIP_CLOSED;
\r
1098 NyLPC_Assert(i_inst->txbuf.rp==i_inst->txbuf.wp);
\r
1100 goto ReturnWithUnlock;
\r
1101 case UIP_FIN_WAIT_1:
\r
1102 case UIP_FIN_WAIT_2:
\r
1105 unlockResource(i_inst);
\r
1106 NyLPC_cThread_yield();
\r
1107 lockResource(i_inst);
\r
1111 }while(!NyLPC_cStopwatch_isExpired(&sw));
\r
1112 unlockResource(i_inst);
\r
1116 case UIP_CLOSE_WAIT:
\r
1118 i_inst->tcpstateflags=UIP_LAST_ACK;
\r
1120 unlockResource(i_inst);
\r
1121 if(sendWithRetransmit(i_inst,TCP_FIN|TCP_ACK,NULL,0,&sw,&sq)==0){
\r
1123 NyLPC_cThread_yield();
\r
1125 if(waitForTxRemove(i_inst,sq,&sw)){
\r
1127 lockResource(i_inst);
\r
1128 //TX消去後にCLOSEDに遷移していればOK
\r
1129 if(i_inst->tcpstateflags==UIP_CLOSED)
\r
1131 NyLPC_Assert(i_inst->txbuf.rp==i_inst->txbuf.wp);
\r
1132 goto ReturnWithUnlock;
\r
1134 unlockResource(i_inst);
\r
1140 unlockResource(i_inst);
\r
1144 // if(i_inst->_smutex._lock_count>0){
\r
1145 // NyLPC_Warning();
\r
1147 //このパスに到達するのは、FIN送信/ACKに成功したにも拘らず、規定時間内にCLOSEDに遷移しなかった場合。
\r
1148 //コネクションを強制遷移して、RST
\r
1149 lockResource(i_inst);
\r
1150 f=i_inst->tcpstateflags;
\r
1151 if(f!=UIP_CLOSED){
\r
1152 //もし、強制CLOSE遷移であれば、RSTも送信。
\r
1153 i_inst->tcpstateflags=UIP_CLOSED;
\r
1154 unlockResource(i_inst);
\r
1157 unlockResource(i_inst);
\r
1159 NyLPC_cStopwatch_finalize(&sw);
\r
1162 unlockResource(i_inst);
\r
1163 NyLPC_cStopwatch_finalize(&sw);
\r
1168 * uipサービスタスクが実行する関数です。
\r
1169 * 定期的に実行する関数。最低でも1s単位で実行してください。
\r
1171 void NyLPC_cTcpSocket_periodic(
\r
1172 NyLPC_TcTcpSocket_t* i_inst)
\r
1175 struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
\r
1176 NyLPC_TcStopwatch_t sw;
\r
1177 NyLPC_TUInt32 now;
\r
1179 NyLPC_cStopwatch_initialize(&sw);
\r
1180 now=NyLPC_cStopwatch_now();
\r
1182 lockResource(i_inst);
\r
1183 if(i_inst->tcpstateflags==UIP_CLOSED)
\r
1185 //CLOSEDなら、バッファ開放。
\r
1186 resetTxQWithUnlock(i_inst);
\r
1187 }else if(i_inst->txbuf.rp==i_inst->txbuf.wp){
\r
1188 //再送信パケットがなければ何もしないよ。
\r
1189 unlockResource(i_inst);
\r
1190 }else if(i_inst->uip_connr.peer_win==0){
\r
1191 //peer_winが0の場合は何もしない。
\r
1192 unlockResource(i_inst);
\r
1195 rp=i_inst->txbuf.rp;
\r
1196 NyLPC_cStopwatch_set(&sw,q[rp].tick_of_sent);
\r
1197 if(NyLPC_cStopwatch_elapseInMsec(&sw)>q[rp].rto32){
\r
1198 //最古のパケットの送信時間をチェックして、タイムアウトが発生したら、再送時間と送信時刻をセット
\r
1201 for(i=rp;i!=i_inst->txbuf.wp;i=(i+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ){
\r
1202 q[i].tick_of_sent=now;
\r
1204 if(q[rp].rto32>UIP_IP_RTO_MAX_RTO){
\r
1205 //最古のRTOが64秒を超えたら、CLOSED
\r
1206 i_inst->tcpstateflags =UIP_CLOSED;
\r
1207 resetTxQWithUnlock(i_inst);
\r
1211 for(i=rp;i!=i_inst->txbuf.wp;i=(i+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ){
\r
1212 // NyLPC_cUipService_sendIPv4Tx(NyLPC_cIPv4Payload_getBuf(&(q[i].data)));
\r
1213 NyLPC_cUipService_sendIPv4Tx(q[i].packet);
\r
1215 unlockResource(i_inst);
\r
1218 unlockResource(i_inst);
\r
1221 NyLPC_cStopwatch_finalize(&sw);
\r
1225 * uipサービスタスクが実行する関数です。
\r
1228 void NyLPC_cTcpSocket_startService(NyLPC_TcTcpSocket_t* i_inst,const NyLPC_TcIPv4Config_t* i_config)
\r
1230 NyLPC_Assert(i_inst->tcpstateflags==UIP_CLOSED);//閉じてなければおかしい。
\r
1231 i_inst->uip_connr.lipaddr=&(i_config->ip_addr);
\r
1232 i_inst->uip_connr.default_mss=i_config->default_mss;
\r
1233 //NyLPC_cTcpSocket_setSynPayload関数でも実行するけど、IFのリセット時なのでここでもやる。
\r
1234 NyLPC_cFifoBuffer_clear(&(i_inst->rxbuf));
\r
1238 * uipサービスタスクが実行する関数です。
\r
1241 void NyLPC_cTcpSocket_stopService(NyLPC_TcTcpSocket_t* i_inst)
\r
1243 lockResource(i_inst);
\r
1244 if(i_inst->tcpstateflags==UIP_CLOSED)
\r
1246 unlockResource(i_inst);
\r
1248 i_inst->tcpstateflags=UIP_CLOSED;
\r
1249 resetTxQWithUnlock(i_inst);
\r
1256 void* NyLPC_cTcpSocket_parseRx(
\r
1257 NyLPC_TcTcpSocket_t* i_inst,
\r
1258 const NyLPC_TcIPv4Payload_t* i_ipp)
\r
1261 NyLPC_TUInt16 tmp16;
\r
1262 NyLPC_TUInt16 data_size;
\r
1263 NyLPC_TUInt8 in_tcpflag=i_ipp->payload.tcp->flags;
\r
1264 const void* tcp_data_offset;
\r
1265 NyLPC_TBool is_new_packet;
\r
1267 void* dlist[NyLPC_TcTcpSocket_NUMBER_OF_TXQ];
\r
1272 tmp16=NyLPC_TTcpHeader_getHeaderLength(i_ipp->payload.tcp);
\r
1273 //TCPペイロードの長さは、IPパケットの長さ-(IPヘッダ+TCPヘッダ)
\r
1274 data_size=NyLPC_TIPv4Header_getPacketLength(i_ipp->header)-NyLPC_TIPv4Header_getHeaderLength(i_ipp->header)-tmp16;
\r
1276 tcp_data_offset=i_ipp->payload.rawbuf+tmp16;
\r
1279 lockResource(i_inst);
\r
1281 //RSTのチェック。RST受信時は、状態にかかわらず、CLOSEDステータスに移行する。
\r
1282 if (in_tcpflag & TCP_RST)
\r
1284 i_inst->tcpstateflags =UIP_CLOSED;
\r
1289 is_new_packet=NyLPC_ntohl(i_ipp->payload.tcp->seqno32)==i_inst->uip_connr.rcv_nxt32;
\r
1295 if(NyLPC_TTcpHeader_getTcpMmsOpt(i_ipp->payload.tcp,&tmp16)){
\r
1297 i_inst->uip_connr.peer_mss=tmp16;
\r
1299 //受信パケットを元に、未ACKパケットの数を計算
\r
1300 num_of_noack=getNumOfSending(i_inst,i_ipp->payload.tcp->ackno32);//i_inst->txbuf.num_of_txq;
\r
1303 switch(i_inst->tcpstateflags)
\r
1305 case UIP_SYN_RCVD:
\r
1306 //ACKを受信したら、ESTABLISHEDへ。
\r
1307 //すべてのパケットをACKしたかで判定。()
\r
1308 if(num_of_noack==0){
\r
1309 i_inst->tcpstateflags=UIP_ESTABLISHED;
\r
1311 //それ以外のパケットはドロップする。
\r
1312 break;//goto DROP;
\r
1314 //新しいパケットがなければ、無応答
\r
1315 if(!is_new_packet){
\r
1316 break;//goto DROP;
\r
1318 //引き続き、ESTABLISHEDの処理へ。
\r
1319 case UIP_ESTABLISHED:
\r
1321 if(is_new_packet){
\r
1322 if(addRecvData(i_inst,tcp_data_offset,data_size)){
\r
1324 i_inst->uip_connr.rcv_nxt32+=data_size;
\r
1326 //失敗したときは必要に応じて単純ACK
\r
1331 if(is_new_packet && (in_tcpflag & TCP_FIN)){
\r
1332 //FINがあるときは、ステータスをCLOSE_WAITへセットして、ACKを返す。
\r
1333 i_inst->tcpstateflags = UIP_CLOSE_WAIT;
\r
1334 i_inst->uip_connr.rcv_nxt32++;
\r
1337 case UIP_CLOSE_WAIT:
\r
1340 case UIP_LAST_ACK:
\r
1341 //ACK(by FIN)が得られたなら、CLOSEDへ。
\r
1342 if(num_of_noack==0){
\r
1343 i_inst->tcpstateflags=UIP_CLOSED;
\r
1347 case UIP_FIN_WAIT_1:
\r
1349 if(is_new_packet){
\r
1350 i_inst->uip_connr.rcv_nxt32+=data_size;
\r
1351 if(in_tcpflag & TCP_FIN){
\r
1352 i_inst->uip_connr.rcv_nxt32++;
\r
1353 if(num_of_noack==0){
\r
1355 i_inst->tcpstateflags=UIP_TIME_WAIT;
\r
1358 i_inst->tcpstateflags=UIP_CLOSING;
\r
1361 }else if(num_of_noack==0){
\r
1363 i_inst->tcpstateflags=UIP_FIN_WAIT_2;
\r
1367 case UIP_FIN_WAIT_2:
\r
1368 //FIN受信->TIME_WAITへ(pureACK)
\r
1369 if(is_new_packet && (in_tcpflag & TCP_FIN)){
\r
1370 i_inst->uip_connr.rcv_nxt32++;
\r
1371 i_inst->tcpstateflags=UIP_TIME_WAIT;
\r
1375 //ACK受信したら、TIME_WAITへ
\r
1376 if(num_of_noack==0){
\r
1377 i_inst->tcpstateflags=UIP_TIME_WAIT;
\r
1383 case UIP_TIME_WAIT:
\r
1386 case UIP_SYN_SENT:
\r
1387 //connect関数実行中しか起動しないステータス
\r
1388 if(num_of_noack==0){
\r
1389 i_inst->tcpstateflags=UIP_ESTABLISHED;
\r
1390 i_inst->uip_connr.rcv_nxt32=NyLPC_ntohl(i_ipp->payload.tcp->seqno32)+1;
\r
1392 //それ以外のパケットはドロップする。
\r
1393 break;//goto DROP;
\r
1401 i_inst->uip_connr.peer_win=NyLPC_ntohs(i_ipp->payload.tcp->wnd16);
\r
1403 //送信キューから、Peerが受信したデータを削除する。
\r
1404 if(in_tcpflag & TCP_ACK){
\r
1405 //再送パケットキューから送信済みのデータを回収(後で開放)
\r
1407 s=updateTxQByIndex(i_inst,i_ipp->payload.tcp->ackno32,dlist);
\r
1412 //新しいパケットがきた場合は、再送キューのACKを更新する。
\r
1413 if(is_new_packet){
\r
1415 updateTxAck(i_inst,NyLPC_htonl(i_inst->uip_connr.rcv_nxt32));
\r
1419 if(((in_tcpflag&(TCP_FIN|TCP_SYN))!=0x00) ||
\r
1420 ((!is_new_packet) && (data_size>0)))
\r
1422 //ソケットからPureACKを生成 as setPacket(i_inst,i_ipp,TCP_ACK,NULL,0);
\r
1423 ret=NyLPC_cUipService_allocSysTxBuf();
\r
1424 setTxPacket(i_inst,ret,TCP_ACK,NULL,0);
\r
1428 unlockResource(i_inst);
\r
1432 NyLPC_cUipService_releaseTxBuf(dlist[i]);
\r
1437 //ACKしたパケットを送信キューから削除
\r
1438 unlockResource(i_inst);
\r
1445 * 入力されたパケットからRSTパケットを生成して返す。
\r
1447 void* NyLPC_cTcpSocket_allocTcpReverseRstAck(
\r
1448 const NyLPC_TcIPv4Payload_t* i_src)
\r
1450 struct NyLPC_TIPv4Header* iph;
\r
1451 struct NyLPC_TTcpHeader* tcph;
\r
1452 NyLPC_TUInt8 iph_word=0x05;
\r
1453 NyLPC_TUInt8 tcph_word=(UIP_TCPH_LEN) / 4;
\r
1454 void* txb=NyLPC_cUipService_allocSysTxBuf();
\r
1456 iph=(struct NyLPC_TIPv4Header*)txb;
\r
1457 iph->vhl=0x40|(0x0f&iph_word);
\r
1458 iph->destipaddr=i_src->header->srcipaddr;
\r
1459 iph->srcipaddr =i_src->header->destipaddr;
\r
1460 NyLPC_TIPv4Header_writeTxIpHeader(iph,UIP_PROTO_TCP);
\r
1463 tcph=(struct NyLPC_TTcpHeader*)(((NyLPC_TUInt8*)txb)+NyLPC_TIPv4Header_getHeaderLength(iph));
\r
1465 tcph->tcpoffset=(tcph_word<<4);
\r
1467 tcph->flags = TCP_RST | TCP_ACK;
\r
1468 //sorce & destination port
\r
1469 tcph->srcport = i_src->payload.tcp->destport;
\r
1470 tcph->destport = i_src->payload.tcp->srcport;
\r
1472 tcph->ackno32 = NyLPC_htonl(NyLPC_ntohl(i_src->payload.tcp->seqno32)+1);
\r
1474 tcph->seqno32 = i_src->payload.tcp->ackno32;
\r
1475 //uip_func_tcp_send_noconn(BUF);
\r
1476 tcph->urgp[0] = tcph->urgp[1] = 0;
\r
1477 tcph->tcpchksum= 0;
\r
1480 //最終的なパケットサイズと必要ならペイロードを書き込み
\r
1481 iph->len16=NyLPC_htons((iph_word+tcph_word)*4);
\r
1485 tcph->tcpchksum=~(NyLPC_TIPv4Header_makeTcpChecksum(iph));
\r
1486 iph->ipchksum = ~(NyLPC_TIPv4Header_makeIpChecksum(iph));
\r