📄 nettcp.c
字号:
}
/*
* 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;
#if ONETASK_SUPPORT == 0
u_long abortTime;
#endif
long dTime = timeout;
u_int segSize;
int sendSize;
int st = 0;
u_int8 err;
/* We don't use the timeout argument when running in a single task!
We also don't want to be able to block.
And we want to be able to do a quick poll, without having to make a timeout of at least 1 jiffy. */
#if ONETASK_SUPPORT == 0
if (timeout)
abortTime = jiffyTime() + timeout;
#endif
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((unsigned)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) {
/* We don't use the timeout argument when running in a single task!
We also don't want to be able to block.
And we want to be able to do a quick poll, without having to make a timeout of at least 1 jiffy. */
#if ONETASK_SUPPORT == 0
if (!timeout || (dTime = diffJTime(abortTime)) > 0)
OSSemPend(tcb->writeSem, (UINT)dTime, &err);
else
#endif
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() < (unsigned long)(tcb->minFreeBufs + (sendSize / NBUFSZ) * 2)) {
/* We don't use the timeout argument when running in a single task!
We also don't want to be able to block.
And we want to be able to do a quick poll, without having to make a timeout of at least 1 jiffy. */
#if ONETASK_SUPPORT == 0
if (!timeout || (dTime = diffJTime(abortTime)) > 0)
OSSemPend(tcb->writeSem, MIN((UINT)dTime, WRITESLEEP), &err);
else
#endif
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, (unsigned)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, &err);
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;
u_int8 err;
/* 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;
/* We don't support blocking when running in a single task! */
#if ONETASK_SUPPORT == 0
else while (tcb->state != CLOSED)
OSSemPend(tcb->connectSem, 0, &err);
#else
else st = TCPERR_TIMEOUT;
#endif
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. */
int 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((unsigned short)(ipHdr->ip_len - sizeof(IPHdr)));
/* Validate the TCP checksum including fields from IP TTL. */
if ((chkSum = inChkSum(inBuf, (unsigned short)(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;
}
NTOHL(tcpHdr->seq);
NTOHL(tcpHdr->ack);
NTOHS(tcpHdr->win);
NTOHS(tcpHdr->urgent);
segLen = ipHdr->ip_len - sizeof(IPHdr) - tcpHeadLen;
/* Find the connection if any. */
conn.localIPAddr = ipHdr->ip_dst.s_addr;
conn.localPort = tcpHdr->dstPort;
conn.remoteIPAddr = ipHdr->ip_src.s_addr;
conn.remotePort = tcpHdr->srcPort;
if((tcb = tcbLookup(&conn)) == NULL) {
TCPCB *ntcb;
if(!(tcpHdr->flags & TH_SYN)) {
/* No open TCB for this connection so reject */
tcpReset(inBuf, ipHdr, tcpHdr, (unsigned short)segLen);
return;
}
/*
* Check for a LISTEN on this connection request.
*/
conn.remoteIPAddr = 0;
conn.remotePort = 0;
if((tcb = tcbLookup(&conn)) == NULL) {
/*
* Could be a LISTEN with a null local address.
*/
conn.localIPAddr = 0;
if((tcb = tcbLookup(&conn)) == NULL) {
/* No unspecified LISTEN so reject */
tcpReset(inBuf, ipHdr, tcpHdr, (unsigned short)segLen);
return;
}
}
/* We've found a server listen socket, so clone the TCB */
if(tcb->flags & CLONE) {
// OS_EVENT *connectSem; /* Semaphore for connections. */
// OS_EVENT *readSem; /* Semaphore for read function. */
// OS_EVENT *writeSem; /* Semaphore for write function. */
// OS_EVENT *mutex; /* Semaphore for mutex. */
u_long connectSem; /* Semaphore for connections. */
u_long readSem; /* Semaphore for read function. */
u_long writeSem; /* Semaphore for write function. */
u_long mutex; /* Semaphore for mutex. */
/*
* If no room in the listen queue, we have to reject the connection.
*/
if (tcb->listenQOpen < listenQLen(tcb)) {
tcpReset(inBuf, ipHdr, tcpHdr, (unsigned short)segLen);
return;
}
/* Get a free TCB. */
OS_ENTER_CRITICAL();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -