📄 tcp.c
字号:
// If i = 5, we are not connected. If it is a SYN then assign
// a temporary conection to it for processing
if (i == 5)
{
if (tcp->flags & FLG_SYN)
{
// Find first unused connection (one with IP = 0)
for (j=0; j < 5; j++)
{
if (conxn[j].ipaddr == 0)
{
nr = j;
// Initialize new connection
conxn[nr].state = STATE_LISTEN;
break;
}
}
// If all connections are used then drop msg
if (j == 5) return;
if (debug)
{
serial_send("TCP: New connection ");
memset(text, 0, 10);
itoa((UINT)nr, text, 10);
serial_send(text);
serial_send("\r");
}
}
}
// By now we should have a connection number in range of 0-4
// Do a check to avoid any chance of exceeding size of struct
if (nr > 4)
{
if (debug) serial_send("TCP: Error in assigning conxn number\r");
return;
}
// Eventually put in protection against wrapping sequence
// numbers, for now make the client start over if his
// sequence number is close to wrapping
if (tcp->sequence > 0xFFFFFF00L)
{
if (debug) serial_send("TCP: Rcvd a high sequence number\r");
conxn[nr].ipaddr = 0;
tcp_send(FLG_RST, 20, NO_CONNECTION);
return;
}
// Handle messages whose action is mostly independent of state
// such as RST, SYN, and segment with no ACK. That way the
// state machine below does not need to worry about it.
if (tcp->flags & FLG_RST)
{
// An RST does not depend on state at all. And it does
// not count as data so do not send an ACK here. Close
// connection
if (debug) serial_send("TCP: Rcvd a reset\r");
conxn[nr].ipaddr = 0;
return;
}
else if (tcp->flags & FLG_SYN)
{
// A SYN segment only makes sense if connection is in LISTEN
if ((conxn[nr].state != STATE_LISTEN) &&
(conxn[nr].state != STATE_CLOSED))
{
if (debug) serial_send("TCP: Error, rcvd bogus SYN\r");
conxn[nr].ipaddr = 0;
tcp_send(FLG_RST, 20, NO_CONNECTION);
return;
}
}
else if ((tcp->flags & FLG_ACK) == 0)
{
// Incoming segments except SYN or RST must have ACK bit set
// See TCP/IP Illustrated, Vol 2, Page 965
// Drop segment but do not send a reset
if (debug) serial_send("TCP: Error, rcvd segment has no ACK\r");
return;
}
// Compute length of header including options, and from that
// compute length of actual data
header_len = (tcp->flags & 0xF000) >> 10;
data_len = len - header_len;
// Handle TCP state machine for this connection
switch (conxn[nr].state)
{
case STATE_CLOSED:
case STATE_LISTEN:
// If incoming segment contains SYN and no ACK, then handle
if ((tcp->flags & FLG_SYN) && ((tcp->flags & FLG_ACK) == 0))
{
// Capture his starting sequence number and generate
// my starting sequence number
// Fill in connection information
conxn[nr].ipaddr = ip->source_ipaddr;
conxn[nr].port = tcp->source_port;
conxn[nr].state = STATE_LISTEN;
conxn[nr].his_sequence = 1 + tcp->sequence;
conxn[nr].his_ack = tcp->ack_number;
// Use system clock for initial sequence number
EA = 0;
conxn[nr].my_sequence = initial_sequence_nr;
initial_sequence_nr += 64000L;
EA = 1;
// Send header options with the next message
// Since timestamps are optional and we do not use
// them, do not have to send them
// After sending the SYN ACK the client browser will
// blast me with 2 messages, an ACK, and a HTTP GET
tcp_send(FLG_SYN | FLG_ACK, 28, nr);
// My SYN flag increments my sequence number
// My sequence number is always updated to point to
// the next byte to be sent. So the incoming ack
// number should equal my sequence number
conxn[nr].my_sequence++;
conxn[nr].state = STATE_SYN_RCVD;
if (debug) serial_send("TCP: Entered SYN RCVD state\r");
}
else
{
// Sender is out of sync so send reset
conxn[nr].ipaddr = 0;
tcp_send(FLG_RST, 20, NO_CONNECTION);
}
break;
case STATE_SYN_RCVD:
// He may already be sending me data - should process it
conxn[nr].his_sequence += data_len;
conxn[nr].his_ack = tcp->ack_number;
if (tcp->flags & FLG_FIN)
{
// His FIN counts as a byte of data
conxn[nr].his_sequence++;
tcp_send(FLG_ACK, 20, nr);
conxn[nr].state = STATE_CLOSE_WAIT;
if (debug) serial_send("TCP: Entered CLOSE_WAIT state\r");
// At this point we would normally wait for the application
// to close. For now, send FIN right away.
tcp_send(FLG_FIN | FLG_ACK, 20, nr);
conxn[nr].my_sequence++; // For my FIN
conxn[nr].state = STATE_LAST_ACK;
if (debug) serial_send("TCP: Entered LAST ACK state\r");
}
// Make sure he is ACKing my SYN
else if (tcp->ack_number == conxn[nr].my_sequence)
{
conxn[nr].state = STATE_ESTABLISHED;
if (debug) serial_send("TCP: Entered ESTABLISHED state\r");
// If sender sent data ignore it and he will resend
// Do not send response because we received no
// data... wait for client to send something to me
}
break;
case STATE_ESTABLISHED:
conxn[nr].his_ack = tcp->ack_number;
if (tcp->flags & FLG_FIN)
{
// His FIN counts as a byte of data
conxn[nr].his_sequence++;
tcp_send(FLG_ACK, 20, nr);
conxn[nr].state = STATE_CLOSE_WAIT;
if (debug) serial_send("TCP: Entered CLOSE_WAIT state\r");
// At this point we would normally wait for the application
// to close. For now, send FIN immediately.
tcp_send(FLG_FIN | FLG_ACK, 20, nr);
conxn[nr].my_sequence++; // For my FIN
conxn[nr].state = STATE_LAST_ACK;
if (debug) serial_send("TCP: Entered LAST ACK state\r");
}
else if (data_len != 0)
{
// Received normal TCP segment from sender with data
// Send an ACK immediately and pass the data on to
// the application
conxn[nr].his_sequence += data_len;
tcp_send(FLG_ACK, 20, nr); // Send ACK
// Send pointer to start of TCP payload
// http_server increments my sequence number when
// sending so don't worry about it here
result = http_server(inbuf, header_len, nr, 0);
// Start timer to close conxn if no activity
conxn[nr].inactivity = INACTIVITY_TIME;
}
break;
case STATE_CLOSE_WAIT:
// With this code, should not get here
if (debug) serial_send("TCP: Oops! Rcvd unexpected message\r");
break;
case STATE_LAST_ACK:
conxn[nr].his_ack = tcp->ack_number;
// If he ACK's my FIN then close
if (tcp->ack_number == conxn[nr].my_sequence)
{
conxn[nr].state = STATE_CLOSED;
conxn[nr].ipaddr = 0; // Free up struct area
just_closed = TRUE;
}
break;
case STATE_FIN_WAIT_1:
// He may still be sending me data - should process it
conxn[nr].his_sequence += data_len;
conxn[nr].his_ack = tcp->ack_number;
if (tcp->flags & FLG_FIN)
{
// His FIN counts as a byte of data
conxn[nr].his_sequence++;
tcp_send(FLG_ACK, 20, nr);
// If he has ACK'd my FIN then we can close connection
if (tcp->ack_number == conxn[nr].my_sequence)
{
conxn[nr].state = STATE_TIME_WAIT;
if (debug) serial_send("TCP: Entered TIME_WAIT state\r");
conxn[nr].state = STATE_CLOSED;
conxn[nr].ipaddr = 0; // Free up connection
just_closed = TRUE;
}
else
{
// He has not ACK'd my FIN. This happens when there is a simultaneous
// close - I got his FIN but he has not yet ACK'd my FIN
conxn[nr].state = STATE_CLOSING;
if (debug) serial_send("TCP: Entered CLOSING state\r");
}
}
else if (tcp->ack_number == conxn[nr].my_sequence)
{
// He has ACK'd my FIN but has not sent a FIN yet himself
conxn[nr].state = STATE_FIN_WAIT_2;
if (debug) serial_send("TCP: Entered FIN_WAIT_2 state\r");
}
break;
case STATE_FIN_WAIT_2:
// He may still be sending me data - should process it
conxn[nr].his_sequence += data_len;
conxn[nr].his_ack = tcp->ack_number;
if (tcp->flags & FLG_FIN)
{
conxn[nr].his_sequence++; // For his FIN flag
tcp_send(FLG_ACK, 20, nr);
conxn[nr].state = STATE_TIME_WAIT;
if (debug) serial_send("TCP: Entered TIME_WAIT state\r");
conxn[nr].state = STATE_CLOSED;
conxn[nr].ipaddr = 0; // Free up struct area
just_closed = TRUE;
}
break;
case STATE_TIME_WAIT:
// With this code, should not get here
if (debug) serial_send("TCP: Oops! In TIME_WAIT state\r");
break;
case STATE_CLOSING:
// Simultaneous close has happened. I have received his FIN
// but he has not yet ACK'd my FIN. Waiting for ACK.
// Will not receive data in this state
conxn[nr].his_ack = tcp->ack_number;
if (tcp->ack_number == conxn[nr].my_sequence)
{
conxn[nr].state = STATE_TIME_WAIT;
if (debug) serial_send("TCP: Entered TIME_WAIT state\r");
// Do not send any response to his ACK
conxn[nr].state = STATE_CLOSED;
conxn[nr].ipaddr = 0; // Free up struct area
just_closed = TRUE;
}
break;
default:
if (debug) serial_send("TCP: Error, no handler\r");
break;
}
// This is for debug, to see when conxn closes
if (just_closed)
{
just_closed = FALSE;
if (debug)
{
serial_send("TCP: Closed connection ");
memset(text, 0, 10);
itoa((UINT)nr, text, 10);
serial_send(text);
serial_send("\r");
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -