📄 tcp.c
字号:
// Check to make sure that we received a TX Buffer
if(ps->TxBuffer == INVALID_BUFFER)
return FALSE;
ps->TxCount = 0;
IPSetTxBuffer(ps->TxBuffer, sizeof(TCP_HEADER));
}
ps->Flags.bIsTxInProgress = TRUE;
MACPut(byte);
ps->RemoteWindow--;
if(++ps->TxCount >= MAX_TCP_DATA_LEN)
TCPFlush(s);
return TRUE;
}
/*********************************************************************
* Function: BOOL TCPDiscard(TCP_SOCKET s)
*
* PreCondition: TCPInit() is already called.
*
* Input: s - socket
*
* Output: TRUE if socket received data was discarded
* FALSE if socket received data was already
* discarded.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
BOOL TCPDiscard(TCP_SOCKET s)
{
SOCKET_INFO* ps;
ps = &TCB[s];
// This socket must contain data for it to be discarded.
if(!ps->Flags.bIsGetReady)
return FALSE;
MACDiscardRx();
ps->Flags.bIsGetReady = FALSE;
return TRUE;
}
/*********************************************************************
* Function: WORD TCPGetArray(TCP_SOCKET s, BYTE *buffer,
* WORD count)
*
* PreCondition: TCPInit() is already called AND
* TCPIsGetReady(s) == TRUE
*
* Input: s - socket
* buffer - Buffer to hold received data.
* count - Buffer length
*
* Output: Number of bytes loaded into buffer.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
WORD TCPGetArray(TCP_SOCKET s, BYTE *buffer, WORD count)
{
SOCKET_INFO *ps;
ps = &TCB[s];
if ( ps->Flags.bIsGetReady )
{
if ( ps->Flags.bFirstRead )
{
// Position read pointer to begining of TCP data
IPSetRxBuffer(sizeof(TCP_HEADER));
ps->Flags.bFirstRead = FALSE;
}
ps->Flags.bIsTxInProgress = TRUE;
return MACGetArray(buffer, count);
}
else
return 0;
}
/*********************************************************************
* Function: BOOL TCPGet(TCP_SOCKET s, BYTE *byte)
*
* PreCondition: TCPInit() is already called AND
* TCPIsGetReady(s) == TRUE
*
* Input: s - socket
* byte - Pointer to a byte.
*
* Output: TRUE if a byte was read.
* FALSE if byte was not read.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
BOOL TCPGet(TCP_SOCKET s, BYTE *byte)
{
SOCKET_INFO* ps;
ps = &TCB[s];
if ( ps->Flags.bIsGetReady )
{
if ( ps->Flags.bFirstRead )
{
// Position read pointer to begining of correct
// buffer.
IPSetRxBuffer(sizeof(TCP_HEADER));
ps->Flags.bFirstRead = FALSE;
}
if ( ps->RxCount == 0 )
{
MACDiscardRx();
ps->Flags.bIsGetReady = FALSE;
return FALSE;
}
ps->RxCount--;
*byte = MACGet();
return TRUE;
}
return FALSE;
}
/*********************************************************************
* Function: BOOL TCPIsGetReady(TCP_SOCKET s)
*
* PreCondition: TCPInit() is already called.
*
* Input: s - socket to test
*
* Output: TRUE if socket 's' contains any data.
* FALSE if socket 's' does not contain any data.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
BOOL TCPIsGetReady(TCP_SOCKET s)
{
/*
* A socket is said to be "Get" ready when it has already
* received some data. Sometime, a socket may be closed,
* but it still may contain data. Thus in order to ensure
* reuse of a socket, caller must make sure that it reads
* a socket, if is ready.
*/
return(TCB[s].Flags.bIsGetReady);
}
/*********************************************************************
* Function: void TCPTick(void)
*
* PreCondition: TCPInit() is already called.
*
* Input: None
*
* Output: Each socket FSM is executed for any timeout
* situation.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
void TCPTick(void)
{
TCP_SOCKET s;
TICK diffTicks;
TICK tick;
SOCKET_INFO* ps;
DWORD seq;
BYTE flags;
flags = 0x00;
// Periodically all "not closed" sockets must perform timed operations
for(s = 0; s < MAX_SOCKETS; s++)
{
ps = &TCB[s];
if ( ps->Flags.bIsGetReady || ps->Flags.bIsTxInProgress )
continue;
// Closed or Passively Listening socket do not care
// about timeout conditions.
if ( (ps->smState == TCP_CLOSED) ||
(ps->smState == TCP_LISTEN &&
ps->Flags.bServer == TRUE) )
continue;
tick = TickGet();
// Calculate timeout value for this socket.
diffTicks = TickGetDiff(tick, ps->startTick);
// If timeout has not occured, do not do anything.
if(diffTicks <= ps->TimeOut)
continue;
// Most states require retransmission, so check for transmitter
// availability right here - common for all.
if(!IPIsTxReady(TRUE))
return;
// Restart timeout reference.
ps->startTick = TickGet();
// Update timeout value if there is need to wait longer.
ps->TimeOut <<= 1;
// This will be one more attempt.
ps->RetryCount++;
// A timeout has occured. Respond to this timeout condition
// depending on what state this socket is in.
switch(ps->smState)
{
case TCP_SYN_SENT:
// Keep sending SYN until we hear from remote node.
// This may be for infinite time, in that case
// caller must detect it and do something.
// Bug Fix: 11/1/02
flags = SYN;
break;
case TCP_SYN_RECEIVED:
// We must receive ACK before timeout expires.
// If not, resend SYN+ACK.
// Abort, if maximum attempts counts are reached.
if(ps->RetryCount <= MAX_RETRY_COUNTS)
{
flags = SYN | ACK;
}
else
{
if(ps->Flags.bServer)
{
ps->smState = TCP_LISTEN;
}
else
{
flags = SYN;
ps->smState = TCP_SYN_SENT;
}
}
break;
case TCP_ESTABLISHED:
#if !defined(TCP_NO_WAIT_FOR_ACK)
// Don't let this connection idle for very long time.
// If we did not receive or send any message before timeout
// expires, close this connection.
if(ps->RetryCount <= MAX_RETRY_COUNTS)
{
if(ps->TxBuffer != INVALID_BUFFER)
{
MACSetTxBuffer(ps->TxBuffer, 0);
MACFlush();
}
else
flags = ACK;
}
else
{
// Forget about previous transmission.
if(ps->TxBuffer != INVALID_BUFFER)
{
MACDiscardTx(ps->TxBuffer);
ps->TxBuffer = INVALID_BUFFER;
}
#endif
// Request closure.
flags = FIN | ACK;
DebugPrint("!");
ps->smState = TCP_FIN_WAIT_1;
#if !defined(TCP_NO_WAIT_FOR_ACK)
}
#endif
break;
case TCP_FIN_WAIT_1:
if(ps->RetryCount <= MAX_RETRY_COUNTS)
{
// Send another FIN
flags = FIN;
}
else
{
// Close on our own, we can't seem to communicate
// with the remote node anymore
CloseSocket(ps);
}
break;
case TCP_FIN_WAIT_2:
case TCP_CLOSING:
// Close on our own, we can't seem to communicate
// with the remote node anymore
CloseSocket(ps);
break;
case TCP_TIME_WAIT:
// Wait around for a while (2MSL) and then goto closed state
CloseSocket(ps);
break;
case TCP_CLOSE_WAIT:
flags = FIN;
ps->smState = TCP_LAST_ACK;
break;
case TCP_LAST_ACK:
// Send some more FINs or close anyway
if(ps->RetryCount <= MAX_RETRY_COUNTS)
flags = FIN;
else
CloseSocket(ps);
break;
}
if(flags)
{
if(flags & ACK)
seq = ps->SND_SEQ;
else
seq = ps->SND_SEQ++;
SendTCP(&ps->remote,
ps->localPort,
ps->remotePort,
seq,
ps->SND_ACK,
flags);
}
}
}
/*********************************************************************
* Function: BOOL TCPProcess(NODE_INFO* remote,
* IP_ADDR *localIP,
* WORD len)
*
* PreCondition: TCPInit() is already called AND
* TCP segment is ready in MAC buffer
*
* Input: remote - Remote node info
* len - Total length of TCP semgent.
*
* Output: TRUE if this function has completed its task
* FALSE otherwise
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
BOOL TCPProcess(NODE_INFO *remote, IP_ADDR *localIP, WORD len)
{
TCP_HEADER TCPHeader;
PSEUDO_HEADER pseudoHeader;
TCP_SOCKET socket;
WORD_VAL checksum1;
WORD_VAL checksum2;
BYTE optionsSize;
// Calculate IP pseudoheader checksum.
pseudoHeader.SourceAddress = remote->IPAddr;
pseudoHeader.DestAddress = *localIP;
pseudoHeader.Zero = 0x0;
pseudoHeader.Protocol = IP_PROT_TCP;
pseudoHeader.TCPLength = len;
SwapPseudoTCPHeader(pseudoHeader);
checksum1.Val = ~CalcIPChecksum((BYTE*)&pseudoHeader,
sizeof(pseudoHeader));
// Now calculate TCP packet checksum in NIC RAM - should match
// pesudo header checksum
checksum2.Val = CalcIPBufferChecksum(len);
// Compare checksums. Note that the endianness is different.
if(checksum1.v[0] != checksum2.v[1] || checksum1.v[1] != checksum2.v[0])
{
MACDiscardRx();
return TRUE;
}
// Retrieve TCP header.
IPSetRxBuffer(0);
MACGetArray((BYTE*)&TCPHeader, sizeof(TCPHeader));
SwapTCPHeader(&TCPHeader);
// Skip over options and retrieve all data bytes.
optionsSize = (BYTE)((TCPHeader.DataOffset.Val << 2)-
sizeof(TCPHeader));
len = len - optionsSize - sizeof(TCPHeader);
// Position packet read pointer to start of data area.
IPSetRxBuffer((TCPHeader.DataOffset.Val << 2));
// Find matching socket.
socket = FindMatchingSocket(&TCPHeader, remote);
if(socket != INVALID_SOCKET)
{
HandleTCPSeg(socket, remote, &TCPHeader, len);
}
else
{
// If this is an unknown socket, or we don't have any
// listening sockets available, discard it we can't
// process it right now
MACDiscardRx();
// // Send a RESET to the remote node is it knows that we
// // are not available
// TCPHeader.AckNumber += len;
// if( TCPHeader.Flags.bits.flagSYN ||
// TCPHeader.Flags.bits.flagFIN )
// TCPHeader.AckNumber++;
//
// SendTCP(remote,
// TCPHeader.DestPort,
// TCPHeader.SourcePort,
// TCPHeader.AckNumber,
// TCPHeader.SeqNumber,
// RST);
}
return TRUE;
}
/*********************************************************************
* Function: static void TransmitTCP(NODE_INFO* remote
* TCP_PORT localPort,
* TCP_PORT remotePort,
* DWORD seq,
* DWORD ack,
* BYTE flags,
* BUFFER buffer,
* WORD len)
*
* PreCondition: TCPInit() is already called AND
* TCPIsPutReady() == TRUE
*
* Input: remote - Remote node info
* localPort - Source port number
* remotePort - Destination port number
* seq - Segment sequence number
* ack - Segment acknowledge number
* flags - Segment flags
* buffer - Buffer to which this segment
* is to be transmitted
* len - Total data length for this segment.
*
* Output: A TCP segment is assembled and put to transmit.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
static void TransmitTCP(NODE_INFO *remote,
TCP_PORT localPort,
TCP_PORT remotePort,
DWORD tseq,
DWORD tack,
BYTE flags,
BUFFER buffer,
WORD len)
{
WORD_VAL checkSum;
TCP_HEADER header;
TCP_OPTIONS options;
PSEUDO_HEADER pseudoHeader;
// Make sure that this Tx buffer isn't currently being transmitted
while( !IPIsTxReady(TRUE) ); //TODO: This may need to be conditionally false
// Obtain an AutoFree buffer if this packet is a control packet
// only (contains no application data in an already allocated
// buffer)
if(buffer == INVALID_BUFFER)
buffer = MACGetTxBuffer(TRUE);
if(buffer == INVALID_BUFFER)
return;
IPSetTxBuffer(buffer, 0);
header.SourcePort = localPort;
header.DestPort = remotePort;
header.SeqNumber = tseq;
header.AckNumber = tack;
header.Flags.bits.Reserved2 = 0;
header.DataOffset.Reserved3 = 0;
header.Flags.byte = flags;
// Receive window = MAC Free buffer size - TCP header (20) - IP header (20)
// - ETHERNET header (14 if using NIC) .
header.Window = MACGetFreeRxSize();
#if !defined(STACK_USE_SLIP)
/*
* Limit one segment at a time from remote host.
* This limit increases overall throughput as remote host does not
* flood us with packets and later retry with significant delay.
*/
if ( header.Window >= MAC_RX_BUFFER_SIZE )
header.Window = MAC_RX_BUFFER_SIZE;
else if ( header.Window > 54 )
{
header.Window -= 54;
}
else
header.Window = 0;
#else
if ( header.Window > 40 )
{
header.Window -= 40;
}
else
header.Window = 0;
#endif
header.Checksum = 0;
header.UrgentPointer = 0;
SwapTCPHeader(&header);
len += sizeof(header);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -