为了账号安全,请及时绑定邮箱和手机立即绑定

是否可以在同一端口上创建多个(SSL)ServerSocket?

是否可以在同一端口上创建多个(SSL)ServerSocket?

心有法竹 2023-04-19 10:31:10
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 对象吗?文档说:ServerSocket 的构造函数在无法监听指定端口时抛出异常(例如,该端口已被使用)您可能想知道为什么我首先要这样做,而不是简单地让一个线程运行 ServerSocket.accept()。好吧,我的假设是(如果我错了请纠正我)accept() 方法可能需要一些时间才能完成建立连接,特别是如果 ServerSocket 是 SSL(因为握手)。所以如果两个客户端同时想要连接,一个必须等待另一个。这对于高流量服务器来说是非常糟糕的。更新:似乎 accept() 方法将在属于队列的连接建立后立即返回。这意味着如果有一排客户端等待连接,服务器线程可以以最快的方式处理请求并且只需要一个线程。(除了为每个请求创建一个新线程并启动线程所花费的时间,但使用线程池时该时间可以忽略不计)ServerSocket 还有一个名为“backlog”的参数,您可以在其中设置队列中的最大连接数。根据《Java 基础网络》一书 3.3.3TCP 本身可以在接受连接方面领先于 TCP 服务器应用程序。它维护一个连接到侦听套接字的“积压队列”,TCP 本身已完成但尚未被应用程序接受。这个队列存在于底层 TCP 实现和创建监听套接字的服务器进程之间。预完成连接的目的是加快连接阶段,但队列的长度是有限的,以免预先形成太多与服务器的连接,这些服务器由于任何原因不以相同的速率接受它们。当接收到传入连接请求且积压队列未满时,TCP 完成连接协议并将连接添加到积压队列。此时,客户端应用程序已完全连接,但服务器应用程序尚未收到连接作为 ServerSocket.accept 的结果值。当它这样做时,该条目将从队列中删除。我仍然不确定在 SSL 的情况下,握手是否也由 ServerSocket.accept() 并行完成以进行同时连接。更新 2 ServerSocket.accept() 方法本身根本不做任何真正的网络。一旦操作系统建立了新的 TCP 连接,它就会返回。操作系统本身持有一个等待 TCP 连接的队列,可以通过 ServerSocket 构造函数中的“backlog”参数来控制:ServerSocket serverSocket = new ServerSocket(port, 50); //this will create a server socket with a maximum of 50 connections in the queueSSL 握手在客户端调用 Socket.connect()之后完成。因此,一个 ServerSocket.accept() 线程就足够了。
查看完整描述

1 回答

?
慕标5832272

TA贡献1966条经验 获得超4个赞

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

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

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

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

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

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

*:虽然Linux >=3.9 做了某种负载平衡,但仅适用于 UDP(所以不是 SSLServerSocket并且有选项SO_REUSEPORT,这并不是在所有平台上都可用。


查看完整回答
反对 回复 2023-04-19
  • 1 回答
  • 0 关注
  • 134 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信