Detailed three-way handshake
Why does the SYN segment not carry data but consume a sequence number?
Remember:
• A segment that does not occupy a sequence number does not need to be confirmed, such as a pure ACK packet
• A SYN segment requires the confirmation of the other party and needs to occupy a sequence number
• All TCP segments that consume a sequence number must be confirmed by the other end. If
no acknowledgment is received for this segment, it will be retransmitted until the specified number of times is reached.
The second step of the three-way handshake: the server replies with SYN + ACK
After the server receives the SYN segment from the client, it sets both the SYN and ACK flags. The
"Serial Number" stores the server's own serial number.
The "Confirmation Number" field specifies the serial number of the peer (client) to send the next segment. Here Equal to client ISN plus one
State transitions in the three-way handshake
What will happen if the server never returns an ACK to the SYN packet sent by the client?
What determines the number of times the client retransmits the SYN?
Total retransmission time: 63s = 1s+2s+4s+8s+16s+32s
$ sysctl -a | grep tcp_syn_retries
net.ipv4.tcp_syn_retries = 6
TCP self-connection: a phenomenon that seems to be a bug
Are both source and destination port numbers possible?
Test script
self_connect.sh
while true
do
telnet 127.0.0.1 50000
done
How to solve the self-connection problem
• The port that the service monitors is not the same as the port randomly assigned by the client
• When a self-connection occurs, actively close the connection
The port monitored by the service cannot be the same as the temporary port number of the client. The
range of the temporary port number of the client is
determined by the /proc/sys/net/ipv4/iplocalport_range file, as long as the port number monitored by the server is not in the This range is fine.
When a self-connection occurs, actively close the connection
func (sd *sysDialer) doDialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) {
fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", sd.Dialer.Control)
for i := 0; i < 2 && (laddr == nil || laddr.Port == 0) && (selfConnect(fd, err) || spuriousENOTAVAIL(err)); i++ {
if err == nil {
fd.Close()
}
return newTCPConn(fd), nil
}
func selfConnect(fd *netFD, err error) bool {
// If the connect failed, we clearly didn't connect to ourselves.
if fd.laddr == nil || fd.raddr == nil {
return true
}
l := fd.laddr.(*TCPAddr)
r := fd.raddr.(*TCPAddr)
return l.Port == r.Port && l.IP.Equal(r.IP)
}