详解三次握手
为什么 SYN 段不携带数据却要消耗一个序列号呢?
记住:
• 不占用序列号的段是不需要确认的,比如纯 ACK 包
• SYN 段需要对方的确认,需要占用一个序列号
• 凡是消耗序列号的 TCP 报文段,一定需要对端确认。如果这个段
没有收到确认,会一直重传直到达到指定的次数为止。
三次握手第二步:服务端回复 SYN + ACK
服务端收到客户端的 SYN 段以后,将 SYN 和 ACK 标记都置位
「序列号」存放服务端自己的序列号
「确认号」字段指定了对端(客户端)下次发送 段的序号,这里等于客户端 ISN 加一
三次握手中的状态变迁
如果客户端发出去的 SYN 包,服务端⼀直没有回 ACK 会发⽣什么?
客户端重传 SYN 的次数由什么决定?
重传总时间:63s = 1s+2s+4s+8s+16s+32s
$ sysctl -a | grep tcp_syn_retries
net.ipv4.tcp_syn_retries = 6
TCP ⾃连接:⼀个看似 Bug 的现象
源端⼝号和⽬标端⼝号都是⾃⼰可能吗?
测试脚本
self_connect.sh
while true
do
telnet 127.0.0.1 50000
done
如何解决⾃连接问题
• 让服务监听的端⼝与客户端随机分配的端⼝不可能相同即可
• 当出现⾃连接的时候,主动关掉连接
让服务监听的端⼝与客户端临时端⼝号不可能相同即可
客户端临时端⼝号的范围由 /proc/sys/net/ipv4/iplocalport_range
⽂件决定,只要服务端监控的端⼝号不在这个范围内就可以了。
当出现⾃连接的时候,主动关掉连接
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)
}