Network Component  Version 6.6
MDK-Professional Middleware for IP Networking
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
TCP Socket

This section describes the implementation of the Transmission Control Protocol (TCP) in the Network Component. TCP runs on top of the Internet Protocol (IP). TCP is a connection-oriented and reliable full duplex protocol supporting a pair of byte streams, one for each direction. A TCP connection must be established before exchanging data. TCP retransmits data that do not reach the final destination due to errors or data corruption. Data is delivered in the sequence of its transmission.

Opening a TCP Connection

Before a client can connect to a server, the server must first bind to and listen at a port to open it up for connections. This is called a passive open. Once the passive open is established, a client may initiate an active open. In fact, a passive open can specify that the server is waiting for an active open from a specific client.

In the Net_Config_TCP.h configuration file the usage of TCP sockets is automatically enabled. In addition, to open an active or passive TCP connection, you need to call the net_main() function. This function handles all protocol related data. It has to be run frequently to ensure proper operation.

void main (void) {
init ();
// Initialize the Network Core
while (1) {
// Run main Network Core 'thread'
..
}
}

The Network Component can handle multiple connections on the same port. Several applications (for example Web server, FTP server, Telnet server, etc.) are using this concept.

Sending TCP Data

The TCP protocol is a byte stream service. It does not know anything about the format of the data being sent. It simply takes the data, encapsulates it into a TCP packet, and sends it to the remote peer. The TCP socket then keeps sent packets in memory and waits for an acknowledge from the remote peer.

If the packet is not acknowledged when the timeout expires, the same packet is resent. This process is repeated until a packet is either acknowledged or the TCP socket aborts the connection.

Example for Sending Data

The following example shows how to send large amounts of data using TCP sockets. 64 Kbytes are sent to the remote IP address 192.168.0.100, which is listening on port 1000. The TCP socket is permanently allocated and is not released when the data is sent or when the connection is closed.

  1. Initialize the Network Component (using net_initialize) and allocate a free TCP socket (using tcp_get_socket):
    #include "rl_net.h"
    uint8_t tcp_soc;
    uint8_t soc_state;
    bool wait_ack;
    void main (void) {
    tcp_soc = tcp_get_socket (TCP_TYPE_CLIENT, 0, 120, tcp_callback);
    soc_state = 0;

  2. Run net_main of the Network Component and call the send_data() function from an endless loop:
    while (1) {
    send_data ();
    }
    }

  3. The send_data() function must be implemented as a state machine. It opens an active TCP connection, sends data, and closes the TCP connection in the end. When the soc_state is 0, the connection is initiated (using tcp_connect):
    void send_data (void) {
    static const uint8_t rem_IP[4] = {192,168,0,100};
    static int bcount;
    uint32_t max;
    uint8_t *sendbuf;
    switch (soc_state) {
    case 0:
    tcp_connect (tcp_soc, rem_IP, 1000, 0);
    bcount = 0;
    wait_ack = false;
    soc_state = 1;
    return;

  4. Next, state 1 is waiting for the tcpEventConnect event. This event is received in the tcp_callback() event callback function, which places the send_data() process into state 2 (sending data state).
  5. In state 2, allocate the maximum possible size of transmit buffer (using tcp_max_data_size), fill it with some data, and send it. The maximum possible transmit buffer is allocated to reduce the number of packets and improve the transfer speed.
    After the packet is sent, wait for the remote acknowledge before proceeding with the next data packet.
    case 2:
    if (wait_ack == true) {
    return;
    }
    max = tcp_max_dsize (tcp_soc);
    sendbuf = tcp_get_buf (max);
    for (i = 0; i < max; i += 2) {
    sendbuf[i] = bcount >> 8;
    sendbuf[i+1] = bcount & 0xFF;
    if (bcount >= 32768) {
    soc_state = 3;
    break;
    }
    }
    tcp_send (tcp_soc, sendbuf, i);
    wait_ack = true;
    return;

  6. State 3 is achieved when the data transfer is finished. Wait for the last packet to be acknowledged and then close the TCP connection (using tcp_close).
    case 3:
    if (wait_ack == true) {
    return;
    }
    tcp_close (tcp_soc);
    soc_state = 4;
    return;
    }
    }

  7. The embedded application waits for the TCP socket to connect before starting to send data. When the data packet is sent, the application waits for the acknowledge before creating and sending the next data packet. Use the callback listener function to wait for the remote acknowledge.
    uint32_t tcp_callback (int32_t socket, tcpEvent event, const uint8_t *buf, uint32_t len) {
    // This function is called on TCP event
    switch (event) {
    ..
    // Socket is now connected and ready to send data.
    soc_state = 2;
    break;
    // Our sent data has been acknowledged by remote peer
    wait_ack = false;
    break;
    ..
    }
    return (0);
    }
Note
  • This assumes that the Network Interface Adapter is selected, enabled, and properly configured.
  • If the system runs out of TCP sockets, the application hangs in an endless loop. The system error function will respond with the error code ERR_TCP_ALLOC.

Multiple TCP Connections

Server applications often require to be able to accept several TCP connections from clients on the same port. The handling of these multiple connections must be implemented in the user application.

Because TCP socket is a connection-oriented service, it accepts only one concurrent connection. The basic packet multiplexing is done in the TCP Transport layer and the user application receives the socket number as a parameter in the callback function.

The framework of the user application shall contain the following basic functions:

  1. The user_init() function to initialize all user application sessions at startup.
    void user_init () {
    USER_INFO *user_s;
    int i;
    for (i = 0; i < user_num_sess; i++) {
    user_s = &user_session[i];
    user_s->Count = 0;
    user_s->Flags = 0;
    user_s->BCnt = 0;
    user_s->Tout = 0;
    user_s->File = NULL;
    user_s->Script= NULL;
    // Allocate a TCP socket for the session.
    user_s->Socket = tcp_get_socket (TCP_TYPE_SERVER, TCP_TOS_NORMAL,
    120, user_listener);
    user_s->State = USER_STATE_ERROR;
    if (user_s->Socket > 0) {
    if (tcp_listen (user_s->Socket, USER_SERVER_PORT) == netOK) {
    user_s->State = USER_STATE_IDLE;
    }
    }
    }
    }
    All user sessions are now initialized and each session has allocated it's own TCP socket. A socket is listening on selected USER_SERVER_PORT port.
  1. The user_listener() callback function for TCP socket. This callback function is common for all TCP sockets allocated in this user application.
    uint32_t user_listener (int32_t socket, tcpEvent event, const uint8_t *buf, uint32_t len) {
    USER_INFO *user_s;
    uint8_t session;
    int i;
    session = user_map_session (socket);
    if (session == 0)) {
    return (false);
    }
    user_s = &user_session[session-1];
    switch (event) {
    if (user_s->State == USER_STATE_IDLE) {
    user_s->State = USER_STATE_RESERVED;
    }
    return (true);
    user_kill_session (user_s);
    return (true);
    user_s->State = USER_STATE_ACTIVE;
    return (true);
    user_kill_session (user_s);
    return (true);
    user_s->Count += user_s->BCnt;
    user_s->BCnt = 0;
    return (true);
    ..
    return (true);
    }
    return (false);
    }
  1. The user_map_session() function to map the socket, which has generated a callback event, to it's owner session.
    static uint8_t user_map_session (uint8_t socket) {
    int i;
    for (i = 1; i <= user_num_sess; i++) {
    if (user_session[i-1].Socket == socket) {
    return (i);
    }
    }
    return (0);
    }
  1. The user_kill_session() function to initialize the session to a default state, close any eventually opened files and release any eventually allocated buffers.
    static void user_kill_session (USER_INFO *user_s) {
    user_s->State = USER_STATE_IDLE;
    if (user_s->Flags & USER_FLAG_FOPENED) {
    user_fclose (user_s->File);
    user_s->File = NULL;
    }
    if (user_s->Script != NULL) {
    free_mem (user_s->Script);
    user_s->Script = NULL;
    }
    user_s->Flags = 0;
    user_s->Count = 0;
    user_s->BCnt = 0;
    user_s->Tout = 0;
    }
  1. The user_run_server() function to maintain the application jobs, timeouts, etc. This function shall be frequently called from the main loop.
    void user_run_server () {
    USER_INFO *user_s;
    int i;
    for (i = 0; i < user_num_sess; i++) {
    user_s = &user_session[i];
    switch (user_s->State) {
    case USER_STATE_IDLE:
    case USER_STATE_RESERVED:
    // Keep TCP sockets listening.
    if (tcp_get_state (user_s->Socket) < tcpStateLISTEN) {
    tcp_listen (user_s->Socket, USER_SERVER_PORT);
    }
    break;
    case USER_STATE_WAITING:
    if (sec_tick == true) {
    if (--user_s->Tout == 0) {
    // A timeout expired.
    user_kill_session (user_s);
    }
    }
    break;
    case USER_STATE_ACTIVE:
    ..
    break;
    }
    }
    }
Note
One TCP socket will be used for every user session. You need to reserve enough TCP sockets in the Net_Config_TCP.h configuration file for all user sessions.

TCP Socket Configuration

net_config_tcp_h.png
TCP Socket Configuration File

The TCP sockets configuration file Net_Config_TCP.h contains the following settings:

  • Number of TCP Sockets specifies the number of available TCP sockets. This number specifies the maximum number of simultaneously opened TCP connections.
  • Number of Retries specifies the number of retransmissions before the TCP module gives up. Data is retransmitted if it is not acknowledged within the timeout frame defined by the Retry Timeout in seconds.
  • Retry Timeout in seconds is the timeout after which the TCP module retransmits the data. This is the initial timeout value. When the data exchange is going on, the system measures the response time and corrects this timeout.
  • Default Connect Timeout in seconds is the default keep-alive timeout. After this timeout has expired, the TCP link is disconnected. This parameter is used for services such as HTTP Web Server and Telnet Server.
  • Maximum Segment Size specifies the maximum number of bytes in the TCP segment's data field. Acceptable values for this parameter are in the range from 536 to 1460 bytes.
  • Receive Window Size specifies the amount of data the TCP socket is able to buffer.