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

📄 nettcp.c

📁 一个uCOS-II下的tcpip协议栈实现
💻 C
📖 第 1 页 / 共 5 页
字号:
	"ACK+P+R",					/* 28 = 16 + 8 + 4 */
	"A+P+R+F",					/* 29 = 16 + 8 + 4 + 1 */
	"A+P+R+S",					/* 30 = 16 + 8 + 4 + 2 */
	"A+P+R+S+F"					/* 31 = 16 + 8 + 4 + 2 + 1 */
};

char tcpEchoStack[STACK_SIZE];			/* The TCP echo task stack. */


/***********************************/
/*** PUBLIC FUNCTION DEFINITIONS ***/
/***********************************/
/*
 * Initialize the TCP subsystem.
 */
void tcpInit(void)
{
	int i;
	
	/* The TCB free list. */
	memset(tcbs, 0, sizeof(tcbs));
	topTcpCB = &tcbs[0];
	for (i = 0; i < MAXTCP; i++) {
		tcbs[i].next = &tcbs[i + 1];
		/* Prev referencing self indicates that it's on the free list. */
		tcbs[i].prev = &tcbs[i];
		timerCreate(&tcbs[i].resendTimer);
		timerCreate(&tcbs[i].keepTimer);
		tcbs[i].state = CLOSED;
	}
	tcbs[MAXTCP - 1].next = NULL;

	/* The TCB hash table. */
	memset(&tcbTbl, 0, sizeof(tcbTbl));
	
	/* The TCP stats. */
#if STATS_SUPPORT > 0
	memset(&tcpStats, 0, sizeof(tcpStats));
	tcpStats.headLine.fmtStr	= "\t\tTCP STATISTICS\r\n";
	tcpStats.curFree.fmtStr		= "\tCURRENT FREE: %5lu\r\n";
	tcpStats.curFree.val		= MAXTCP;
	tcpStats.minFree.fmtStr		= "\tMINIMUM FREE: %5lu\r\n";
	tcpStats.minFree.val		= MAXTCP;
	tcpStats.runt.fmtStr		= "\tRUNT HEADERS: %5lu\r\n";
	tcpStats.checksum.fmtStr	= "\tBAD CHECKSUM: %5lu\r\n";
	tcpStats.conout.fmtStr		= "\tOUT CONNECTS: %5lu\r\n";
	tcpStats.conin.fmtStr		= "\tIN CONNECTS : %5lu\r\n";
	tcpStats.resetOut.fmtStr	= "\tRESETS SENT : %5lu\r\n";
	tcpStats.resetIn.fmtStr		= "\tRESETS REC'D: %5lu\r\n";
#endif
	
	/* The new sequence number offset. */
	newISNOffset = magic();
	
	/* Start the TCP echo server. */
	OSTaskCreate(tcpEcho, NULL, tcpEchoStack + STACK_SIZE, PRI_ECHO);
	
}

/* 
 * Return a new TCP descriptor on success or an error code (negative) on 
 *	failure. 
 */
int tcpOpen(void)
{
	int st;
	TCPCB *tcb;
	
	OS_ENTER_CRITICAL();
	if ((tcb = topTcpCB) != NULL) {
		topTcpCB = topTcpCB->next;
		STATS(if (--tcpStats.curFree.val < tcpStats.minFree.val)
				tcpStats.minFree.val = tcpStats.curFree.val;)
	}
	OS_EXIT_CRITICAL();
	
	if (!tcb)
		st = TCPERR_ALLOC;
	else {
		st = (int)(tcb - &tcbs[0]);
		tcb->next = tcb;		/* Self ref => unlinked. */
		tcb->prev = NULL;		/* Always NULL when neither free nor linked. */
		
		tcb->freeOnClose = 0;
		tcb->traceLevel = LOG_INFO;
		tcb->keepAlive = 0;
		tcb->keepProbes = 0;
		
		/* Grab semaphores. */
		if (!tcb->connectSem)
			if ((tcb->connectSem = OSSemCreate(0)) == NULL)
				st = TCPERR_ALLOC;
		if (!tcb->readSem)
			if ((tcb->readSem = OSSemCreate(0)) == NULL)
				st = TCPERR_ALLOC;
		if (!tcb->writeSem)
			if ((tcb->writeSem = OSSemCreate(0)) == NULL)
				st = TCPERR_ALLOC;
		if (!tcb->mutex)
			if ((tcb->mutex = OSSemCreate(1)) == NULL)
				st = TCPERR_ALLOC;
		TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpOpen[%d]: Opened", st));
	}
	
	return st;
}

/* 
 * Close a TCP connection and release the descriptor.
 * Any outstanding packets in the queues are dropped.
 * Return 0 on success when the peer acknowledges our message
 * or an error code on failure. 
 */
int tcpClose(u_int td)
{
	int st = 0;
	TCPCB *tcb = &tcbs[td];

	/* Protect from race on tcb->state. */
	OS_ENTER_CRITICAL();	
	if (td >= MAXTCP || tcb->prev == tcb) {
		OS_EXIT_CRITICAL();
		st = TCPERR_PARAM;

	} else if (tcb->state == CLOSED) {
		OS_EXIT_CRITICAL();
		TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpClose[%d]: Freeing closed", td));
		tcbFree(tcb);
		
	} else {
		/* 
		 * Initiate a half-close on our side by sending a FIN.  The
		 * freeOnClose flag is set so the TCB will be freed when the
		 * state reaches CLOSED.  Note that a timer will limit the
		 * time that we wait in FINWAIT2.
		 */
		tcb->freeOnClose = !0;
		OS_EXIT_CRITICAL();
		
		st = tcpDisconnect(td);
		TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpClose[%d]: Closed", td));
	}	
	return st;
}

/*
 * Bind an IP address and port number in the sockaddr structure as our
 * address on a TCP connection.
 * Note: The IP address must be zero (wild) or equal to localHost since that
 * is all that ipDispatch() will recognize.  You can only bind a CLOSED
 * connection.
 * Return 0 on success, an error code on failure.
 */
int tcpBind(u_int td, struct sockaddr_in *myAddr)
{
	int st = 0;
	TCPCB *tcb = &tcbs[td];
	
	if (td >= MAXTCP || tcb->prev == tcb || !myAddr)
		st = TCPERR_PARAM;
	else if (myAddr->ipAddr != 0 && myAddr->ipAddr != localHost)
		st = TCPERR_INVADDR;
	else if (tcb->state != CLOSED)
		st = TCPERR_CONNECT;	/* Can't bind an active connection. */
	else {
		tcb->ipSrcAddr = htonl(myAddr->ipAddr);
		tcb->tcpSrcPort = htons(myAddr->sin_port);

		TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpBind[%d]: to %s:%u mss %d", 
					(int)(tcb - &tcbs[0]),
					ip_ntoa(tcb->ipSrcAddr), ntohs(tcb->tcpSrcPort),
					tcb->mss));
	}
	
	return st;
}

/*
 * Establish a connection with a remote host.  Unless tcpBind() has been called,
 * the local IP address and port number are generated automatically.
 * Return 0 on success, an error code on failure.
 */
int tcpConnectJiffy(u_int td, const struct sockaddr_in *remoteAddr, u_char tos, u_int timeout)
{
	int st = 0;
	TCPCB *tcb = &tcbs[td];
	u_long abortTime;
	long dTime = timeout;
	
	if (timeout)
		abortTime = jiffyTime() + timeout;
		
	if (td >= MAXTCP || tcb->prev == tcb || !remoteAddr)
		st = TCPERR_PARAM;
	else if (remoteAddr->ipAddr == 0 || remoteAddr->sin_port == 0)
		st = TCPERR_INVADDR;
	else if (tcb->ipSrcAddr == 0 && localHost == 0)
		st = TCPERR_CONFIG;
	else if (tcb->state != CLOSED)
		st = TCPERR_CONNECT;	/* Already connected! */
	else {
		tcbInit(tcb);
		tcb->ipTOS = tos;
		if (tcb->ipSrcAddr == 0)
			tcb->ipSrcAddr = htonl(localHost);
		if (tcb->tcpSrcPort == 0) {
			OS_ENTER_CRITICAL();
			tcb->tcpSrcPort = htons(tcpFreePort++);
			OS_EXIT_CRITICAL();
		}
		tcb->ipDstAddr = htonl(remoteAddr->ipAddr);
		tcb->tcpDstPort = htons(remoteAddr->sin_port);

		/* Initialize connection parameters. */		
		tcb->rcv.wnd = TCP_DEFWND;
		tcb->mss = ipMTU(tcb->ipDstAddr) - sizeof(IPHdr) - sizeof(TCPHdr);
		tcb->mss = MAX(tcb->mss, TCP_MINMSS);
		tcb->minFreeBufs = ((tcb->mss + NBUFSZ) / NBUFSZ);

		/* 
		 * Load the connection structure and link the TCB into the connection
		 * table so that tcpInput can find it.
		 */
		tcb->conn.remoteIPAddr = tcb->ipDstAddr;
		tcb->conn.remotePort = tcb->tcpDstPort;
		tcb->conn.localIPAddr = tcb->ipSrcAddr;
		tcb->conn.localPort = tcb->tcpSrcPort;
		tcbLink(tcb);
		
		TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpConnect[%d]: to %s:%u mss %d", 
					(int)(tcb - &tcbs[0]),
					ip_ntoa(tcb->ipDstAddr), ntohs(tcb->tcpDstPort),
					tcb->mss));
		
		/* Send SYN, go into SYN_SENT state */
		tcb->flags |= ACTIVE;
		sendSyn(tcb);
		setState(tcb, SYN_SENT);
		tcpOutput(tcb);
		STATS(tcpStats.conout.val++;)
		
		/* Wait for connection or failure. */
		while(tcb->state != ESTABLISHED && !st) {
			if (tcb->state == CLOSED) {
				/* 
				 * Post the connect semaphore in case another task was also
				 * waiting on it.  Unlikely for connect but a single extra
				 * post costs little and improves robustness.
				 */
				OSSemPost(tcb->connectSem);
				if (tcb->closeReason)
					st = tcb->closeReason;
				else
				    st = TCPERR_EOF;
			} else if (!timeout || (dTime = diffJTime(abortTime)) > 0) {
				OSSemPend(tcb->connectSem, (UINT)dTime);
			} else {					/* Abort on timeout. */
				closeSelf(tcb, TCPERR_TIMEOUT);
				tcbUnlink(tcb);
			}
		}
	}
			
	TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpConnect[%d]: %s", 
				(int)(tcb - &tcbs[0]), tcbStates[tcb->state]));
	
	return st;
}

/*
 * tcpDisconnect - Tell the peer that we will not be sending any more data
 * (i.e. perform a half close on a connection).  tcpRead() will then
 * wait until the connection closes.
 * Return 0 when the peer acknowledges our message or an error code on
 * failure.
 */
int tcpDisconnect(u_int td)
{
	int st = 0;
	TCPCB *tcb = &tcbs[td];
	
	TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpDisconnect[%d]: state %s", 
				(int)(tcb - &tcbs[0]), tcbStates[tcb->state]));
					
	if (td >= MAXTCP || tcb->prev == tcb)
		st = TCPERR_PARAM;
		
	else {
		switch(tcb->state){
		case LISTEN:
		case SYN_SENT:
			/*
			 * We haven't established a connection yet so we can just
			 * close this.
			 */
			closeSelf(tcb, 0);
			break;
			
		case SYN_RECEIVED:
		case ESTABLISHED:
			/* 
			 * Initiate a half-close on our side by sending a FIN.
			 * Our FIN is indicated by setting sndcnt to the number of bytes in
			 * the output queue + 1. 
			 */
			tcb->sndcnt++;
			tcb->snd.nxt++;
			setState(tcb, FINWAIT1);
			tcpOutput(tcb);
			break;
			
		case CLOSE_WAIT:
			/* 
			 * The peer has initiated a half-close.  We'll ACK it and complete
			 * the close by sending a FIN and waiting for it to be acknowledged.
			 * Our FIN is indicated by setting sndcnt to the number of bytes in
			 * the output queue + 1. 
			 */
			tcb->sndcnt++;
			tcb->snd.nxt++;
			setState(tcb, LAST_ACK);
			tcpOutput(tcb);
			break;
			
		case FINWAIT1:
		case FINWAIT2:
		case LAST_ACK:
		case CLOSING:
		case TIME_WAIT:
			/* Do nothing - we're already closing! */
			break;
		
		case CLOSED:
			/* Nothing to do! */
			break;
		}
	}	
	return st;
}

/*
 * Set the number of backLog connections which will be queued to be picked up
 * by calls to accept.  Without this call, no connection will be opened until
 * tcpAccept() or tcpConnect() is called.
 * Return the actual size of the queue on success, an error code on failure.
 */
int tcpListen(u_int td, int backLog)
{
	int st = 0;
	TCPCB *tcb = &tcbs[td];
	
	if (td >= MAXTCP || tcb->prev == tcb)
		st = TCPERR_PARAM;
		
	else if (tcb->tcpSrcPort == 0)
		st = TCPERR_CONFIG;
		
	else {
		switch(tcb->state){
		case CLOSED:
			tcbInit(tcb);
			tcb->conn.localIPAddr = tcb->ipSrcAddr;
			tcb->conn.localPort = tcb->tcpSrcPort;
			/* XXX Do we want 0 or left over address? */
			tcb->conn.remoteIPAddr = 0;
			tcb->conn.remotePort = 0;
			tcbLink(tcb);
			setState(tcb, LISTEN);
			/* Fall through... */
		case LISTEN:
			st = tcb->listenQOpen = MIN(backLog, MAXLISTEN);
			tcb->flags |= CLONE;
			
			TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpListen[%d]: %s:%u #%u", 
						(int)(tcb - &tcbs[0]),
						ip_ntoa(tcb->ipSrcAddr), tcb->tcpSrcPort,
						tcb->listenQOpen));
			break;
		case SYN_SENT:
		case SYN_RECEIVED:
		case ESTABLISHED:
		case CLOSE_WAIT:
		case FINWAIT1:
		case FINWAIT2:
		case CLOSING:
		case LAST_ACK:
		case TIME_WAIT:
			st = TCPERR_CONNECT;
			break;
		}
	}	
	
	TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpListen[%d]: %s => %d", 
				(int)(tcb - &tcbs[0]), tcbStates[tcb->state], st));
					
	return st;
}

/*
 * Pick up a connection opened by a remote host.  tcpBind() must be used to
 * specify the local address (possibly zero) and port number (non-zero) for
 * the connection.  Unless tcpListen() has been called, no connection will
 * be accepted until this is called.
 * Return a new TCP descriptor for the opened connection on success, an
 * error code on failure.  The peer's IP and port values are returned
 * in peerAddr.
 */
#pragma argsused
int tcpAcceptJiffy(u_int td, struct sockaddr_in *peerAddr, u_int timeout)
{
	int st = 0;
	TCPCB *ntcb = NULL;
	TCPCB *tcb = &tcbs[td];
	u_long abortTime;
	long dTime = timeout;
	
	TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpAccept[%d]: accepting %s:%u", 
				(int)(tcb - &tcbs[0]),
				ip_ntoa(tcb->ipSrcAddr), ntohs(tcb->tcpSrcPort)));
					
	if (timeout)
		abortTime = jiffyTime() + timeout;
		
	if (td >= MAXTCP || tcb->prev == tcb)
		st = TCPERR_PARAM;
		
	else if (tcb->tcpSrcPort == 0)
		st = TCPERR_CONFIG;
		
	else {
		switch(tcb->state){
		case CLOSED:
			tcbInit(tcb);
			tcb->conn.localIPAddr = tcb->ipSrcAddr;
			tcb->conn.localPort = tcb->tcpSrcPort;
			/* XXX Do we want 0 or whatever's in peerAddr? */
			tcb->conn.remoteIPAddr = 0;
			tcb->conn.remotePort = 0;

⌨️ 快捷键说明

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