📄 tcp.c
字号:
#include <debug.h>
#include <datatypes.h>
#include <timers.h>
#include <ethernet.h>
#if defined(IPv6)
#include <ipv6.h>
extern struct _in6_addr LocalIP;
#else
#include <ip.h>
#endif
#include <tcp_ip.h>
#include <system.h>
/** \brief Used for storing field information about the received TCP packet
*
* Various fields from the TCP packet are stored in this variable. These
* values are then used to perform the necessary actions as defined
* by the TCP specification: correctnes of the received TCP packet is
* checked by analyzing these fields, appropriate socket data is adjusted
* and/or control packet is sent based on it. See tcp_frame definition
* for struct information.
*/
struct tcp_frame received_tcp_packet;
/** \brief TCP table holding connection parameters for every TCP socket
*
* TCP table is an array of tcp_socket structures holding all of the
* necessary information about the state, timers, timeouts and sequence
* and port numbers of the TCP sockets opened. Number of TCP sockets
* that can be opened at any given time is defined by the #NO_OF_TCPSOCKETS
* and may be changed in order to change the amount of used RAM memory.
* See tcb definition for more information about the structure itself.
*
* \note As seen in the code, an array size is actually bigger for one
* than the #NO_OF_TCPSOCKETS defines. The last entry is used for sending
* control packets as answers to incoming TCP packets that do not map
* to any existing TCP sockets.
*/
struct tcb tcp_socket[NO_OF_TCPSOCKETS + 1];
UINT8 tcp_tempbuf[MIN_TCP_HLEN + 1]; /**< Temporary buffer used for sending TCP control packets */
/***********************************************************************/
/******* TCP API functions ********/
/***********************************************************************/
/** \brief Allocate a free socket in TCP socket pool
* \ingroup tcp_app_api
* \author
* \li Jari Lahti (jari.lahti@violasystems.com)
* \date 21.07.2002
* \param soctype type of socket wanted. Can take one of the following
* values:
* \li #TCP_TYPE_NONE
* \li #TCP_TYPE_SERVER
* \li #TCP_TYPE_CLIENT
* \li #TCP_TYPE_CLIENT_SERVER
* \param tos type of service for socket. For now only #TCP_TOS_NORMAL.
* \param tout Timeout of socket in seconds. Defines after how many seconds
* of inactivity (application not sending and/or receiving any data
* over TCP connection) will the TCP socket automatically be closed.
* \param listener pointer to callback function that will be invoked by
* the TCP/IP stack to inform socket application of certain events. See
* tcpc_demo_eventlistener() and tcps_demo_eventlistener() for more
* information on events and possible actions.
* \return
* \li -1 - Error getting requested socket
* \li >=0 - Handle to reserved socket
*
* Invoke this function to try to obtain a free socket from TCP socket pool.
* Function returns a handle to the free socket that is later used for
* accessing the allocated socket (opening, connecting, sending data,
* closing, aborting, etc.).
*/
#ifdef IPv6
INT8 tcp_getsocket (UINT8 soctype, UINT8 tos, UINT16 tout, INT32 (*listener)(INT8, UINT8, IPv6Addr*, UINT32) )
#else
INT8 tcp_getsocket (UINT8 soctype, UINT8 tos, UINT16 tout, INT32 (*listener)(INT8, UINT8, UINT32, UINT32) )
#endif
{
INT8 i;
struct tcb* soc;
if( NO_OF_TCPSOCKETS < 0 )
return(-1);
if( NO_OF_TCPSOCKETS == 0 )
return(-1);
if( (soctype != TCP_TYPE_SERVER) &&
(soctype != TCP_TYPE_CLIENT) &&
(soctype != TCP_TYPE_CLIENT_SERVER) &&
(soctype != TCP_TYPE_NONE) ) {
TCP_DEBUGOUT("Invalid socket type requested\r\n");
return(-1);
}
if(listener == 0) {
TCP_DEBUGOUT("ERROR:Event listener function not specified\r\n");
return(-1);
}
TCP_DEBUGOUT("Searching for free TCP socket...\r\n");
for(i=0; i < NO_OF_TCPSOCKETS; i++) {
soc = &tcp_socket[i]; /* Get Socket */
if(soc->state == TCP_STATE_FREE) {
/* We found it */
TCP_DEBUGOUT("Free socket found\r\n");
soc->state = TCP_STATE_RESERVED;
soc->type = soctype;
soc->tos = tos;
soc->event_listener = listener;
#ifdef IPv6
// memset (&soc->rem_ip->u.byte[0],0,sizeof(IPv6Addr));
soc->rem_ip = (void *)0x0000;
#else
soc->rem_ip = 0;
#endif
soc->remport = 0;
soc->locport = 0;
soc->flags = 0;
soc->tout = tout*TIMERTIC;
return(i);
}
}
/* We are there so no socket found */
TCP_DEBUGOUT("No socket found\r\n");
return(-1);
}
/** \brief Release a TCP socket
* \ingroup tcp_app_api
* \author
* \li Jari Lahti (jari.lahti@violasystems.com)
* \date 21.07.2002
* \param sochandle handle to socket to be released
* \return
* \li -1 - Error releasing the socket (Wrong socket handle or socket
* not in proper state to be released)
* \li >=0 - handle of the released socket (can not be used any more
* untill allocated again with tcp_getsocket()).
*
* Once the application does not need the TCP socket any more it can invoke
* this function in order to release it. This is usefull if there is a very
* limited number of sockets (in order to save some memory) shared among
* several applications.
*/
INT8 tcp_releasesocket (INT8 sochandle)
{
struct tcb* soc;
if( NO_OF_TCPSOCKETS < 0 )
return(-1);
if( NO_OF_TCPSOCKETS == 0 )
return(-1);
if( sochandle > NO_OF_TCPSOCKETS ) {
TCP_DEBUGOUT("Socket handle non-valid\r\n");
return(-1);
}
if( sochandle < 0 ) {
TCP_DEBUGOUT("Socket handle non-valid\r\n");
return(-1);
}
soc = &tcp_socket[sochandle]; /* Get referense */
if( (soc->state != TCP_STATE_FREE) &&
(soc->state != TCP_STATE_RESERVED) &&
(soc->state != TCP_STATE_CLOSED) ) {
TCP_DEBUGOUT("Socket is not on valid state to be released\r\n");
return(-1);
}
/* We are there so all OK */
soc->state = TCP_STATE_FREE;
soc->type = TCP_TYPE_NONE;
soc->tos = 0;
soc->event_listener = 0;
soc->rem_ip = 0;
soc->remport = 0;
soc->locport = 0;
soc->flags = 0;
return(sochandle);
}
/** \brief Put TCP socket to listen on a given port
* \ingroup tcp_app_api
* \author
* \li Jari Lahti (jari.lahti@violasystems.com)
* \date 21.07.2002
* \param sochandle handle to socket to be placed to listen state
* \param port TCP port number on which it should listen
* \return
* \li -1 - Error
* \li >=0 - OK (Socket put to listening state. Handle to
* socket returned)
*
* This function will attempt to put socket to listening state. This
* is only possible if socket was defined as either #TCP_TYPE_SERVER or
* #TCP_TYPE_CLIENT_SERVER. If basic correctness checks pass, socket is
* put to listening mode and corresponding tcb entry is initialized.
*
*/
INT8 tcp_listen (INT8 sochandle, UINT16 port)
{
struct tcb* soc;
if( NO_OF_TCPSOCKETS < 0 )
return(-1);
if( NO_OF_TCPSOCKETS == 0 )
return(-1);
if( sochandle > NO_OF_TCPSOCKETS ) {
TCP_DEBUGOUT("Socket handle non-valid\r\n");
return(-1);
}
if( sochandle < 0 ) {
TCP_DEBUGOUT("Socket handle non-valid\r\n");
return(-1);
}
soc = &tcp_socket[sochandle]; /* Get referense */
if( (soc->type & TCP_TYPE_SERVER) == 0 ) {
TCP_DEBUGOUT("Socket has no server properties\r\n");
return(-1);
}
if( soc->event_listener == 0) {
TCP_DEBUGOUT("ERROR:No event listener function specified\r\n");
return(-1);
}
if( (soc->state != TCP_STATE_RESERVED) &&
(soc->state != TCP_STATE_LISTENING) &&
(soc->state != TCP_STATE_CLOSED) &&
(soc->state != TCP_STATE_TIMED_WAIT) ) {
TCP_DEBUGOUT("Not possible to listen, socket on connected state\r\n");
return(-1);
}
/* Init socket */
soc->state = TCP_STATE_LISTENING;
/*soc->type = TCP_TYPE_SERVER;*/
soc->flags = 0;
soc->rem_ip = 0;
soc->remport = 0;
soc->locport = port;
soc->send_unacked = 0;
soc->myflags = 0;
soc->send_next = 0xFFFFFFFF;
soc->send_mtu = TCP_DEF_MTU;
soc->receive_next = 0;
soc->retries_left = 0;
TCP_DEBUGOUT("TCP listening socket created\r\n");
return(sochandle);
}
/** \brief Initialize connection establishment towards remote IP&port
* \ingroup tcp_app_api
* \author
* \li Jari Lahti (jari.lahti@violasystems.com)
* \date 21.07.2002
* \param sochandle handle to socket to be used for connection establishment
* \param ip remote IP address to connect to
* \param rport remote port number to connect to
* \param myport local port to use for connection. This value can be
* specified directly or, if a value of 0 is given, TCP module will
* determine local TCP port automatically.
* \return
* \li -1 - Error
* \li >=0 - OK (Connection establishment procedure started. Socket handle
* returned.)
*
* Invoke this function to start connection establishment procedure towards
* remote host over some socket. This is only possible if socket was
* defined as either #TCP_TYPE_CLIENT or #TCP_TYPE_CLIENT_SERVER. Function
* will make some basic checks and if everything is OK, corresponding tcb
* socket entry will be initialized and connection procedure started.
*/
#ifdef IPv6
INT8 tcp_connect (INT8 sochandle, struct _in6_addr* ip, UINT16 rport, UINT16 myport )
#else
INT8 tcp_connect (INT8 sochandle, UINT32 ip, UINT16 rport, UINT16 myport )
#endif
{
struct tcb* soc;
TCP_DEBUGOUT("FUNCTION: tcp_connect\r\n");
if( NO_OF_TCPSOCKETS < 0 )
return(-1);
if( NO_OF_TCPSOCKETS == 0 )
return(-1);
if( sochandle > NO_OF_TCPSOCKETS ) {
TCP_DEBUGOUT("Socket handle non-valid\r\n");
return(-1);
}
if( sochandle < 0 ) {
TCP_DEBUGOUT("Socket handle non-valid\r\n");
return(-1);
}
/* Is the local port defined */
if( myport == 0 )
myport = tcp_getfreeport();
if( myport == 0 )
return(-1);
soc = &tcp_socket[sochandle]; /* Get referense */
/* Do we have client properties? */
if( (soc->type & TCP_TYPE_CLIENT) == 0 ) {
TCP_DEBUGOUT("Socket has no client properties\r\n");
return(-1);
}
if( soc->event_listener == 0) {
TCP_DEBUGOUT("ERROR:No event listener function specified\r\n");
return(-1);
}
/* Are we on LISTENING, RESERVED or CLOSED state */
if( (soc->state != TCP_STATE_RESERVED) &&
(soc->state != TCP_STATE_LISTENING) &&
(soc->state != TCP_STATE_CLOSED) ) {
TCP_DEBUGOUT("Socket on unvalid state to initialize CONNECT\r\n");
return(-1);
}
/* Then just set parameters and send SYN */
soc->rem_ip = ip;
soc->remport = rport;
soc->locport = myport;
soc->flags = 0;
soc->send_mtu = TCP_DEF_MTU;
/* get initial sequence number */
soc->send_unacked = tcp_initseq();
soc->send_next = soc->send_unacked + 1;
soc->myflags = TCP_FLAG_SYN;
tcp_sendcontrol(sochandle);
tcp_newstate(soc, TCP_STATE_SYN_SENT);
return(sochandle);
}
/** \brief Send user data over TCP using given TCP socket
* \ingroup tcp_app_api
* \author
* \li Jari Lahti (jari.lahti@violasystems.com)
* \date 25.07.2002
* \param sockethandle handle to TCP socket to be used for sending data
* \param buf pointer to data buffer (start of user data)
* \param blen buffer length in bytes (without space reserved at the
* beginning of buffer for headers)
* \param dlen length of user data to be sent (in bytes)
* \return
* \li -1 - Error
* \li >0 - OK (number represents number of bytes actually sent)
*
* \warning
* \li <i>buf</i> parameter is a pointer to data to be sent in
* user buffer. But note that there <b>MUST</b> be sufficient
* free buffer space before that data for TCP header (of #MIN_TCP_HLEN
* size).
*
* Invoke this function to initiate data sending over TCP connection
* established over a TCP socket. Since data is not buffered (in order
* to reduce RAM memory consumption) new data can not be sent until
* data that was previously sent is acknowledged. So, application knows when
* it can send new data either by:
* \li waiting for TCP_EVENT_ACK in event_listener function
* \li invoking tcp_check_send() function to check if it is possible
* to send data
*
*/
INT16 tcp_send (INT8 sockethandle, UINT8* buf, UINT16 blen, UINT16 dlen)
{
struct tcb* soc;
TCP_DEBUGOUT("Entering to send TCP data packet\r\n");
kick_WD();
if( sockethandle < 0 ) {
TCP_DEBUGOUT("ERROR:Socket Handle not valid (<0)\r\n");
return(-1);
}
if( sockethandle > NO_OF_TCPSOCKETS ) {
TCP_DEBUGOUT("ERROR:Socket Handle not valid (>NO_OF_TCPSOCKETS)\r\n");
return(-1);
}
soc = &tcp_socket[sockethandle]; /* Get socket */
if(soc->state != TCP_STATE_CONNECTED) {
TCP_DEBUGOUT("TCP is not connected!!\r\n");
return(-1);
}
if(soc->send_unacked != soc->send_next) {
TCP_DEBUGOUT("TCP contains unacked data, cannot send more\r\n");
return(-1);
}
if( dlen > blen )
dlen = blen;
if(dlen + MIN_TCP_HLEN > soc->send_mtu) {
if(soc->send_mtu > MIN_TCP_HLEN)
dlen = soc->send_mtu - MIN_TCP_HLEN;
else
return(-1);
}
soc->send_next += dlen;
soc->myflags = TCP_FLAG_ACK | TCP_FLAG_PUSH;
process_tcp_out(sockethandle, buf - MIN_TCP_HLEN, blen + MIN_TCP_HLEN + 1, dlen);
return(dlen);
}
/** \brief Initiate TCP connection closing procedure
* \ingroup tcp_app_api
* \author
* \li Jari Lahti (jari.lahti@violasystems.com)
* \date 21.07.2002
* \param sochandle handle to socket on which TCP connection is to be closed
* \return
* \li -2 - there is unacked data on this socket. Try again later.
* \li -1 - Error
* \li >=0 - OK (connection closing procedure started. Handle to socket
* returned)
*
* Invoke this function to start connetion closing procedure over a given
* socket. Note that connection is not immediately closed. It may take some
* time for that to happen. Event_listener function will be invoked with
* appropriate event when that really happens.
*/
INT8 tcp_close (INT8 sochandle)
{
struct tcb* soc;
TCP_DEBUGOUT("FUNCTION: tcp_close\r\n");
if( NO_OF_TCPSOCKETS < 0 )
return(-1);
if( NO_OF_TCPSOCKETS == 0 )
return(-1);
if( sochandle > NO_OF_TCPSOCKETS ) {
TCP_DEBUGOUT("Socket handle non-valid\r\n");
return(-1);
}
if( sochandle < 0 ) {
TCP_DEBUGOUT("Socket handle non-valid\r\n");
return(-1);
}
soc = &tcp_socket[sochandle]; /* Get referense */
switch(soc->state) {
case TCP_STATE_LISTENING:
tcp_newstate(soc, TCP_STATE_CLOSED);
break;
case TCP_STATE_SYN_RECEIVED:
soc->myflags = TCP_FLAG_ACK | TCP_FLAG_FIN;
soc->send_unacked++;
soc->send_next++;
tcp_sendcontrol(sochandle);
tcp_newstate(soc, TCP_STATE_FINW1);
break;
case TCP_STATE_SYN_SENT:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -