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

📄 nettcp.c

📁 一个uCOS-II下的tcpip协议栈实现
💻 C
📖 第 1 页 / 共 5 页
字号:
			tcbLink(tcb);
			setState(tcb, LISTEN);
			/* Fall through... */
		case LISTEN:
			if (tcb->flags & CLONE) {
				OS_ENTER_CRITICAL();
				while(!ntcb && !st) {
					if (tcb->state != LISTEN) {
						st = TCPERR_CONNECT;
					} else if (!listenQEmpty(tcb)) {
						listenQPop(tcb, &ntcb);
					} else {
						OS_EXIT_CRITICAL();
						if (!timeout || (dTime = diffJTime(abortTime)) > 0)
							OSSemPend(tcb->connectSem, (UINT)dTime);
						else
							st = TCPERR_TIMEOUT;		/* Abort on timeout. */
						OS_ENTER_CRITICAL();
					} 
				}
				OS_EXIT_CRITICAL();
			} else {
				while(CLOSED < tcb->state && tcb->state < ESTABLISHED) {
					if (!timeout || (dTime = diffJTime(abortTime)) > 0)
						OSSemPend(tcb->connectSem, (UINT)dTime);
					else {
						closeSelf(tcb, TCPERR_TIMEOUT);
						tcbUnlink(tcb);
					}
				}
				if (tcb->state == ESTABLISHED)
					ntcb = tcb;
				else if (tcb->closeReason)
					st = tcb->closeReason;
				else
					st = TCPERR_CONNECT;
			}
			if (!st) {
				ntcb->flags &= ~CLONE;
				peerAddr->ipAddr = ntohl(tcb->ipDstAddr);
				peerAddr->sin_port = ntohs(tcb->tcpDstPort);
				st = (int)(ntcb - &tcbs[0]);
				TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpAccept[%d]: %s:%u", 
							(int)(tcb - &tcbs[0]),
							ip_ntoa(tcb->ipDstAddr), ntohs(tcb->tcpDstPort)));
			} else {
				TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpAccept[%d]: at %s => %d", 
							(int)(tcb - &tcbs[0]), tcbStates[tcb->state], st));
			}
			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;
		}
	}	
	return st;
}

/*
 * Read from a connected TCP connection.  If a timeout is non-zero, we block 
 *	until the requested data is received or the timeout expires.  Otherwise
 *	we block only until at least one byte has been received or an error
 *	occurs.
 * Note: Ideally we would return less than len bytes only if a PUSH flag
 *	was received however this is yet to be implemented.
 * Return the number of bytes read on success, an error code on failure. 
 */
int tcpRead(u_int td, void *s, u_int len)
{
	return tcpReadJiffy(td, s, len, 0);
}
int tcpReadJiffy(u_int td, void *s1, u_int len, u_int timeout)
{
	char *s = (char*)s1;
	TCPCB *tcb = &tcbs[td];
	u_long abortTime;
	long dTime = timeout;
	u_int i;
	int st = 0;

	if (timeout)
		abortTime = jiffyTime() + timeout;
		
	if (td >= MAXTCP || tcb->prev == tcb)
		st = TCPERR_PARAM;
		
	else if (tcb->state == CLOSED
				|| tcb->ipSrcAddr == 0
				|| tcb->tcpSrcPort == 0
				|| tcb->ipDstAddr == 0
				|| tcb->tcpDstPort == 0)
		st = TCPERR_CONNECT;
	
	/*
	 * Loop here until either we have received something, hit a snag, or had
	 *	our connection closed.
	 */
	else while (len) {
		/* If there's something in the receive buffer, start with that. */
		if (tcb->rcvBuf) {
			i = nTrim(s, &tcb->rcvBuf, len);
			TCPDEBUG((tcb->traceLevel + 1, TL_TCP, "tcpRead[%d]: %u:%.*H",
						td, i, min(60, i * 2), s));
			st += i;
			len -= i;
			s += i;
			
			OS_ENTER_CRITICAL();
			tcb->rcvcnt -= i;
			OS_EXIT_CRITICAL();

		/* 
		 * If there's something in the receive queue, dequeue the next segment. 
		 * If successful, adjust our receive window.
		 */
		} else if (tcb->rcvcnt != 0) {
			nDEQUEUE(&tcb->rcvq, tcb->rcvBuf);
			if (tcb->rcvBuf) {
				OS_ENTER_CRITICAL();
				i = tcb->rcv.wnd;
				/* 
				 * Since we queue buffer chains and not characters, the receive
				 * window is adjusted by multiples of buffer lengths.
				 *
				 * XXX Here we assume that normally each buffer chain is of
				 * length 1.  If this is not the case, it may be better to
				 * adjust the buffer size rather than complicate this.  Only
				 * if you want to support greatly varying segment sizes would
				 * it be worth tracking the number of buffers in each chain.
				 */
				if ((tcb->rcv.wnd += NBUFSZ) > TCP_DEFWND)
					tcb->rcv.wnd = TCP_DEFWND;
				/* Do a window update if it was closed. */
				if (i == 0) {
					tcb->flags |= FORCE;
					OS_EXIT_CRITICAL();
					
					tcpOutput(tcb);
				} else {
					OS_EXIT_CRITICAL();
				}
			}
		
		/*
		 * We've emptied the receive queue.  If we've copied something and
		 *	there's no timeout, let's return what we've got rather than 
		 *	waiting for more.
		 *
		 * XXX We should only exit here if a PUSH segment was received.
		 */
		} else if (st && !timeout)
			len = 0;
		
		/*
		 * If we're expecting something to come in, wait for it.  Otherwise,
		 * return EOF.
		 */
		else switch(tcb->state) {
		case LISTEN:
		case SYN_SENT:
		case SYN_RECEIVED:
		case ESTABLISHED:
		case FINWAIT1:
		case FINWAIT2:
			if (!timeout || (dTime = diffJTime(abortTime)) > 0)
				OSSemPend(tcb->readSem, (UINT)dTime);
			else
				len = 0;		/* Abort on timeout. */
			break;
		case CLOSED:
		case CLOSE_WAIT:
		case CLOSING:
		case LAST_ACK:
		case TIME_WAIT:
			if (tcb->closeReason)
				st = tcb->closeReason;
			else
				st = TCPERR_EOF;
			len = 0;
		    break;
		}
	}
	
	return st;
}

/*
 * Write to a connected TCP connection.  This blocks until either all bytes
 *	are queued, the timeout is reached, or an error occurs.
 * Return the number of bytes written on success, an error code on failure.
 */
int tcpWrite(u_int td, const void *s, u_int len)
{
	return tcpWriteJiffy(td, s, len, 0);
}
int tcpWriteJiffy(u_int td, const void *s1, u_int len, u_int timeout)
{
	const char *s = (char*)s1;
	TCPCB *tcb = &tcbs[td];
	NBuf *outBuf = NULL;
	u_long abortTime;
	long dTime = timeout;
	u_int segSize;
	int sendSize;
	int st = 0;

	if (timeout)
		abortTime = jiffyTime() + timeout;
		
	if (td >= MAXTCP || tcb->prev == tcb)
		st = TCPERR_PARAM;
		
	else if (tcb->state == CLOSED
				|| tcb->ipSrcAddr == 0
				|| tcb->tcpSrcPort == 0
				|| tcb->ipDstAddr == 0
				|| tcb->tcpDstPort == 0) {
		st = TCPERR_CONNECT;
	}
	
	/*
	 * Loop here until either we have queued our data, hit a snag, had
	 * our connection closed, or timed out.
	 */
	else while (len) {
		/* 
		 * Wait for space in the queue to queue up what we've got up to
		 * a full length segment. 
		 */
		OS_ENTER_CRITICAL();
		sendSize = tcb->snd.wnd - tcb->sndcnt;
		OS_EXIT_CRITICAL();
		sendSize = MIN(sendSize, len);
		sendSize = MIN(sendSize, tcb->mss);
		
		/*
		 * Block if we can't send anything or if we've got our quota of 
		 * outstanding segments already in the queue.  It's up to the
		 * input side to wake us up when things open up.
		 */
		if (sendSize <= 0 || tcb->sndq.qLen >= TCP_MAXQUEUE) {
			if (!timeout || (dTime = diffJTime(abortTime)) > 0)
				OSSemPend(tcb->writeSem, (UINT)dTime);
			else
				len = 0;		/* Abort on timeout. */
			
		/* 
		 * Get a network buffer to fill.  Ensure that enough buffers remain
		 * on the free list to enable receiving an acknowledgement on a full
		 * length segment.
		 * If we fail, poll for free buffers until we get one or we time out.
		 */
		} else if (!outBuf) {
			if (nBUFSFREE() < tcb->minFreeBufs + (sendSize / NBUFSZ) * 2) {
				if (!timeout || (dTime = diffJTime(abortTime)) > 0)
					OSSemPend(tcb->writeSem, MIN((UINT)dTime, WRITESLEEP));
				else
					len = 0;		/* Abort on timeout. */
					
			} else {
				nGET(outBuf);
			}
			/* Loop again and update the open size. */
		
		/*
		 * Prepare and queue whatever we can.
		 */
		} else {
			nAPPEND(outBuf, s, sendSize, segSize);
			if (segSize > 0) {
				TCPDEBUG((tcb->traceLevel + 1, TL_TCP, "tcpWrite[%d]: %u:%.*H",
							td, segSize, min(60, segSize * 2), s));
				len -= segSize;
				s += segSize;
				switch(tcb->state) {
				case SYN_SENT:
				case SYN_RECEIVED:
				case ESTABLISHED:
				case CLOSE_WAIT:
					OSSemPend(tcb->mutex, 0);
					nENQUEUE(&tcb->sndq, outBuf);
					OSSemPost(tcb->mutex);
					outBuf = NULL;
					st += segSize;
					
					OS_ENTER_CRITICAL();
					tcb->sndcnt += segSize;
					OS_EXIT_CRITICAL();
					
					tcpOutput(tcb);
					break;
				case LISTEN:
				case FINWAIT1:
				case FINWAIT2:
				case CLOSING:
				case LAST_ACK:
				case TIME_WAIT:
				case CLOSED:
					nFreeChain(outBuf);
					len = 0;
					if (tcb->closeReason)
						st = tcb->closeReason;
					else
					    st = TCPERR_EOF;
					break;
				}
			}
		}
	}
	
	return st;
}


/*
 * tcpWait - Wait for the connection to be closed.  Normally this will be
 * done after a disconnect before trying to reuse the TCB.  This will fail
 * if the connection is not closing.
 * Returns 0 on success or an error code if the connection is not
 * closing.
 */
int tcpWait(u_int td)
{
	TCPCB *tcb = &tcbs[td];
	int st = 0;

	/* Here we allow the TCB to be on the free list. */
	if (td >= MAXTCP)
		st = TCPERR_PARAM;
		
	else if (tcb->state != CLOSED && tcb->state < FINWAIT1)
		st = TCPERR_CONNECT;
	
	else while (tcb->state != CLOSED)
		OSSemPend(tcb->connectSem, 0);
	
	return st;
}


/* 
 * Receive an incoming datagram.  This is called from IP with the IP and
 * TCP headers intact at the head of the buffer chain.
 */
void tcpInput(NBuf *inBuf, u_int ipHeadLen)
{
	
	TCPCB *tcb;
	Connection conn;
	u_int tcpHeadLen;			/* Length of TCP header. */
	int16 segLen;				/* TCP segment length exclusive of flags. */
	IPHdr *ipHdr;				/* Ptr to IP header in output buffer. */
	TCPHdr *tcpHdr;				/* Ptr to TCP header in output buffer. */
	
	u_int chkSum;
	static chkFail = 0;
	
	if (inBuf == NULL) {
		TCPDEBUG((LOG_ERR, TL_TCP, "tcpInput: Null input dropped"));
		return;
	}

	/*
	 * Strip off the IP options.  The TCP checksum includes fields from the
	 * IP header but without the options.
	 */
	if (ipHeadLen > sizeof(IPHdr)) {
		inBuf = ipOptStrip(inBuf, ipHeadLen);
		ipHeadLen = sizeof(IPHdr);
	}
	
	/*
	 * Get IP and TCP header together in first nBuf.
	 */
	if (inBuf->len < sizeof(TCPHdr) + sizeof(IPHdr)) {
		if ((inBuf = nPullup(inBuf, sizeof(TCPHdr) + ipHeadLen)) == 0) {
			STATS(tcpStats.runt.val++;)
			TCPDEBUG((LOG_ERR, TL_TCP, "tcpInput: Runt packet dropped"));
#if DEBUG_SUPPORT > 0
			nDumpChain(inBuf);
#endif
			return;
		}
	}
	ipHdr = nBUFTOPTR(inBuf, IPHdr *);
	/*
	 * Note: We use ipHeadLen below just in case we kept an option with
	 *	the IP header.
	 */
	tcpHdr = (TCPHdr *)((char *)ipHdr + ipHeadLen);

	/*
	 * Prepare the header for the TCP checksum.  The TCP checksum is
	 * computed on a pseudo IP header as well as the TCP header and
	 * the data segment.  The pseudo IP header includes the length
	 * (not including the length of the IP header), protocol, source
	 * address and destination address fields.  We prepare this by
	 * clearing the TTL field and loading the length in the IP checksum
	 * field.
	 */
	ipHdr->ip_ttl = 0;
	ipHdr->ip_sum = htons(ipHdr->ip_len - sizeof(IPHdr));
	
	/* Validate the TCP checksum including fields from IP TTL. */
	if ((chkSum = inChkSum(inBuf, ipHdr->ip_len - 8, 8)) != 0) {
		/* Checksum failed, ignore segment completely */
		STATS(tcpStats.checksum.val++;)
		TCPDEBUG((LOG_ERR, TL_TCP, "tcpInput: Bad checksum %X", chkSum));
#if DEBUG_SUPPORT > 0
		nDumpChain(inBuf);
#endif
		if (++chkFail > 3) {
			/* Break point. */
			TCPDEBUG((LOG_ERR, TL_TCP, "tcpInput: Serious checksum issue here."));
		}
		nFreeChain(inBuf);
		return;
	}

	/* Convert needed TCP fields to host byte order. */
	if ((tcpHeadLen = tcpHdr->tcpOff * 4) < sizeof(TCPHdr)) {
		/* TCP header is too small */
		STATS(tcpStats.runt.val++;)
		TCPDEBUG((LOG_ERR, TL_TCP, "tcpInput: Bad TCP header len %u", tcpHeadLen));
#if DEBUG_SUPPORT > 0
		nDumpChain(inBuf);
#endif
		nFreeChain(inBuf);
		return;
	}

⌨️ 快捷键说明

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