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)
}