0. 背景介绍:

client向server的54321端口发起tcp连接,三次握手之后没有传输数据,在server端抓包如下:

可以看到,在458秒的时候,server向第三台机器发送了一个SYN包,它选择的源端口却是我们的监听端口54321,这就奇怪了,再仔细看看会发现这个SYN包比普通的SYN包要“苗条”一些,它少了一些选项字段;

进一步要明确的就是这个第三方的目的地址和端口是什么服务呢?略过各种排查询问不表,最终得知在这台server上有一个定时任务会使用nmap工具进行端口扫描,而我们抓包看到的第三方机器正是扫描的目的地址和端口之一;

namp在进行SYN探测的时候会采用raw-packet的方式自行构造数据包向外发送,从而绕过了内核协议栈流程,这样做的好处就是不占用实际的连接资源,不用真正进行三次握手过程。由于报文是自行构造的,所以上图中的第4个报文看起来与其他经由协议栈标准流程产生的SYN包有所不同;

三言两语就把本次的主题说完了,实际上在排查的过程中想到的问题还要更多一些,下面我们再回过头做些拓展的学习;


1. ip_local_port_range

在看到这个奇怪的SYN报文时的第一反应就是难道说内核在选择随机源端口的时候不会跳过处于监听状态的端口号吗?

我们知道,在TCP/IP协议栈中,随机源端口的选择范围由ip_local_port_range指出,在本例的环境中如下:

而在本例中,server端的监听端口为54321,正好落在上述范围内,那么server端的协议栈在向外连接选择源端口的过程中会不会没有跳过这个监听端口呢。让我们写段程序来验证一下,在server端跑一个程序,该程序不断向外发送tcp连接请求,同时进行抓包观察:

可以看到,协议栈按顺序递增的次序依次选取源端口号,同时跳过了监听端口54321,从54320直接跳到54322;

另外,从下图中还可以看出,协议栈在选取源端口的时候,确实遵循了由ip_local_port_range所列出的范围:

当尝试到61000后,又回绕到32768;


2. tcp的self-connection

tcp的self-connection现象是指:当client和server处在同一台主机,且server端没有正常启动,client反复尝试连接,最后会连接上自己的现象;

首先,tcp self-connection现象出现的前提条件为:

  • client和server处在同一台主机上
  • server端没有正常启动,即监听端口不存在
  • server端的监听端口处在ip_local_port_range指定的范围内;

1) 在实际测试的过程中,使client程序不断的尝试连接本机54321端口,但该端口此时并没有被监听;

2) 一开始connect()调用会返回errno 111, Connection refused;

3) 直到某个时刻,连接成功建立,如下图:

Java socket监听网络数据 java监听tcp端口_目的地址

4) 此后如果继续尝试连接,则会返回errno 106, Transport endpoint is already connected;

这是因为协议栈在处理连接请求的时候会对套接字的状态进行判断,而此时该套接字已经处于连接状态了;

5) 当client进程退出后,连接的状态如下图:

Java socket监听网络数据 java监听tcp端口_抓包_02

连接进入TIME_WAIT状态,与主动关闭的流程一致;

关于tcp self-connection的成因分析网上有很多资料,这里就不再赘述了,有兴趣的朋友可以再进一步检索;


更多的网络与协议分享还将继续呈现,春节就要到了,祝大家have a good time!