Tcp自连接
如果客户端和服务端都在同一个环境,并且客户端先于服务端启动,那么很有可能产生自连接的现象。
所谓自连接,就是tcp两端使用了同一个端口进行连接,即localhost:port->localhost:port。
实现自连接
下面提供一个例子来验证tcp自连接
代码:recipes-master\python\self-connect.py
#!/usr/bin/pythonimport errno
import socket
import sys
import timeif len(sys.argv) < 2:print "Usage: %s port" % sys.argv[0]print "port should in net.ipv4.ip_local_port_range"
else:port = int(sys.argv[1])for i in range(65536):try:# 循环对端口port创建一个连接sock = socket.create_connection(('localhost', port))print "connected", sock.getsockname(), sock.getpeername()time.sleep(60*60)except socket.error, e:if e.errno != errno.ECONNREFUSED:break
我们选取一个没有正在监听的端口,可以通过netstat -ntlp
查看正在监听的tcp端口,然后我们再从ip_local_port_range,可以通过以下命令查看ip_local_port_range
sudo sysctl net.ipv4.ip_local_port_range
我们这里选取33000,执行文件
[root@localhost python]# python self-connect.py 33000
connected ('::1', 33000, 0, 0) ('::1', 33000, 0, 0)
...
前面一个就是我们的源端口,而后面一个就是我们的目的端口,而地址都是我们的本地地址,我们可以看见已经出现了自连接的情况
原因详解
- 客户端从ip_local_port_range中选取了一个临时端口 x 后,客户端发送syn报文到33000端口,但因为 33000 没有打开,因此连接失败 。
- 再次尝试连接,重新选取临时端口号 x + 1 ,再次尝试连接,还是失败。
- 进行到n次尝试时,选取的临时端口号刚好是 33000 端口,然后向 33000 端口发起syn报文,此时因为33000是被选取的端口,已经被打开,所以此时会连接上。
解决方案
- 在连接成功后,判断一下是不是自连接,也就是源ip端口与目的ip端口是不是相同,相同的话就断开此连接
#include<sys/socket.h>// 获取本地连接信息
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);// 获取服务端连接信息
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
- 选取一个ip_local_port_range以外的端口作为服务端端口,或者设置ip_local_port_range范围在服务端端口以外,让客户端端口与服务端端口不冲突