Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers tcp_timer.c Source File

tcp_timer.c

Go to the documentation of this file.
00001 /**
00002  * @file tcp_timer.c
00003  * @brief TCP timer management
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneTCP Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00026  * @version 1.7.6
00027  **/
00028 
00029 //Switch to the appropriate trace level
00030 #define TRACE_LEVEL TCP_TRACE_LEVEL
00031 
00032 //Dependencies
00033 #include "core/net.h"
00034 #include "core/socket.h"
00035 #include "core/tcp.h"
00036 #include "core/tcp_misc.h"
00037 #include "core/tcp_timer.h"
00038 #include "ipv4/ipv4.h"
00039 #include "ipv6/ipv6.h"
00040 #include "date_time.h"
00041 #include "debug.h"
00042 
00043 //Check TCP/IP stack configuration
00044 #if (TCP_SUPPORT == ENABLED)
00045 
00046 
00047 /**
00048  * @brief TCP timer handler
00049  *
00050  * This routine must be periodically called by the TCP/IP stack to
00051  * handle retransmissions and TCP related timers (persist timer,
00052  * FIN-WAIT-2 timer and TIME-WAIT timer)
00053  *
00054  **/
00055 
00056 void tcpTick(void)
00057 {
00058    error_t error;
00059    uint_t i;
00060    uint_t n;
00061    uint_t u;
00062 
00063    //Loop through opened sockets
00064    for(i = 0; i < SOCKET_MAX_COUNT; i++)
00065    {
00066       //Shortcut to the current socket
00067       Socket *socket = socketTable + i;
00068       //Check socket type
00069       if(socket->type != SOCKET_TYPE_STREAM)
00070          continue;
00071       //Check the current state of the TCP state machine
00072       if(socket->state == TCP_STATE_CLOSED)
00073          continue;
00074 
00075       //Is there any packet in the retransmission queue?
00076       if(socket->retransmitQueue != NULL)
00077       {
00078          //Retransmission timeout?
00079          if(tcpTimerElapsed(&socket->retransmitTimer))
00080          {
00081 #if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
00082             //When a TCP sender detects segment loss using the retransmission
00083             //timer and the given segment has not yet been resent by way of
00084             //the retransmission timer, the value of ssthresh must be updated
00085             if(!socket->retransmitCount)
00086             {
00087                //Amount of data that has been sent but not yet acknowledged
00088                uint_t flightSize = socket->sndNxt - socket->sndUna;
00089                //Adjust ssthresh value
00090                socket->ssthresh = MAX(flightSize / 2, 2 * socket->smss);
00091             }
00092 
00093             //Furthermore, upon a timeout cwnd must be set to no more than
00094             //the loss window, LW, which equals 1 full-sized segment
00095             socket->cwnd = MIN(TCP_LOSS_WINDOW * socket->smss, socket->txBufferSize);
00096 
00097             //After a retransmit timeout, record the highest sequence number
00098             //transmitted in the variable recover
00099             socket->recover = socket->sndNxt - 1;
00100 
00101             //Enter the fast loss recovery procedure
00102             socket->congestState = TCP_CONGEST_STATE_LOSS_RECOVERY;
00103 #endif
00104             //Make sure the maximum number of retransmissions has not been reached
00105             if(socket->retransmitCount < TCP_MAX_RETRIES)
00106             {
00107                //Debug message
00108                TRACE_INFO("%s: TCP segment retransmission #%u (%u data bytes)...\r\n",
00109                   formatSystemTime(osGetSystemTime(), NULL), socket->retransmitCount + 1,
00110                   socket->retransmitQueue->length);
00111 
00112                //Retransmit the earliest segment that has not been
00113                //acknowledged by the TCP receiver
00114                tcpRetransmitSegment(socket);
00115 
00116                //Use exponential back-off algorithm to calculate the new RTO
00117                socket->rto = MIN(socket->rto * 2, TCP_MAX_RTO);
00118                //Restart retransmission timer
00119                tcpTimerStart(&socket->retransmitTimer, socket->rto);
00120                //Increment retransmission counter
00121                socket->retransmitCount++;
00122             }
00123             else
00124             {
00125                //The maximum number of retransmissions has been exceeded
00126                tcpChangeState(socket, TCP_STATE_CLOSED);
00127                //Turn off the retransmission timer
00128                tcpTimerStop(&socket->retransmitTimer);
00129             }
00130 
00131             //TCP must use Karn's algorithm for taking RTT samples. That is, RTT
00132             //samples must not be made using segments that were retransmitted
00133             socket->rttBusy = FALSE;
00134          }
00135       }
00136 
00137       //Check the current state of the TCP state machine
00138       if(socket->state == TCP_STATE_CLOSED)
00139          continue;
00140 
00141       //The persist timer is used when the remote host advertises
00142       //a window size of zero
00143       if(!socket->sndWnd && socket->wndProbeInterval)
00144       {
00145          //Time to send a new probe?
00146          if(tcpTimerElapsed(&socket->persistTimer))
00147          {
00148             //Make sure the maximum number of retransmissions has not been reached
00149             if(socket->wndProbeCount < TCP_MAX_RETRIES)
00150             {
00151                //Debug message
00152                TRACE_INFO("%s: TCP zero window probe #%u...\r\n",
00153                   formatSystemTime(osGetSystemTime(), NULL), socket->wndProbeCount + 1);
00154 
00155                //Zero window probes usually have the sequence number one less than expected
00156                tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt - 1, socket->rcvNxt, 0, FALSE);
00157                //The interval between successive probes should be increased exponentially
00158                socket->wndProbeInterval = MIN(socket->wndProbeInterval * 2, TCP_MAX_PROBE_INTERVAL);
00159                //Restart the persist timer
00160                tcpTimerStart(&socket->persistTimer, socket->wndProbeInterval);
00161                //Increment window probe counter
00162                socket->wndProbeCount++;
00163             }
00164             else
00165             {
00166                //Enter CLOSED state
00167                tcpChangeState(socket, TCP_STATE_CLOSED);
00168             }
00169          }
00170       }
00171 
00172       //To avoid a deadlock, it is necessary to have a timeout to force
00173       //transmission of data, overriding the SWS avoidance algorithm. In
00174       //practice, this timeout should seldom occur (see RFC 1122 4.2.3.4)
00175       if(socket->state == TCP_STATE_ESTABLISHED || socket->state == TCP_STATE_CLOSE_WAIT)
00176       {
00177          //The override timeout occurred?
00178          if(socket->sndUser && tcpTimerElapsed(&socket->overrideTimer))
00179          {
00180             //The amount of data that can be sent at any given time is
00181             //limited by the receiver window and the congestion window
00182             n = MIN(socket->sndWnd, socket->txBufferSize);
00183 
00184 #if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
00185             //Check the congestion window
00186             n = MIN(n, socket->cwnd);
00187 #endif
00188             //Retrieve the size of the usable window
00189             u = n - (socket->sndNxt - socket->sndUna);
00190 
00191             //Send as much data as possible
00192             while(socket->sndUser > 0)
00193             {
00194                //The usable window size may become zero or negative,
00195                //preventing packet transmission
00196                if((int_t) u <= 0)
00197                   break;
00198 
00199                //Calculate the number of bytes to send at a time
00200                n = MIN(u, socket->sndUser);
00201                n = MIN(n, socket->smss);
00202 
00203                //Send TCP segment
00204                error = tcpSendSegment(socket, TCP_FLAG_PSH | TCP_FLAG_ACK,
00205                   socket->sndNxt, socket->rcvNxt, n, TRUE);
00206                //Failed to send TCP segment?
00207                if(error)
00208                   break;
00209 
00210                //Advance SND.NXT pointer
00211                socket->sndNxt += n;
00212                //Adjust the number of bytes buffered but not yet sent
00213                socket->sndUser -= n;
00214                //Update the size of the usable window
00215                u -= n;
00216             }
00217 
00218             //Check whether the transmitter can accept more data
00219             tcpUpdateEvents(socket);
00220 
00221             //Restart override timer if necessary
00222             if(socket->sndUser > 0)
00223                tcpTimerStart(&socket->overrideTimer, TCP_OVERRIDE_TIMEOUT);
00224          }
00225       }
00226 
00227       //The FIN-WAIT-2 timer prevents the connection
00228       //from staying in the FIN-WAIT-2 state forever
00229       if(socket->state == TCP_STATE_FIN_WAIT_2)
00230       {
00231          //Maximum FIN-WAIT-2 time has elapsed?
00232          if(tcpTimerElapsed(&socket->finWait2Timer))
00233          {
00234             //Debug message
00235             TRACE_WARNING("TCP FIN-WAIT-2 timer elapsed...\r\n");
00236             //Enter CLOSED state
00237             tcpChangeState(socket, TCP_STATE_CLOSED);
00238          }
00239       }
00240 
00241       //TIME-WAIT timer
00242       if(socket->state == TCP_STATE_TIME_WAIT)
00243       {
00244          //2MSL time has elapsed?
00245          if(tcpTimerElapsed(&socket->timeWaitTimer))
00246          {
00247             //Debug message
00248             TRACE_WARNING("TCP 2MSL timer elapsed (socket %u)...\r\n", i);
00249             //Enter CLOSED state
00250             tcpChangeState(socket, TCP_STATE_CLOSED);
00251 
00252             //Dispose the socket if the user does not have the ownership anymore
00253             if(!socket->ownedFlag)
00254             {
00255                //Delete the TCB
00256                tcpDeleteControlBlock(socket);
00257                //Mark the socket as closed
00258                socket->type = SOCKET_TYPE_UNUSED;
00259             }
00260          }
00261       }
00262    }
00263 }
00264 
00265 
00266 /**
00267  * @brief Start TCP timer
00268  * @param[in] timer Pointer to the timer structure
00269  * @param[in] delay Time interval
00270  **/
00271 
00272 void tcpTimerStart(TcpTimer *timer, systime_t delay)
00273 {
00274    //Start timer
00275    timer->startTime = osGetSystemTime();
00276    timer->interval = delay;
00277 
00278    //The timer is now running...
00279    timer->running = TRUE;
00280 }
00281 
00282 
00283 /**
00284  * @brief Stop TCP timer
00285  * @param[in] timer Pointer to the timer structure
00286  **/
00287 
00288 void tcpTimerStop(TcpTimer *timer)
00289 {
00290    //Stop timer
00291    timer->running = FALSE;
00292 }
00293 
00294 
00295 /**
00296  * @brief Check whether a TCP timer is running
00297  * @param[in] timer Pointer to the timer structure
00298  * @return Timer state
00299  **/
00300 
00301 bool_t tcpTimerRunning(TcpTimer *timer)
00302 {
00303    //Check whether the timer is running
00304    return timer->running;
00305 }
00306 
00307 
00308 /**
00309  * @brief Check whether a TCP timer has elapsed
00310  * @param[in] timer Pointer to the timer structure
00311  * @return Timer state
00312  **/
00313 
00314 bool_t tcpTimerElapsed(TcpTimer *timer)
00315 {
00316    systime_t time;
00317 
00318    //Check whether the timer is running
00319    if(!timer->running)
00320       return FALSE;
00321 
00322    //Get current time
00323    time = osGetSystemTime();
00324 
00325    //Check whether the specified time interval has elapsed
00326    if(timeCompare(time, timer->startTime + timer->interval) >= 0)
00327       return TRUE;
00328    else
00329       return FALSE;
00330 }
00331 
00332 
00333 /**
00334  * @brief Get current time interval
00335  * @param[in] timer Pointer to the timer structure
00336  * @return Current time interval
00337  **/
00338 
00339 systime_t tcpTimerGetInterval(TcpTimer *timer)
00340 {
00341    //Return current time interval
00342    return timer->interval;
00343 }
00344 
00345 #endif
00346