Java TCP 服务器 class 抽象

Java TCP server class abstraction

出于学习目的,我正在编写一个 Java TCP 服务器。这被包装到它自己的 class 中作为 SyteTCPServer,它使用 ServerSocket 来处理连接逻辑。这是一个学校项目,其中良好的代码实践非常重要。

它被放在自己的 class 中的原因是因为有一个特定的应用程序级协议。 class 是一个更大项目的一部分。

谷歌搜索时,我只发现人们将所有 ServerSocket 及其附带的逻辑直接放在 main 方法中。我不确定这是面向对象编程的方式吗?

我的 SyteTCPServer 有一个简单的 Start()Stop() 方法,它隐藏了使用 ServerSocket、处理客户端等的实现。

但是,我有点难过,因为有很多网络功能,IntelliJ 警告我捕获 IOExceptions。接受客户端可能会抛出 IOException,因此可能会得到所述客户端的输出流,writeBytes,..你明白我的意思了。

在抽象的上下文中,我如何最好地处理这些异常?我是否在方法旁边写了一个 throws IOException,使上层代码 try...catch 成为 SyteTCPServer.Start

此外,如果发生任何异常,整个服务器应该停止,还是我 "hide" 其中一个客户端没有正确连接?或者,我应该在客户端连接失败时触发事件吗?我真的不知道..

由于UI/core代码分离,我也避免在像瘟疫一样发生异常时写入输出。

我希望这些问题不会太多。

这是我的启动方法示例,抛出 IOException:

public void Start() throws IOException {
    this.listenSocket = new ServerSocket(this.port);

    Listen();
}

我认为区分可能收到的不同 IOExceptions 很重要。例如,它是创建新连接时的异常还是连接已经建立时的异常,这是否是某种预期的错误?最简单的方法是只阅读 documentation 并根据实际情况处理错误:

用于构建服务器套接字:

public ServerSocket(int port) throws IOException

  • IOException - if an I/O error occurs when opening the socket.
  • SecurityException - if a security manager exists and its checkListen method doesn't allow the operation.
  • IllegalArgumentException - if the port parameter is outside the specified range of valid port values, which is between 0 and 65535, inclusive.

接受连接:

public Socket accept() throws IOException

  • IOException - if an I/O error occurs when waiting for a connection.
  • SecurityException - if a security manager exists and its checkAccept method doesn't allow the operation.
  • SocketTimeoutException - if a timeout was previously set with setSoTimeout and the timeout has been reached.
  • IllegalBlockingModeException - if this socket has an associated channel, the channel is in non-blocking mode, and there is no connection ready to be accepted

以此类推

关于在哪里捕获异常:这也取决于您希望服务器在特定情况下执行的操作。但一般情况下,捕获'expected'个错误并当场处理,但将非故意的错误抛到更高的级别。

我的意思是:

public void foo() throws IOException {
    try {
        serverSocket = new ServerSocket(PORT);
    } catch (IOException e) {
        // Port is in use -> perhaps retry on another port
        // If things fail, throw exception anyway
    } finally {
        if (!serverSocket.isClosed()) {
            try {
                serverSocket.close();
            } catch (IOException e) {
                // This exception is to be taken care of internally, not thrown
            }
        }
    }
}

另请注意,这不仅适用于服务器端,也适用于客户端。

祝你写服务器好运!

I only found people putting all the ServerSocket and its accompanying logic directly inside the main method. I'm not sure that's the way to go when it comes to OOP?

将所有 ServerSocket 创建代码及其后续逻辑(如接受、处理和 read/write 直接放在 main() 方法中给客户端绝对不是一个好的 OOP 实践。

对于OOP,我们应该从对象的角度来思考,以及这些对象如何相互交互来满足期望的功能。每个对象都封装了不同的职责。

Accepting a client could throw an IOException, so could getting the output stream of said client, so could writeBytes,.. you get my point.

In the context of abstraction, how do I best go about these exceptions? Do I write a throws IOException next to the method, making the higher-up code try...catch the SyteTCPServer.Start?

我可以强调几个最佳实践

  • 不要忘记用所有必要的信息记录异常,因为它是以后调试和其他改进不可或缺的工具。
  • 不要在流程中捕获多个 try/catch 的相同异常。
  • 捕获异常后不要吞下并重新抛出它们,除非确实不需要。
  • 最后,如何处理异常还要看是不是可恢复异常。如果不可恢复记录异常,清理资源。如果可恢复记录异常并按照步骤从异常中恢复。这里最好的指南是 API.
  • 的 javadoc

Also, should the whole server stop must any exceptions occur, or do I "hide" the fact that one of the clients did not connect properly? Or perhaps, should I fire an event when a client failed to connect?

随着您继续执行代码,我相信您会发现更多 类,其中之一可能是在单独的线程中处理每个客户端连接请求,比如 ClientConnectionHandler对象(线程),以便其他客户端不被阻塞。因此每个客户端连接都将成为一个独立的执行线程。

采用这种设计,如果异常是特定于该客户端连接的,则无需关闭整个服务器。只需终止该客户端线程并清理资源就足够了。