1.自连接是什么
在发起连接时,TCP/IP的协议栈会先选择source IP和source port,在没有显示调用bind()的情况下,source IP由路由表确定,source port由TCP/IP协议栈从local port range中选取尚未使用的port。
如果destination IP正好是本机,而destination port位于local port range,且没有服务器监听(listen)的话,source port可能正好选中了destination port,这就出现了(source IP,source port)=(destination IP,destination port)的情况,即发生了自连接。
2.自连接是怎么发生的
1. 两个tcp连接同时打开
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ax6Cx2X-1620216330809)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/43753ba6ae7f4b3eb984a2d0ff204720~tplv-k3u1fbpfcp-watermark.image)]
2.本来正常运行的客户端和服务端,服务端偶然挂掉之后,客户端再去连接它,就有可能出现自连接现象。
例如:同一台机器上的客户端和服务端(端口:50000)建立了一个长连接,服务端的进程挂掉了,端口50000释放,客户端去connect这个服务端的目的端口的时候正好选择了50000作为源端口,此时该端口没人用(因为服务端挂断了),使用是合法的,于是自连接形成了。
3.如何防止自连接
1. 让服务监听的端口与客户端随机分配的端口不可能相同即可
因为客户端随机分配的范围由 /proc/sys/net/ipv4/ip_local_port_range 文件决定,,所以只要让服务监听的端口小于 客户端分配的端口范围,就不会出现客户端与服务端口相同的情况。
2. 出现自连接的时候,主动关掉连接
例如在Go语言的tcp库中,加入的自连接判断
func selfConnect(fd *netFD, err error) bool {// If the connect failed, we clearly didn't connect to ourselves.if err != nil {return false}// The socket constructor can return an fd with raddr nil under certain// unknown conditions. The errors in the calls there to Getpeername// are discarded, but we can't catch the problem there because those// calls are sometimes legally erroneous with a "socket not connected".// Since this code (selfConnect) is already trying to work around// a problem, we make sure if this happens we recognize trouble and// ask the DialTCP routine to try again.// TODO: try to understand what's really going on.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)
}