📄 tcp.c
字号:
/*********************************************************************
*
* TCP Module for Microchip TCP/IP Stack
* Based on RFC 793
*
*********************************************************************
* FileName: TCP.C
* Dependencies: string.h
* StackTsk.h
* Helpers.h
* IP.h
* MAC.h
* ARP.h
* Tick.h
* TCP.h
* Processor: PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F
* Complier: Microchip C18 v3.02 or higher
* Microchip C30 v2.01 or higher
* Company: Microchip Technology, Inc.
*
* Software License Agreement
*
* This software is owned by Microchip Technology Inc. ("Microchip")
* and is supplied to you for use exclusively as described in the
* associated software agreement. This software is protected by
* software and other intellectual property laws. Any use in
* violation of the software license may subject the user to criminal
* sanctions as well as civil liability. Copyright 2006 Microchip
* Technology Inc. All rights reserved.
*
* This software is provided "AS IS." MICROCHIP DISCLAIMS ALL
* WARRANTIES, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, NOT LIMITED
* TO MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
* INFRINGEMENT. Microchip shall in no event be liable for special,
* incidental, or consequential damages.
*
*
* Author Date Comment
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Nilesh Rajbharti 5/8/01 Original (Rev 1.0)
* Nilesh Rajbharti 5/22/02 Rev 2.0 (See version.log for detail)
* Nilesh Rajbharti 11/1/02 Fixed TCPTick() SYN Retry bug.
* Nilesh Rajbharti 12/5/02 Modified TCPProcess()
* to include localIP as third param.
* This was done to allow this function
* to calculate checksum correctly.
* Roy Schofield 10/1/04 TCPConnect() startTick bug fix.
* Howard Schlunder 1/3/05 Fixed HandleTCPSeg() unexpected
* discard problem identified by Richard
* Shelquist.
* Howard Schlunder 1/16/06 Fixed an imporbable RX checksum bug
* when using a Microchip Ethernet controller)
* Howard Schlunder 5/10/06 Revised TCP state machine, add TCP_FIN_2
* Howard Schlunder 8/01/06 Adjusted response to ACK only in TCP_SYN_SENT state
* Howard Schlunder 8/03/06 Fixed checksum comparison check
* reported by DouglasPunch on Microchip Forum.
* Howard Schlunder 8/11/06 Fixed a resource leak causing MAC TX
* Buffers to be obtained but not
* released when many web requests were
* received concurrently.
********************************************************************/
#define THIS_IS_TCP
#include <string.h>
#include "..\Include\StackTsk.h"
#include "..\Include\Helpers.h"
#include "..\Include\IP.h"
#include "..\Include\MAC.h"
#include "..\Include\Tick.h"
#include "..\Include\TCP.h"
#if defined(STACK_USE_TCP)
// Max TCP data length is MAC_TX_BUFFER_SIZE - sizeof(TCP_HEADER) -
// sizeof(IP_HEADER) - sizeof(ETHER_HEADER)
#define MAX_TCP_DATA_LEN (MAC_TX_BUFFER_SIZE - 54)
// TCP Timeout value to begin with.
#define TCP_START_TIMEOUT_VAL ((TICK)TICK_SECOND * (TICK)3)
// TCP Flags defined in RFC
#define FIN (0x01)
#define SYN (0x02)
#define RST (0x04)
#define PSH (0x08)
#define ACK (0x10)
#define URG (0x20)
// TCP Header
typedef struct _TCP_HEADER
{
WORD SourcePort;
WORD DestPort;
DWORD SeqNumber;
DWORD AckNumber;
struct
{
unsigned char Reserved3 : 4;
unsigned char Val : 4;
} DataOffset;
union
{
struct
{
unsigned char flagFIN : 1;
unsigned char flagSYN : 1;
unsigned char flagRST : 1;
unsigned char flagPSH : 1;
unsigned char flagACK : 1;
unsigned char flagURG : 1;
unsigned char Reserved2 : 2;
} bits;
BYTE byte;
} Flags;
WORD Window;
WORD Checksum;
WORD UrgentPointer;
} TCP_HEADER;
// TCP Options as defined by RFC
#define TCP_OPTIONS_END_OF_LIST (0x00)
#define TCP_OPTIONS_NO_OP (0x01)
#define TCP_OPTIONS_MAX_SEG_SIZE (0x02)
typedef struct _TCP_OPTIONS
{
BYTE Kind;
BYTE Length;
WORD_VAL MaxSegSize;
} TCP_OPTIONS;
#define SwapPseudoTCPHeader(h) (h.TCPLength = swaps(h.TCPLength))
// IP pseudo header as defined by RFC 793
typedef struct _PSEUDO_HEADER
{
IP_ADDR SourceAddress;
IP_ADDR DestAddress;
BYTE Zero;
BYTE Protocol;
WORD TCPLength;
} PSEUDO_HEADER;
#define LOCAL_PORT_START_NUMBER (1024)
#define LOCAL_PORT_END_NUMBER (5000)
// Local temp port numbers.
#ifdef STACK_CLIENT_MODE
static WORD _NextPort = LOCAL_PORT_START_NUMBER;
#endif
// The TCB array is very large. With the C18 compiler, one must
// modify the linker script to make an array that spans more than
// one memory bank. To do this, make the necessary changes to your
// processor's linker script (.lkr). Here is an example showing
// gpr11 and 128 bytes of gpr12 being combined into one 384 byte
// block used exclusively by the TCB_MEM data section:
// ...
// //DATABANK NAME=gpr11 START=0xB00 END=0xBFF
// //DATABANK NAME=gpr12 START=0xC00 END=0xCFF
// DATABANK NAME=gpr11b START=0xB00 END=0xC7F PROTECTED
// DATABANK NAME=gpr12 START=0xC80 END=0xCFF
// ...
// SECTION NAME=TCB_MEM RAM=gpr11b
// ...
#pragma udata TCB_MEM
SOCKET_INFO TCB[MAX_SOCKETS];
#pragma udata bla // Return to any other RAM section
static void HandleTCPSeg(TCP_SOCKET s,
NODE_INFO *remote,
TCP_HEADER *h,
WORD len);
static void TransmitTCP(NODE_INFO *remote,
TCP_PORT localPort,
TCP_PORT remotePort,
DWORD seq,
DWORD ack,
BYTE flags,
BUFFER buffer,
WORD len);
static TCP_SOCKET FindMatchingSocket(TCP_HEADER *h,
NODE_INFO *remote);
static void SwapTCPHeader(TCP_HEADER* header);
static void CloseSocket(SOCKET_INFO* ps);
#define SendTCP(remote, localPort, remotePort, seq, ack, flags) \
TransmitTCP(remote, localPort, remotePort, seq, ack, flags, \
INVALID_BUFFER, 0)
/*********************************************************************
* Function: void TCPInit(void)
*
* PreCondition: None
*
* Input: None
*
* Output: TCP is initialized.
*
* Side Effects: None
*
* Overview: Initialize all socket states
*
* Note: This function is called only once during lifetime
* of the application.
********************************************************************/
void TCPInit(void)
{
TCP_SOCKET s;
SOCKET_INFO* ps;
// Initialize all sockets.
for(s = 0; s < MAX_SOCKETS; s++)
{
ps = &TCB[s];
ps->smState = TCP_CLOSED;
ps->Flags.bServer = FALSE;
ps->Flags.bIsPutReady = TRUE;
ps->Flags.bFirstRead = TRUE;
ps->Flags.bIsTxInProgress = FALSE;
ps->Flags.bIsGetReady = FALSE;
if(ps->TxBuffer != INVALID_BUFFER)
{
MACDiscardTx(ps->TxBuffer);
ps->TxBuffer = INVALID_BUFFER;
}
ps->TimeOut = TCP_START_TIMEOUT_VAL;
ps->TxCount = 0;
}
}
/*********************************************************************
* Function: TCP_SOCKET TCPListen(TCP_PORT port)
*
* PreCondition: TCPInit() is already called.
*
* Input: port - A TCP port to be opened.
*
* Output: Given port is opened and returned on success
* INVALID_SOCKET if no more sockets left.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
TCP_SOCKET TCPListen(TCP_PORT port)
{
TCP_SOCKET s;
SOCKET_INFO* ps;
for(s = 0; s < MAX_SOCKETS; s++)
{
ps = &TCB[s];
if(ps->smState == TCP_CLOSED)
{
// We have a CLOSED socket.
// Initialize it with LISTENing state info.
ps->smState = TCP_LISTEN;
ps->localPort = port;
ps->remotePort = 0;
// There is no remote node IP address info yet.
ps->remote.IPAddr.Val = 0x00;
// If a socket is listened on, it is a SERVER.
ps->Flags.bServer = TRUE;
ps->Flags.bIsGetReady = FALSE;
if(ps->TxBuffer != INVALID_BUFFER)
{
MACDiscardTx(ps->TxBuffer);
ps->TxBuffer = INVALID_BUFFER;
}
ps->Flags.bIsPutReady = TRUE;
return s;
}
}
return INVALID_SOCKET;
}
/*********************************************************************
* Function: TCP_SOCKET TCPConnect(NODE_INFO* remote,
* TCP_PORT remotePort)
*
* PreCondition: TCPInit() is already called.
*
* Input: remote - Remote node address info
* remotePort - remote port to be connected.
*
* Output: A new socket is created, connection request is
* sent and socket handle is returned.
*
* Side Effects: None
*
* Overview: None
*
* Note: By default this function is not included in
* source. You must define STACK_CLIENT_MODE to
* be able to use this function.
********************************************************************/
#ifdef STACK_CLIENT_MODE
TCP_SOCKET TCPConnect(NODE_INFO *remote, TCP_PORT remotePort)
{
TCP_SOCKET s;
SOCKET_INFO* ps;
BOOL lbFound;
lbFound = FALSE;
// Find an available socket
for(s = 0; s < MAX_SOCKETS; s++)
{
ps = &TCB[s];
if(ps->smState == TCP_CLOSED)
{
lbFound = TRUE;
break;
}
}
// If there is no socket available, return error.
if(!lbFound)
return INVALID_SOCKET;
// Each new socket that is opened by this node, gets
// next sequential port number.
ps->localPort = ++_NextPort;
if(_NextPort >= LOCAL_PORT_END_NUMBER)
_NextPort = LOCAL_PORT_START_NUMBER-1;
// This is a client socket.
ps->Flags.bServer = FALSE;
// This is the port, we are trying to connect to.
ps->remotePort = remotePort;
// Each new socket that is opened by this node, will
// start with next the next seqeuence number (essentially random)
ps->SND_SEQ++;
ps->SND_ACK = 0;
memcpy((BYTE*)&ps->remote, (const void*)remote, sizeof(ps->remote));
// Send SYN message.
SendTCP(&ps->remote,
ps->localPort,
ps->remotePort,
ps->SND_SEQ,
ps->SND_ACK,
SYN);
ps->smState = TCP_SYN_SENT;
ps->SND_SEQ++;
// Allow TCPTick() to operate properly
ps->startTick = TickGet();
return s;
}
#endif
/*********************************************************************
* Function: BOOL TCPIsConnected(TCP_SOCKET s)
*
* PreCondition: TCPInit() is already called.
*
* Input: s - Socket to be checked for connection.
*
* Output: TRUE if given socket is connected
* FALSE if given socket is not connected.
*
* Side Effects: None
*
* Overview: None
*
* Note: A socket is said to be connected if it is not
* in LISTEN and CLOSED mode. Socket may be in
* SYN_RCVD or FIN_WAIT_1 and may contain socket
* data.
********************************************************************/
BOOL TCPIsConnected(TCP_SOCKET s)
{
return (TCB[s].smState == TCP_ESTABLISHED);
}
/*********************************************************************
* Function: void TCPDisconnect(TCP_SOCKET s)
*
* PreCondition: TCPInit() is already called AND
* TCPIsPutReady(s) == TRUE
*
* Input: s - Socket to be disconnected.
*
* Output: A disconnect request is sent for given socket.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
void TCPDisconnect(TCP_SOCKET s)
{
SOCKET_INFO *ps;
ps = &TCB[s];
// If socket is not connected, may be it is already closed
// or in the process of closing. Since we have called this
// explicitly, close it forcefully.
if(ps->smState != TCP_ESTABLISHED && ps->smState != TCP_SYN_RECEIVED)
{
CloseSocket(ps);
return;
}
// Discard any outstanding data that is to be read.
TCPDiscard(s);
// Send FIN message.
SendTCP(&ps->remote,
ps->localPort,
ps->remotePort,
ps->SND_SEQ,
ps->SND_ACK,
FIN | ACK);
DebugPrint(".");
ps->SND_SEQ++;
ps->smState = TCP_FIN_WAIT_1;
return;
}
/*********************************************************************
* Function: BOOL TCPFlush(TCP_SOCKET s)
*
* PreCondition: TCPInit() is already called.
*
* Input: s - Socket whose data is to be transmitted.
*
* Output: All and any data associated with this socket
* is marked as ready for transmission.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
BOOL TCPFlush(TCP_SOCKET s)
{
SOCKET_INFO *ps;
ps = &TCB[s];
// Make sure that there is TxBuffer assigned to this socket.
if ( ps->TxBuffer == INVALID_BUFFER )
return FALSE;
if ( ps->Flags.bIsPutReady == FALSE )
return FALSE;
TransmitTCP(&ps->remote,
ps->localPort,
ps->remotePort,
ps->SND_SEQ,
ps->SND_ACK,
ACK + PSH, // Use PSH to make sure the end application receives the data right away
ps->TxBuffer,
ps->TxCount);
ps->SND_SEQ += (DWORD)ps->TxCount;
ps->Flags.bIsPutReady = FALSE;
ps->Flags.bIsTxInProgress = FALSE;
#ifdef TCP_NO_WAIT_FOR_ACK
if(ps->TxBuffer != INVALID_BUFFER)
{
MACDiscardTx(ps->TxBuffer);
ps->TxBuffer = INVALID_BUFFER;
}
ps->Flags.bIsPutReady = TRUE;
#endif
return TRUE;
}
/*********************************************************************
* Function: BOOL TCPIsPutReady(TCP_SOCKET s)
*
* PreCondition: TCPInit() is already called.
*
* Input: s - socket to test
*
* Output: TRUE if socket 's' is free to transmit
* FALSE if socket 's' is not free to transmit.
*
* Side Effects: None
*
* Overview: None
*
* Note: Each socket maintains only transmit buffer.
* Hence until a data packet is acknowledeged by
* remote node, socket will not be ready for
* next transmission.
* All control transmission such as Connect,
* Disconnect do not consume/reserve any transmit
* buffer.
********************************************************************/
BOOL TCPIsPutReady(TCP_SOCKET s)
{
if(TCB[s].RemoteWindow == 0)
return FALSE;
if ( TCB[s].TxBuffer == INVALID_BUFFER )
return IPIsTxReady(FALSE);
else
return TCB[s].Flags.bIsPutReady;
}
/*********************************************************************
* Function: BOOL TCPPut(TCP_SOCKET s, BYTE byte)
*
* PreCondition: TCPIsPutReady() == TRUE
*
* Input: s - socket to use
* byte - a data byte to send
*
* Output: TRUE if given byte was put in transmit buffer
* FALSE if transmit buffer is full.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
BOOL TCPPut(TCP_SOCKET s, BYTE byte)
{
SOCKET_INFO* ps;
ps = &TCB[s];
// Make sure that the remote node is able to accept our data
if(ps->RemoteWindow == 0)
return FALSE;
if(ps->TxBuffer == INVALID_BUFFER)
{
ps->TxBuffer = MACGetTxBuffer(FALSE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -