唯一能够确定一个socket连接有4点

  1. 服务器的IP
  2. 服务器的Port
  3. 客户端的IP
  4. 客户端的Port

tomcat最多能建立多少个连接?tomcat作为服务端程序,一直在监听80端口,之前一直以为tomcat每接收到一个新的连接,都会创建一个新的socket,然后这个socket又会占用一个端口。但是事实上并不是这样的,肯定是会创建新的socket的,但是这个新创建的socket并不会占用新的端口。
那么问题来了,客户端发送的数据,服务器是怎么区分是哪个客户端?
关键就在于上面的4个点,标识一个socket的是上面的4个点,而不是单纯的一个本地ip和一个本地端口,所以tomcat每accpet到一个连接,都会创建一个新的socket连接,这个socket连接种包含了下面4个信息,假设现在tomcat的ip为12.1.11.11

服务器ip:12.1.11.11
服务器端口:80
客户端ip:213.32.1.2
客户端端口:80

由这4个信息标识一个唯一的socket,tomcat accept到的每一个连接的服务器ip和端口都是一样的,但是客户端的ip和端口是不一样的,根据唯一标识socket的4点,系统能正确的区分。

服务端的socket有什么特殊的?

其实就我们创建一个新的socket来说,他是没有什么特殊的,他就是一个未连接的socket,特殊的点在于socket.listen()方法

#include<sys/socket.h>
int listen(int sockfd, int backlog);

listen函数仅由TCP服务器调用,当socket函数创建一个套接口时,它被假设为一个主动套装口,也就是说,它是一个将调用connet发起连接的客户套接口。listen函数把一个未连接的套接口转换成一个被动套接口,指示内核应接受指向该套接口的连接请求。根据TCP状态转换图,调用listen导致套接口从CLOSED状态转换到LISTEN状态。
注意,在java中是没有listen()函数的,因为java帮我们进行了封装,使用了ServerSocket来专门代表监听的Socket。
运行下面这个简单的程序

public static void main(String[] args) throws IOException, InterruptedException {
    ServerSocket serverSocket = new ServerSocket(21111);
    Thread.sleep(100000000);
}

在windows中我们就能通过netstat -ano来查看端口占用,可以发现21111正处于监听状态

keepalived监听多个服务_服务器


我们查看Java中的ServerSocket类的构造函数

keepalived监听多个服务_keepalived监听多个服务_02


能够看到,其实这与我们的c语言中的listen()函数差不多,其也有一个backlog参数。

为了更好的理解backlog参数,我们必须认识到内核为任何一个给定的监听套接口维护两个队列:

1、未完成连接队列(incomplete connection queue),每个这样的SYN分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手过程。这些套接口处于SYN_RCVD状态。

2、已完成连接队列(completed connection queue),每个已完成TCP三路握手过程的客户对应其中一项。这些套接口处于ESTABLISHED状态。

当来自客户的SYN到达时,TCP在未完成连接队列中创建一个新项,然后响应以三路握手的第二个分节:服务器的SYN响应,其中稍带对客户SYN的ACK(即SYN+ACK)。这一项一直保留在未完成连接队列中,直到三路握手的第三个分节(客户对服务器SYN的ACK)到达或者该项超时为止(曾经源自Berkeley的实现为这些未完成连接的项设置的超时值为75秒)。如果三路握手正常完成,该项就从未完成连接队列移到已完成连接队列的队尾。当进程调用accept时,已完成连接队列中的队头项将返回给进程,或者如果该队列为空,那么进程将被投入睡眠,直到TCP在该队列中放入一项才唤醒它。

如果监听的线程释放掉监听用的SOCKET了,会影响之前通过这个监听SOCKET建立的TCP连接么?

这个问题的答案是不会的,利用ServerSocket建立的连接,即使是ServerSocket关闭了,这个连接依然还是存在,因为他们之间本身就没有任何关系了

测试代码,Server

public static void main(String[] args) throws IOException, InterruptedException {
    ServerSocket serverSocket = new ServerSocket();
    serverSocket.bind(new InetSocketAddress(8080));
    Socket accept = serverSocket.accept();
    new Thread(() -> {
        try {
            OutputStream outputStream = accept.getOutputStream();
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
            while (true) {
                outputStreamWriter.write(UUID.randomUUID().toString() + System.lineSeparator());
                outputStreamWriter.flush();
                Thread.sleep(1000);
                System.out.println("写了一个");
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
    serverSocket.close();
    System.out.println("连接关闭");
    Thread.sleep(100000);
}

Client

public static void main(String[] args) throws IOException, InterruptedException {
    Socket socket = new Socket();
    socket.connect(new InetSocketAddress("localhost", 8080));
    InputStream in = socket.getInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    String msg;
    while ((msg = reader.readLine()) != null) {
        System.out.println("读了一个");
        System.out.println(msg);
    }
    System.out.println("dsadsa");
    Thread.sleep(100000);
}

运行这两个代码我们就能发现,即使是ServerSocket关闭,依然能继续通信

我们又利用netstat -ano命令来查看端口占用

keepalived监听多个服务_客户端_03

keepalived监听多个服务_服务器_04