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

📄 tcp.c

📁 单片机c语言程序设计100例--基于PIC+PROTEUS
💻 C
📖 第 1 页 / 共 3 页
字号:
/*********************************************************************
*
*                  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 + -