📄 nettcp.c
字号:
/* 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, &err);
} else { /* Abort on timeout. */
closeSelf(tcb, TCPERR_TIMEOUT);
tcbUnlink(tcb);
}
}
#endif
}
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.
*/
int tcpAcceptJiffy(u_int td, struct sockaddr_in *peerAddr, u_int timeout)
{
int st = 0;
TCPCB *ntcb = NULL;
TCPCB *tcb = &tcbs[td];
#if ONETASK_SUPPORT == 0
u_long abortTime;
#endif
long dTime = timeout;
UBYTE err;
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpAccept[%d]: accepting %s:%u",
(int)(tcb - &tcbs[0]),
ip_ntoa(tcb->ipSrcAddr), ntohs(tcb->tcpSrcPort)));
/* We don't use the timeout argument when running in a single task!
We also don't want to be able to block.
And 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->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;
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();
/* We don't use the timeout argument when running in a single task!
We also don't want to be able to block.
And 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->connectSem, (UINT)dTime, &err);
else
#endif
st = TCPERR_TIMEOUT; /* Abort on timeout. */
OS_ENTER_CRITICAL();
}
}
OS_EXIT_CRITICAL();
} else {
/* We don't use the timeout argument when running in a single task!
We also don't want to be able to block.
And want to be able to do a quick poll, without having to make a timeout of at least 1 jiffy. */
#if ONETASK_SUPPORT == 0
while(CLOSED < tcb->state && tcb->state < ESTABLISHED) {
if (!timeout || (dTime = diffJTime(abortTime)) > 0)
OSSemPend(tcb->connectSem, (UINT)dTime, &err);
else {
closeSelf(tcb, TCPERR_TIMEOUT);
tcbUnlink(tcb);
}
}
#endif
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];
#if ONETASK_SUPPORT == 0
u_long abortTime;
#endif
long dTime = timeout;
u_int i;
UBYTE err;
int st = 0;
/* We don't use the timeout argument when running in a single task!
We also don't want to be able to block.
And 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 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:%s", td, i, 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) {
#if 0
nDEQUEUE(&tcb->rcvq, tcb->rcvBuf);
#else
OS_ENTER_CRITICAL();
if (!(&tcb->rcvq) || !tcb->rcvq.qHead) {
OS_EXIT_CRITICAL();
tcb->rcvBuf = NULL;
} else {
tcb->rcvBuf = tcb->rcvq.qHead->nextChain;
tcb->rcvq.qHead = tcb->rcvBuf;
if (tcb->rcvq.qHead == NULL)
tcb->rcvq.qTail = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -