本文介绍了是否可以在同一个端口上创建多个(SSL)ServerSocket?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

限时删除!!

public class ServerThread implements Runnable
{

    private static final int port = 10000;

    @Override
    public void run() {
        ServerSocket serverSocket = new ServerSocket(port);

        while (true) {
            Socket clientSocket = serverSocket.accept();
            ClientThread clientThread = new ClientThread(clientSocket);
            // handle the client request in a separate thread
        }
    }
}

如果我有 10 个不同的线程在运行 ServerThread.run(),这会起作用吗?还是应该为所有线程使用相同的 ServerSocket 对象?

Will this work If I have let's say 10 different threads running ServerThread.run()? Or should I use the same ServerSocket object for all threads?

文档 说:

ServerSocket 的构造函数如果无法侦听指定的端口(例如,该端口已被使用)会抛出异常

您可能想知道为什么我首先要这样做而不是简单地让单个线程运行 ServerSocket.accept().好吧,我的假设是(如果我错了请纠正我)accept() 方法可能需要一些时间来完成建立连接,特别是如果 ServerSocket 是 SSL(因为握手).所以如果两个客户端想同时连接,一个必须等​​待另一个.这对于高流量服务器来说是非常糟糕的.

You may be wondering why I want to do this in the first place and not simply have a single thread running ServerSocket.accept(). Well, my assumption is (correct me if I'm wrong) that the accept() method may take some time to finish establishing a connection, especially if the ServerSocket is SSL (because of handshaking). So if two clients want to connect at the same time, one has to wait for the other. This would be very bad for a high traffic server.

更新: 似乎只要建立属于队列的连接,accept() 方法就会返回.这意味着如果有一个等待连接的客户端队列,服务器线程可以以最快的方式处理请求,并且只需要一个线程.(除了为每个请求创建一个新线程并启动线程所花费的时间,但使用线程池时该时间可以忽略不计)

Update: It seems that the accept() method will return as soon as a connection belonging to a queue is established. This means if there's a queue of clients waiting to connect, the server thread can handle the requests in the fastest way possible and only one thread is needed. (apart from the time it takes to create a new thread for each request and starting the thread, but that time is negligible when using a thread pool)

ServerSocket 还有一个参数叫backlog",可以设置队列中的最大连接数.根据《Java 基础网络》一书 3.3.3

The ServerSocket also has a parameter called "backlog", where you can set the maximum number of connections in the queue. According to the book "Fundamental Networking in Java" 3.3.3

TCP 本身可以在接受连接方面领先于 TCP 服务器应用程序.它维护一个连接到 TCP 本身的监听套接字的积压队列"已完成但尚未被申请接受.这队列存在于底层 TCP 实现和服务器进程之间它创建了监听套接字.预完成连接的目的是为了加快连接阶段,但队列长度有限,以便不要预先形成与不接受它们的服务器的太多连接无论出于何种原因,利率都相同.当收到传入的连接请求时并且积压队列未满,TCP完成连接协议和将连接添加到积压队列.此时,客户端应用程序已完全连接,但服务器应用程序尚未收到连接作为 ServerSocket.accept 的结果值.当它这样做时,条目将从队列中删除.

我仍然不确定在 SSL 的情况下,握手是否也由 ServerSocket.accept() 并行完成以实现同时连接.

I'm still not sure though in the case of SSL, if the handshaking is also done in parallel by ServerSocket.accept() for simultaneous connections.

更新 2 ServerSocket.accept() 方法本身根本不进行任何真正的网络连接.一旦操作系统建立了新的 TCP 连接,它就会返回.操作系统本身持有一个等待TCP连接的队列,可以通过ServerSocket构造函数中的backlog"参数来控制:

Update 2 The ServerSocket.accept() method itself doesn't do any real networking at all. It will return as soon as the operating system has established a new TCP connection. The operating system itself holds a queue of waiting TCP connections, which can be controlled by the "backlog" parameter in the ServerSocket constructor:

ServerSocket serverSocket = new ServerSocket(port, 50);
//this will create a server socket with a maximum of 50 connections in the queue

SSL 握手在客户端调用 Socket.connect() 之后 完成.所以 ServerSocket.accept() 的一个线程就足够了.

SSL handshaking is done after the client calls Socket.connect(). So one thread for ServerSocket.accept() is always enough.

推荐答案

以下是关于您的问题的一些想法:

Here are a few thoughts regarding your problem:

你不能listen()在同一个IP+端口和多个ServerSocket.如果可以,操作系统会将 SYN 数据包传输到哪个套接字?*

You can't listen() on the same IP+port with several ServerSocket. If you could, to which one of the socket would the OS transfer the SYN packet?*

TCP 确实维护了预接受连接的积压,因此对 accept() 的调用将(几乎)立即返回积压队列中的第一个(最旧的)套接字.它通过自动发送 SYN-ACK 数据包来回复客户端发送的 SYN,并等待回复 ACK(3 次握手).但是,正如@zero298 所暗示的那样,尽快接受连接通常不是问题.问题将是处理与您已接受的所有套接字的通信所产生的负载,这很可能使您的服务器瘫痪(这实际上是一种 DoS 攻击)).事实上,backlog 参数通常在这里,所以太多的同时连接在 backlog 队列中等待太长时间等待 accept() 将在到达您的应用程序之前被 TCP 丢弃.

TCP indeed maintains a backlog of pre-accepted connections so a call to accept() will return (almost) immediately the first (oldest) socket in the backlog queue. It does so by sending the SYN-ACK packet automatically in reply to a SYN sent by the client, and waits for the reply-ACK (the 3-way handshake).But, as @zero298 suggests, accepting connections as fast as possible isn't usually the problem. The problem will be the load incurred by processing communication with all the sockets you'll have accepted, which may very well put your server down its knees (it's actually a DoS attack). In fact the backlog parameter is usually here so too many simultaneous connections waiting for too long in the backlog queue to be accept()ed will be dropped by TCP before reaching your application.

不要为每个客户端套接字创建一个线程,我建议您使用 ExecutorService 线程池运行一些最大数量的线程,每个线程处理与一个客户端的通信.这允许系统资源的优雅降级,而不是创建数百万个线程,这反过来会导致线程饥饿、内存问题、文件描述符限制……再加上精心选择的积压值,您将能够获得您的服务器可以提供的最大吞吐量而不会使其崩溃.如果您担心 SSL 上的 DoS,那么您的客户端线程的 run() 方法应该做的第一件事就是在新的服务器上调用 startHandshake()-连接的套接字.

Instead of creating one thread per client socket, I would suggest you use an ExecutorService thread pool running some maximum number of threads, each handling communication with one client. That allows for a graceful degradation of system resources, instead of creating millions of threads which would in turn create thread starvation, memory issues, file descriptor limits, ... Coupled with a carefully-chosen backlog value you'll be able to get the maximum throughput you server can offer without crashing it. And if you're worried about DoS on SSL, the very first thing the run() method of your client-thread should do, is call startHandshake() on the newly-connected socket.

关于 SSL 部分,TCP 本身不能做任何 SSL 预接受,因为它需要执行加密/解码、与密钥库对话等,这远远超出了它的规范.请注意,在这种情况下,您还应该使用 SSLServerSocket.

Regarding the SSL part, TCP itself cannot do any SSL pre-accept, as it need to perform encryption/decoding, talking to a keystore, etc. which are well beyond its specification. Note that you should also use an SSLServerSocket in that case.

要绕过您提供的用例(客户端愿意延迟与您的服务器进行 DoS 握手),您会感兴趣阅读 Oracle 论坛上的帖子,其中 EJP(再次)回答:

To go around the use-case you gave (clients willingly delaying handshake to DoS your server) you'll be interested reading an Oracle forum post about it where EJP (again) answers:

积压队列用于已由 TCP 堆栈完成但尚未被应用程序接受的连接.与 SSL 无关.JSSE 不会进行任何协商,直到您对接受的套接字执行一些 I/O 操作,或者对其调用 startHandshake(),这两者都将在处理连接的线程中执行.我不知道如何利用它来制造 DOS 漏洞,至少不是特定于 SSL 的漏洞.如果您遇到 DOS 情况,很可能您是在 accept() 线程中执行 I/O,而应该在连接处理线程中完成.

*:虽然 Linux >=3.9 做了某种负载均衡,但仅适用于 UDP(所以 not SSLServerSocket) and 带有选项 SO_REUSEPORT,无论如何都不是在所有平台上都可用.

*: Though Linux >=3.9 does some kind of load-balancing, but for UDP only (so not SSLServerSocket) and with option SO_REUSEPORT, which is not available on all platforms anyway.

这篇关于是否可以在同一个端口上创建多个(SSL)ServerSocket?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

1403页,肝出来的..

09-09 02:43