fnTCP_close()

USOCKET fnTCP_close(USOCKET TCP_socket);

TCP_socket is the TCP socket number of the TCP connection that should be closed.


fnTCP_close() can be called by both a client or a server. It causes the open connection to be closed - the close is called an "active close" since it is actively initiated by this side of the TCP connection (the other end of the TCP connection will experience a "passive close" due to this.

See the TCP state transition diagram as reference: µTasker TCP State Transition Diagram

The routine returns the number of the socket if the close could be successfully initiated. In case of errors it returns: An active close involves sending a TCP FIN. The close is completed once the TCP partner has acknowledged the close request and also sent a FIN of its own. The result of the close is the TCP event TCP_EVENT_CLOSED being sent to the TCP socket's call back routine. This routine then knows that the close has terminated successfully - note that the socket may then be set back to listening state if required.

A close may not take place immediately since it is also dependent on the TCP partner. The partner may acknowledge the FIN (putting the TCP state-event machine into the state FIN WAIT-2) but not yet complete the close because it has some extra data to send first. If the TCP partner is no longer contactable the close will first be repeated a few times before a RST is sent to terminate the bad connection. In the case of such an abort the TCP event TCP_EVENT_ABORT will be received by the TCP socket's call back handler instead of the TCP_EVENT_CLOSED.

fnTCP_close() can be commanded at any time by any code in the system. If the close is commanded from within the TCP socket's call back routine the fact that the close has been started should be reported to the TCP layer by returning APP_REQUEST_CLOSE. This is shown in the example below and is a special case for the call back which does apply outside the call back itself.

The µTasker implementation doesn't follow the TCP specification in every detail during this active close. The event TCP_EVENT_CLOSED is sent to the application although the socket state is TIME WAIT - the socket has not fully closed, even though the TCP partner has completely acknowledged the close sequence. The TIME WAIT generally lasts for a period of 2 minutes (specified as 2MSL - twice the Maximum Seqment Lifetime). The idea behind this is that the socket should not be reused during this period of time in case there is undelivered data from the old connection still flying around in cyber-space which could be subsequently received after the socket has been reused for another connection. This restriction is however difficult in embedded systems due to the fact that sockets are generally limited in number and blocking sockets for a long time may result in more serious problems if they run out.

The application can immediate reuse the socket if required (as a reaction to the TCP_EVENT_CLOSED event), for example to put it back to listening state. In any case, although the 2MSL period is otherwise respected the project define REUSE_TIME_WAIT_SOCKETS also enables a socket in the TCP_STATE_TIME_WAIT or TCP_STATE_FIN_WAIT_2 states to be re-used in an emergency. This option is only actually used when there are no free sockets at the time and also only when the new SYN request doesn't originate from the same IP address and the same port as the original connection - this effectively also avoids the effect that the specification attempts to avoid with the long silence period.

Finally note that the state TCP_STATE_FIN_WAIT_2 can block if realised according to the TCP specification. This state waits for a FIN from the TCP partner and has no specified maximum time to wait. If the TCP partner is removed from the network during the close there will never be a FIN received and the socket would theoretcially remained blocked forever. This is avoided by adding a realistic timeout value of MSL to this state and also allowing reuse of a socket from this state in an emergency, as described above.



Example

The following example shows a HTTP 1.0 connection being closed once all served data has been acknowledged.

An important point to note is that the close takes places from within the TCP soscket's call back routine. When a close is requested in this case the return value should be APP_REQUEST_CLOSE. This informs the TCP layer that the recption causes a close to be started so that it can correctly finish its handling of the received TCP frame.

static int fnHTTPListener(USOCKET Socket, unsigned char ucEvent, unsigned char *ucIp_Data, unsigned short usPortLen)
{
    switch (ucEvent) {
    case TCP_EVENT_ACK:
        if (more data to be sent) {
            // send next block of data
            return APP_SENT_DATA;
        }
        else {
            fnTCP_close(http_session->OwnerTCPSocket);
            return APP_REQUEST_CLOSE;
        }
        break;
    }
    return APP_ACCEPT;                
}


See the following forum thread for additional details about working with TCP sockets: µTasker forum TCP discussion.

Related functions

fnGetTCP_Socket();
fnReleaseTCP_Socket();
fnTCP_Listen();
fnGetTCP_state();
fnTCP_Activity();
fnTCP_Connect();




Please use the µTasker forum to ask specific questions.