void tcpStateCloseWait(Socket *socket, TcpHeader *segment, size_t length) { uint_t flags = 0; //Debug message TRACE_DEBUG("TCP FSM: CLOSE-WAIT state\r\n"); //First check sequence number if(tcpCheckSequenceNumber(socket, segment, length)) return; //Check the RST bit if(segment->flags & TCP_FLAG_RST) { //Switch to the CLOSED state tcpChangeState(socket, TCP_STATE_CLOSED); //Number of times TCP connections have made a direct transition to the //CLOSED state from either the ESTABLISHED state or the CLOSE-WAIT state MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpEstabResets, 1); //Return immediately return; } //Check the SYN bit if(tcpCheckSyn(socket, segment, length)) return; //Check the ACK field if(tcpCheckAck(socket, segment, length)) return; #if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED) //Duplicate AK received? if(socket->dupAckCount > 0) flags = SOCKET_FLAG_NO_DELAY; #endif //The Nagle algorithm should be implemented to coalesce //short segments (refer to RFC 1122 4.2.3.4) tcpNagleAlgo(socket, flags); }
void tcpStateEstablished(Socket *socket, TcpHeader *segment, const NetBuffer *buffer, size_t offset, size_t length) { uint_t flags = 0; //Debug message TRACE_DEBUG("TCP FSM: ESTABLISHED state\r\n"); //First check sequence number if(tcpCheckSequenceNumber(socket, segment, length)) return; //Check the RST bit if(segment->flags & TCP_FLAG_RST) { //Switch to the CLOSED state tcpChangeState(socket, TCP_STATE_CLOSED); //Number of times TCP connections have made a direct transition to the //CLOSED state from either the ESTABLISHED state or the CLOSE-WAIT state MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpEstabResets, 1); //Return immediately return; } //Check the SYN bit if(tcpCheckSyn(socket, segment, length)) return; //Check the ACK field if(tcpCheckAck(socket, segment, length)) return; //Process the segment text if(length > 0) tcpProcessSegmentData(socket, segment, buffer, offset, length); //Check the FIN bit if(segment->flags & TCP_FLAG_FIN) { //The FIN can only be acknowledged if all the segment data //has been successfully transferred to the receive buffer if(socket->rcvNxt == (segment->seqNum + length)) { //Advance RCV.NXT over the FIN socket->rcvNxt++; //Send an acknowledgement for the FIN tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE); //Switch to the CLOSE-WAIT state tcpChangeState(socket, TCP_STATE_CLOSE_WAIT); } } #if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED) //Duplicate AK received? if(socket->dupAckCount > 0) flags = SOCKET_FLAG_NO_DELAY; #endif //The Nagle algorithm should be implemented to coalesce //short segments (refer to RFC 1122 4.2.3.4) tcpNagleAlgo(socket, flags); }
error_t tcpSend(Socket *socket, const uint8_t *data, size_t length, size_t *written, uint_t flags) { uint_t n; uint_t totalLength; uint_t event; //Check whether the socket is in the listening state if(socket->state == TCP_STATE_LISTEN) return ERROR_NOT_CONNECTED; //Actual number of bytes written totalLength = 0; //Send as much data as possible do { //Wait until there is more room in the send buffer event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_READY, socket->timeout); //A timeout exception occurred? if(event != SOCKET_EVENT_TX_READY) return ERROR_TIMEOUT; //Check current TCP state switch(socket->state) { //ESTABLISHED or CLOSE-WAIT state? case TCP_STATE_ESTABLISHED: case TCP_STATE_CLOSE_WAIT: //The send buffer is now available for writing break; //LAST-ACK, FIN-WAIT-1, FIN-WAIT-2, CLOSING or TIME-WAIT state? case TCP_STATE_LAST_ACK: case TCP_STATE_FIN_WAIT_1: case TCP_STATE_FIN_WAIT_2: case TCP_STATE_CLOSING: case TCP_STATE_TIME_WAIT: //The connection is being closed return ERROR_CONNECTION_CLOSING; //CLOSED state? default: //The connection was reset by remote side? return (socket->resetFlag) ? ERROR_CONNECTION_RESET : ERROR_NOT_CONNECTED; } //Determine the actual number of bytes in the send buffer n = socket->sndUser + socket->sndNxt - socket->sndUna; //Exit immediately if the transmission buffer is full (sanity check) if(n >= socket->txBufferSize) return ERROR_FAILURE; //Number of bytes available for writing n = socket->txBufferSize - n; //Calculate the number of bytes to copy at a time n = MIN(n, length - totalLength); //Any data to copy? if(n > 0) { //Copy user data to send buffer tcpWriteTxBuffer(socket, socket->sndNxt + socket->sndUser, data, n); //Update the number of data buffered but not yet sent socket->sndUser += n; //Advance data pointer data += n; //Update byte counter totalLength += n; //Total number of data that have been written if(written != NULL) *written = totalLength; //Update TX events tcpUpdateEvents(socket); //To avoid a deadlock, it is necessary to have a timeout to force //transmission of data, overriding the SWS avoidance algorithm. In //practice, this timeout should seldom occur (see RFC 1122 4.2.3.4) if(socket->sndUser == n) tcpTimerStart(&socket->overrideTimer, TCP_OVERRIDE_TIMEOUT); } //The Nagle algorithm should be implemented to coalesce //short segments (refer to RFC 1122 4.2.3.4) tcpNagleAlgo(socket, flags); //Send as much data as possible } while(totalLength < length); //The SOCKET_FLAG_WAIT_ACK flag causes the function to //wait for acknowledgement from the remote side if(flags & SOCKET_FLAG_WAIT_ACK) { //Wait for the data to be acknowledged event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_ACKED, socket->timeout); //A timeout exception occurred? if(event != SOCKET_EVENT_TX_ACKED) return ERROR_TIMEOUT; //The connection was closed before an acknowledgement was received? if(socket->state != TCP_STATE_ESTABLISHED && socket->state != TCP_STATE_CLOSE_WAIT) return ERROR_NOT_CONNECTED; } //Successful write operation return NO_ERROR; }