从backlog参数到TCP状态

Java Socket的api可能很多人会用,但是Java Socket的参数可能很多人都不知道用来干嘛的,甚至都不知道有这些参数。吃饭的路上听到别人聊到backlog参数,于是查了下:

backlog 用于ServerSocket,配置ServerSocket的最大客户端等待队列。

等待队列的意思,先看下面代码

public class Main {
    public static void main(String[] args) throws Exception {
        int port = 8999;
        int backlog = 2;
        ServerSocket serverSocket = new ServerSocket(port, backlog);
        //注意这里只accept了一个连接
        Socket clientSock = serverSocket.accept();
        System.out.println("revcive from " + clientSock.getPort());
        while (true) {
            byte buf[] = new byte[1024];
            int len = clientSock.getInputStream().read(buf);
            System.out.println(new String(buf, 0, len));
        }
    }
}

backlog设置为2表示等待队列的长度为2,因为代码中只accept了一个连接所以后面的连接都应该会放在等待队列中。
这里可能存在几个疑惑的点:
1.等待队列中的连接处于什么状态,到底tcp连接有没有建立起来?
2.如果等待队列长度已满会发生什么?

首先我们看下8999端口上的连接状态

netstat -an|grep “.\8999”

file
接下来我们通过telnet建立第一个连接

telnet localhost 8999

file
file

可以看见第一个连接已经建立好了,接下来再进来的连接就应该在会在等待队列中了

telnet localhost 8999 执行两次后

file

file

这里至少可以说明两点:
1.等待队列中的连接处于ESTABLISHED,tcp连接是有建立起来的
2.三次握手的过程是由内核帮我们完成的,连接的建立与我们socket.accept()方法无关,每次调用socket.accept()方法可以看做是等待队列中take一个连接

继续探索下一个问题 “如果等待队列长度已满会发生什么?”
目前服务端连接数是三个,一个连接在处理,等待队列长度为2,所以此时等待队列已经满了
so just

telnet localhost 8999

这时候你会发现创建新的连接已经超时了
file
file

而此时客户端发送连接请求后一直处于SYN_SENT状态,并没有得到服务端的响应

backlog这个参数设置为-1表示无限制,默认是50个最大等待队列,如果设置无限制,但服务器无法处理那么多连接,那么当很多客户端连到你的服务器时,每一个TCP连接都会占用服务器的内存,会导致服务器崩溃。
另外,就算你设置了backlog为10,如果你的代码中是一直Socket clientSock = serverSocket.accept(),假设我们的机器最多可以同时处理100个请求,总共有100个线程在运行,然后你把在100个线程的线程池处理clientSock,不能处理的clientSock就排队,最后clientSock越来越多,也意味着TCP连接越来越多,也意味着我们的服务器的内存使用越来越高(客户端连接进程,肯定会发送数据过来,数据会保存到服务器端的TCP接收缓存区),最后服务器就宕机了。所以如果你不能处理那么多请求,请不要循环无限制地调用serverSocket.accept(),否则backlog也无法生效。如果真的请求过多,只会让你的服务器宕机(相信很多人都是这么写,要注意点)。

这里主要是对backlog参数做了一个实验,下篇文章我们一起来看看tcp连接状态是怎么转换的。

# tcp 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×