⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nettcp.c

📁 一个uCOS-II下的tcpip协议栈实现
💻 C
📖 第 1 页 / 共 5 页
字号:
		 */
		if(tcb->rcvcnt != 0)
			OSSemPost(tcb->readSem);
		
		/* process FIN bit (p 75) */
		if(tcpHdr->flags & FIN){
			tcb->flags |= FORCE;	/* Always respond with an ACK */

			switch(tcb->state){
			case SYN_RECEIVED:
			case ESTABLISHED:
				tcb->rcv.nxt++;
				setState(tcb, CLOSE_WAIT);
				break;
			case FINWAIT1:
				tcb->rcv.nxt++;
				if(tcb->sndcnt == 0) {
					/* Our FIN has been acked; bypass CLOSING state */
					setState(tcb, TIME_WAIT);
					tcb->retransTime = OSTimeGet() + MSL2 * TICKSPERSEC;
					TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpInput[%d]: Timer %lu in %s", 
								(int)(tcb - & tcbs[0]),
								tcb->retransTime - OSTimeGet(), tcbStates[tcb->state]));
					timeoutJiffy(&tcb->resendTimer, tcb->retransTime, resendTimeout, tcb);
				} else {
					setState(tcb, CLOSING);
				}
				break;
			case FINWAIT2:
				tcb->rcv.nxt++;
				setState(tcb, TIME_WAIT);
				tcb->retransTime = OSTimeGet() + MSL2 * TICKSPERSEC;
				TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpInput[%d]: Timer %lu in %s", 
							(int)(tcb - & tcbs[0]),
							tcb->retransTime - OSTimeGet(), tcbStates[tcb->state]));
				timeoutJiffy(&tcb->resendTimer, tcb->retransTime, resendTimeout, tcb);
				break;
			case CLOSE_WAIT:
			case CLOSING:
			case LAST_ACK:
				break;		/* Ignore */
			case TIME_WAIT:	/* p 76 */
				tcb->retransTime = OSTimeGet() + MSL2 * TICKSPERSEC;
				TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpInput[%d]: Timer %lu in %s", 
							(int)(tcb - & tcbs[0]),
							tcb->retransTime - OSTimeGet(), tcbStates[tcb->state]));
				timeoutJiffy(&tcb->resendTimer, tcb->retransTime, resendTimeout, tcb);
				break;
			}
		}
		
		/* 
		 * We're done with this segment header.  If there was any data, it was
		 * dealt with earlier.
		 */
		nFreeChain(inBuf);
		inBuf = NULL;
		
		/* 
		 * Scan the resequencing queue, looking for a segment we can handle,
		 * and freeing all those that are now obsolete.
		 */
		while(segLen < 0 
				&& nQHEAD(tcb->reseq) 
				&& seqGE(tcb->rcv.nxt, nQHEADSORT(tcb->reseq))) {
			nDEQUEUE(tcb->reseq, inBuf);
			ipHdr = nBUFTOPTR(inBuf, IPHdr *);
			ipHeadLen = ipHdr->ip_hl * 4;
			tcpHdr = (TCPHdr *)((char *)ipHdr + ipHeadLen);
			tcpHeadLen = tcpHdr->tcpOff * 4;
			segLen = ipHdr->ip_len - ipHeadLen - tcpHeadLen;
			if ((segLen = trimSeg(tcb, tcpHdr, inBuf, ipHeadLen + tcpHeadLen, segLen)) < 0) {
				nFreeChain(inBuf);
				inBuf = NULL;
			}
		}
	}
	tcpOutput(tcb);	/* Send any necessary ack */
}

/* 
 * Get and set parameters for the given connection.
 * Return 0 on success, an error code on failure. 
 */
int  tcpIOCtl(u_int td, int cmd, void *arg)
{
	TCPCB *tcb = &tcbs[td];
	int st = 0;

	if (td >= MAXTCP || tcb->prev == tcb)
		st = TCPERR_PARAM;
	else {
		switch(cmd) {
		case TCPCTLG_UPSTATUS:		/* Get the TCP up status. */
			if (arg) 
				*(int *)arg = (tcb->state == ESTABLISHED);
			else
				st = TCPERR_PARAM;
			break;
		case TCPCTLG_RCVCNT:		/* Get the bytes in the receive queue. */
			if (arg)
				*(int *)arg = (int)tcb->rcvcnt;
			else
				st = TCPERR_PARAM;
			break;
		case TCPCTLG_KEEPALIVE:		/* Get the TCP keepalive period. */
			if (arg) 
				*(int *)arg = (int)(tcb->keepAlive / TICKSPERSEC);
			else
				st = TCPERR_PARAM;
			break;
		case TCPCTLS_KEEPALIVE:		/* Set the TCP keepalive period. */
			if (arg) 
				tcb->keepAlive = (u_long)(*(int *)arg) * TICKSPERSEC;
			else
				st = TCPERR_PARAM;
			break;
		case TCPCTLG_TRACELEVEL:	/* Get the TCP trace level. */
			if (arg) 
				*(int *)arg = tcb->traceLevel;
			else
				st = TCPERR_PARAM;
			break;
		case TCPCTLS_TRACELEVEL:	/* Set the TCP trace level. */
			if (arg) 
				tcb->traceLevel = *(int *)arg;
			else
				st = TCPERR_PARAM;
			break;
		default:
			st = TCPERR_PARAM;
			break;
		}
	}
	
	return st;
}


/**********************************/
/*** LOCAL FUNCTION DEFINITIONS ***/
/**********************************/
#if ECHO_SUPPORT > 0
/*
 * tcpEcho - The TCP echo server.  This will handle a single TCP echo
 * connection at a time.
 */
#pragma argsused
static void tcpEcho(void *arg)
{
	#define TCPECHOBUFSZ 50
	struct sockaddr_in localAddr, peerAddr;
	int td, ntd, inCnt, outCnt;
	char tBuf[TCPECHOBUFSZ];
	
	localAddr.ipAddr = localHost;
	localAddr.sin_port = TCPPORT_ECHO;
	
	if ((td = tcpOpen()) < 0) {
		ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Unable to open a TCB (%d)", td));
	} else if ((inCnt = 10) != 0 
			&& (outCnt = tcpIOCtl(td, TCPCTLS_KEEPALIVE, &inCnt)) < 0) {
		ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Error %d setting keep alive", outCnt));
	} else for (;;) {
		if ((inCnt = tcpBind(td, &localAddr)) < 0) {
			ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Error %d on bind", inCnt));
			break;
		} else if ((ntd = tcpAccept(td, &peerAddr)) < 0) {
			ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Error %d on accept", ntd));
			break;
		} else {
			ECHODEBUG((LOG_INFO, TL_ECHO, "tcpEcho: connect %s:%u",
						ip_ntoa(htonl(peerAddr.sin_addr.s_addr)),
						peerAddr.sin_port));
			while ((inCnt = tcpRead(ntd, tBuf, TCPECHOBUFSZ)) >= 0) {
				if ((outCnt = tcpWrite(ntd, tBuf, inCnt)) < inCnt) {
					if (outCnt != TCPERR_EOF) {
						ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: TCP write err %d", outCnt));
					}
					break;
				}
			}
			if (inCnt != TCPERR_EOF) {
				ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: TCP read err %d", inCnt));
			}
		}
		if (ntd >= 0) {
			ECHODEBUG((LOG_INFO, TL_ECHO, "tcpEcho: disconnect %d %s:%u", ntd,
						ip_ntoa(htonl(peerAddr.sin_addr.s_addr)),
						peerAddr.sin_port));
			if ((inCnt = tcpDisconnect(ntd)) < 0) {
				ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Disconnect err %d", inCnt));
				break;
				
			} else if ((inCnt = tcpWait(ntd)) < 0) {
				ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Close wait err %d", inCnt));
				break;
			}
		}
	}
	if (td >= 0 && td != ntd) {
		ECHODEBUG((LOG_INFO, TL_ECHO, "tcpEcho: disconnect %d %s:%u", td,
					ip_ntoa(htonl(peerAddr.sin_addr.s_addr)),
					peerAddr.sin_port));
		if ((inCnt = tcpDisconnect(td)) < 0) {
			ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Disconnect err %d", inCnt));
		}
	}
	OSTaskDel(OS_PRIO_SELF);
}
#endif


/*
 * resendTimeout - The function invoked when the resend timer expires.
 */
static void resendTimeout(void *arg)
{
	register TCPCB *tcb = (TCPCB *)arg;

	if(tcb == NULL) {
		TCPDEBUG((LOG_ERR, TL_TCP, "resendTimeout: Null arg"));
		return;
	} else if (tcb->state == CLOSED) {
		TCPDEBUG((LOG_ERR, TL_TCP, "resendTimeout: Connection closed"));
		return;
	}

	/* Make sure the timer has stopped (we might have been kicked) */
	timerClear(&tcb->resendTimer); 

	TCPDEBUG((tcb->traceLevel, TL_TCP, "resendTimeout[%d]: state=%s", 
				(int)(tcb - &tcbs[0]),
				tcbStates[tcb->state]));
				
	/* 
	 * Check if the timer was set (i.e. there is unacknowledged output 
	 * or we're in TIME_WAIT or in FINWAIT2).
	 */
	if (tcb->snd.una != tcb->snd.nxt 
			|| tcb->state == TIME_WAIT || (tcb->state == FINWAIT2)) {
		/* If the timer hasn't expired, reset it. */
		if (diffTime(tcb->retransTime) > 0) {
			TCPDEBUG((tcb->traceLevel, TL_TCP, 
						"resendTimeout[%d]: Timer reset for %lu in %s", 
						(int)(tcb - & tcbs[0]),
						tcb->retransTime - OSTimeGet(), tcbStates[tcb->state]));
			timeoutJiffy(&tcb->resendTimer, tcb->retransTime, resendTimeout, tcb);
		
		/* Otherwise resend the last unacknowledged segment. */
		} else {
			/* CRITICAL - Prevent tcpInput from updating retransCnt at same time. */
			OS_ENTER_CRITICAL();
			/*
			 * If it's the 2MSL timer that has expired or if we've timed out in
			 * FINWAIT2 close the connection without error. 
			 */
			if (tcb->state == TIME_WAIT 
					|| (tcb->state == FINWAIT2 && tcb->freeOnClose)) {
				OS_EXIT_CRITICAL();
				closeSelf(tcb, 0);
				
			/* Check if we've timed out on retransmissions. */
			} else if (tcb->retransCnt++ >= MAXRETRANS) {
				OS_EXIT_CRITICAL();
				closeSelf(tcb, TCPERR_TIMEOUT);
				
			/* Normal retransmission timeout - reset pointers and invoke tcpOutput(). */
			} else {
				OS_EXIT_CRITICAL();

				/* 
				 * Wait for tcpOutput() to clear critical section before setting
				 * up for resend.
				 */
				OSSemPend(tcb->mutex, 0);
				tcb->flags |= RETRAN;	/* Indicate > 1  transmission */
				tcb->backoff++;
				tcb->snd.ptr = tcb->snd.una;
				/* Reduce slowstart threshold to half current window */
				tcb->ssthresh = tcb->cwind / 2;
				tcb->ssthresh = MAX(tcb->ssthresh, tcb->mss);
				/* Shrink congestion window to 1 packet */
				tcb->cwind = tcb->mss;
				OSSemPost(tcb->mutex);
				
				tcpOutput(tcb);
			}
		}
	}
}
		
	
/*
 * keepTimeout - The function invoked when the keep alive timer expires.
 */
static void keepTimeout(void *arg)
{
	register TCPCB *tcb = (TCPCB *)arg;

	/* Check if we're doing keep alives. */
	if (tcb->keepAlive 
			&& (tcb->state == ESTABLISHED
				|| tcb->state == CLOSE_WAIT)) {
		/* 
		 * If we've received something less recently than the keep alive time,
		 * then reset the timer.
		 */
		if ((long)(OSTimeGet() - tcb->keepTime) < 0) {
			TCPDEBUG((tcb->traceLevel, TL_TCP, 
						"keepTimeout[%d]: Keepalive reset for %lu in %s", 
						(int)(tcb - & tcbs[0]),
						tcb->keepTime - OSTimeGet(), 
						tcbStates[tcb->state]));
			timeoutJiffy(
					&tcb->keepTimer, 
					tcb->keepTime, 
					keepTimeout, 
					tcb);
					
		/*
		 * If we've exceeded our maximum keep alive timeouts, close the
		 * connection.
		 */
		} else if (tcb->keepProbes++ >= MAXKEEPTIMES) {
			TCPDEBUG((LOG_WARNING, TL_TCP, 
						"keepTimeout[%d]: Keepalive expired - closing",
						(int)(tcb - & tcbs[0])));
			closeSelf(tcb, TCPERR_TIMEOUT);
			
		/*
		 * Reset the timer and send a keep alive probe.
		 */
		} else {
			tcb->keepTime = OSTimeGet() + tcb->keepAlive;
			TCPDEBUG((tcb->traceLevel, TL_TCP, 
						"keepTimeout[%d]: Keepalive set for %lu in %s", 
						(int)(tcb - & tcbs[0]),
						tcb->keepTime - OSTimeGet(), 
						tcbStates[tcb->state]));
			timeoutJiffy(
					&tcb->keepTimer, 
					tcb->keepTime, 
					keepTimeout, 
					tcb);
			
			OS_ENTER_CRITICAL();
			tcb->flags |= FORCE | KEEPALIVE;
			OS_EXIT_CRITICAL();
			
			tcpOutput(tcb);
		}
	}
}


static void setState(TCPCB *tcb, TCPState newState)
{
	register TCPState oldState;

	OS_ENTER_CRITICAL();
	if ((oldState = tcb->state) != newState) {
		tcb->state = newState;
		OS_EXIT_CRITICAL();
		
		TCPDEBUG((tcb->traceLevel, TL_TCP, "setState[%d]: %s from %s",
					(int)(tcb - &tcbs[0]),
					tcbStates[newState], tcbStates[oldState]));
					
		switch(newState){
		case FINWAIT2:
			/* 
			 * Limit the time that we'll wait for the other end to close.
			 * XXX To support a half close, you'll need to test for something
			 * here.
			 */
			tcb->retransTime = OSTimeGet() + MAXFINWAIT2 * TICKSPERSEC;
			TCPDEBUG((tcb->traceLevel + 1, TL_TCP, "setState[%d]: Timer %lu in %s", 
						(int)(tcb - & tcbs[0]),
						tcb->retransTime - OSTimeGet(), tcbStates[tcb->state]));
			timeoutJiffy(&tcb->resendTimer, tcb->retransTime, resendTimeout, tcb);
			/* FALL THROUGH... */
		case CLOSED:
		case ESTABLISHED:
		case LISTEN:
		case SYN_SENT:
		case SYN_RECEIVED:
		case FINWAIT1:
		case CLOSE_WAIT:
		case CLOSING:
		case LAST_ACK:
		case TIME_WAIT:
			/* 
			 * Inform user processes that the state has changed.  This leaves it
			 * up to the individual processes to decide if the new state is
			 * significant and if not, then to pend again.  Unfortunately
			 * uC/OS cannot tell us how many processes are waiting on any of
			 * these so we assume that there is at most one and if there is
			 * not one, it won't hurt doing the extra posts.
			 */
			OSSemPost(tcb->connectSem);
			OSSemPost(tcb->readSem);
			OSSemPost(tcb->writeSem);

			break;
			
		default:
			trace(LOG_ERR, "setState: Invalid new state %d old %d",
					newState, oldState);
			tcb->state = oldState;
			break;
		}
		
		/* If we're closed then free or unlink the control block. */
		if (newState == CLOSED) {
			if (tcb->freeOnClose)
				tcbFree(tcb);
			else
				tcbUnlink(tcb);
		}
	} else {
		OS_EXIT_CRITICAL();
	}
	
}

/*
 * Process an incoming acknowledgement and window indication.
 * From RFC page 70.
 * Return zero if successful, ACKDROP if the segment should
 * be dropped, ACKRESET if the segment should be rejected,
 * and ACKCLOSE if the connection is closed.
 */
static int procInFlags(TCPCB *tcb, TCPHdr *tcpHdr, IPHdr *ipHdr)
{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -