Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
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
Generated on Tue Jul 12 2022 17:10:17 by
