📄 nettcp.c
字号:
/******************************/
/*** PUBLIC DATA STRUCTURES ***/
/******************************/
#if 0
const DevDef tcpdef = {
tcpdValid,
tcpdValid,
tcpRead,
tcpWrite,
NULL,
NULL,
tcpIOCtl
};
#endif
#if STATS_SUPPORT > 0
TCPStats tcpStats;
#endif
/*****************************/
/*** LOCAL DATA STRUCTURES ***/
/*****************************/
/*
* TCP Control block free list.
*/
TCPCB tcbs[MAXTCP];
TCPCB *topTcpCB; /* Ptr to top TCB on free list. */
TCPCB *tcbTbl[NTCB]; /* Hash table for lookup. */
u_int16_t tcpFreePort = TCP_DEFPORT; /* Initial local port. */
u_int32_t newISNOffset; /* Offset for the next sequence number. */
/* TCB state labels for debugging. */
char *tcbStates[] = {
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RECEIVED",
"ESTABLISHED",
"FINWAIT1",
"FINWAIT2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT"
};
/* TCP Header Flag labels. */
#define TCPFLAGLABELMASK 0x1F /* We don't display URGENT. */
const char *tcpFlagLabel[] = {
"NONE", /* 0 */
"FIN", /* 1 */
"SYN", /* 2 */
"SYN+FIN", /* 3 = 2 + 1 */
"RST", /* 4 */
"RST+FIN", /* 5 = 4 + 1 */
"RST+SYN", /* 6 = 4 + 2 */
"RST+S+F", /* 7 = 4 + 2 + 1 */
"PUSH", /* 8 */
"PUSH+FIN", /* 9 = 8 + 1 */
"PUSH+SYN", /* 10 = 8 + 2 */
"PUSH+S+F", /* 11 = 8 + 2 + 1 */
"PUSH+RST", /* 12 = 8 + 4 */
"PUSH+R+F", /* 13 = 8 + 4 + 1 */
"PUSH+R+S", /* 14 = 8 + 4 + 2 */
"PUSH+R+S+F", /* 15 = 8 + 4 + 2 + 1 */
"ACK", /* 16 */
"ACK+FIN", /* 17 = 16 + 1 */
"ACK+SYN", /* 18 = 16 + 2 */
"ACK+S+F", /* 19 = 16 + 2 + 1 */
"ACK+RST", /* 20 = 16 + 4 */
"ACK+R+F", /* 21 = 16 + 4 + 1 */
"ACK+R+S", /* 22 = 16 + 4 + 2 */
"ACK+R+S+F", /* 23 = 16 + 4 + 2 + 1 */
"ACK+PUSH", /* 24 = 16 + 8 */
"ACK+P+F", /* 25 = 16 + 8 + 1 */
"ACK+P+S", /* 26 = 16 + 8 + 2 */
"ACK+P+S+F", /* 27 = 16 + 8 + 2 + 1 */
"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 */
};
#if ECHO_SUPPORT > 0
char tcpEchoStack[STACK_SIZE]; /* The TCP echo task stack. */
#endif
/***********************************/
/*** 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();
#if ECHO_SUPPORT > 0
/* Start the TCP echo server. */
OSTaskCreate(tcpEcho, NULL, tcpEchoStack + STACK_SIZE, PRI_ECHO);
#endif
}
/*
* Return a new TCP descriptor on success or an error code (negative) on
* failure.
*/
#if ONETASK_SUPPORT > 0
int tcpOpen(
void (*receiveEvent)(int, u_long),
void (*transmitEvent)(int, u_long),
void (*stateEvent)(int, TCPState, TCPState)
)
#else
int tcpOpen(void)
#endif
{
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. */
#if ONETASK_SUPPORT > 0
// Setup event functions
tcb->receiveEvent = receiveEvent;
tcb->transmitEvent = transmitEvent;
tcb->stateEvent = stateEvent;
#endif
tcb->freeOnClose = 0;
#if DEBUG_SUPPORT == 0
tcb->traceLevel = 0;
#else
tcb->traceLevel = LOG_INFO;
#endif
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];
#if ONETASK_SUPPORT == 0
u_long abortTime;
#endif
long dTime = timeout;
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 || !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++;)
/* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -