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

📄 nettcp.c

📁 uC/IP Release Notes These release notes for uC/IP are divided into the following sections
💻 C
📖 第 1 页 / 共 5 页
字号:
                    tcb->rcvq.qTail = NULL;
                tcb->rcvq.qLen--;
                OS_EXIT_CRITICAL();
                tcb->rcvBuf->nextChain = NULL;
            }
#endif
            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.
         */

/* 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      
        } else if (st && !timeout)
#else
    } else if (st)
#endif      
            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:

/* 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->readSem, (UINT)dTime, &err);
            else
#endif
                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;
#if ONETASK_SUPPORT == 0      
    u_long abortTime;
#endif
    long dTime = timeout;
    u_int segSize;
    int sendSize;
    int st = 0;
    UBYTE 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(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() < 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, 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;
    UBYTE 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(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;
    }
    NTOHL(tcpHdr->seq);

⌨️ 快捷键说明

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