【问题标题】:Client-Server communication where Server initiates服务器发起的客户端-服务器通信
【发布时间】:2013-02-19 14:56:30
【问题描述】:

我想要这个设置:

  • 托管 TCP 套接字服务器的服务器
  • 多个客户端通过 TCP 连接(保持连接打开)

然后我想从服务器向客户端发起一条消息。我不知道如何做到这一点,并且同时有多个客户端会话。我读过的技术涉及服务器侦听端口,当它接收到来自客户端的通信时,它会启动一个新线程来处理和处理它,然后它会返回侦听端口以获取另一个客户端的下一个请求.
那么,我将如何利用它并向在其中一个线程上运行的客户端发送消息?

如果您有兴趣,我的实际使用场景如下。最终目标就像是远程控制您的文件系统将文件上传到服务器。 - 每个客户端都有一个运行在连接服务器的系统托盘中的 java 后台应用程序 - 服务器托管连接,还托管 RESTFul Web 服务以启动通信 - 移动设备通过 RESTFul Web 服务连接到服务器,以请求有关客户端文件系统的信息。所以它可以向下钻取并找到一个文件,然后单击并将文件上传到服务器。 这里的想法是移动用户需要在离开办公室时使用移动设备将文件从桌面上传到服务器。 (而且这是定制产品,所以不能使用第三方应用程序_

PS:我一直在这里查看简单的客户端-服务器聊天程序:http://way2java.com/networking/chat-program-two-way-communication/

【问题讨论】:

    标签: java sockets client-server


    【解决方案1】:

    您希望让服务器始终监听指定端口。一旦服务器注意到该端口上的传入连接,您应该创建一个新线程来处理该客户端和服务器之间的通信,而主线程继续侦听其他传入连接。这样,您可以将多个客户端连接到一个服务器。像这样:

    private void listen() throws IOException {
        serverSocket = new ServerSocket(port)
        while (GlobalFlags.listening) {
            new ServerThread(serverSocket.accept();
            if (GlobalFlags.exit) {
                serverSocket.close();
                break;
            }
        }
    }
    

    其中 GlobalFlags 是控制监听过程的变量,并不是真正需要的。你可以做一段时间 True,然后一直听下去。

    在我的项目中,我有一个主服务器控制器,它的侦听器在线程中运行。控制器控制 GlobalFlags。我敢肯定,除了使用全局标志之外,还有一种更好的方法来进行线程间通信,但对我来说,这是当时最简单的方法。

    ServerThread 应该一直循环在向客户端发送输出和从客户端接收输入之间切换。像这样:

    ServerThread(Socket socket) {
        super("GameServerThread");
        this.socket = socket;
        try {
            this.socket.setTcpNoDelay(true);
        } catch (SocketException e) {
            // Error handling
        }
        this.terminate = false;
    }
    
    @Override
    public void run() {        
        try {
            out = new PrintWriter(socket.getOutputStream(), true);
            in = new BufferedReader(
                 new InputStreamReader(
                 socket.getInputStream()));
    
            String inputLine, outputLine;
    
            while ((inputLine = in.readLine()) != null) {
                    outputLine = processInput(inputLine);
                    out.println(outputLine);
                    if (terminate) {
                        break;
                    }
                }
            }
            out.close();
            in.close();
            socket.close();
    
        } catch (Exception e) {
            // Error handling, should not use Exception but handle all exceptions by themselves.
    }
    

    在客户端,您有一个线程通过类似的循环运行,从服务器接收输入,然后将输出发送到服务器。

    在本例中,processInput 是用于处理客户端输入的函数。如果您希望服务器发起联系,您可以让服务器在侦听输入之前向输出流发送一些内容,并让客户端先侦听。

    我从我自己的一个项目中提取了这个例子,this.socket.setTcpNoDelay(true) 应该让这个过程更快。参考这里:http://www.rgagnon.com/javadetails/java-0294.html

    "java.net.Socket.setTcpNoDelay() 用于启用/禁用 TCP_NODELAY,它禁用/启用 Nagle 的算法。 Nagle 的算法试图通过最小化发送的段数来节省带宽。当应用程序希望减少网络延迟并提高性能时,它们可以禁用 Nagle 算法(即启用 TCP_NODELAY)。数据将更早发送,但会增加带宽消耗。 Nagle 的算法在 RFC 896 中有描述。

    您可以使用 java.net.Socket.getTcpNoDelay()" 获得当前的“TCP_NODELAY”设置


    因此,要向特定客户端发送消息,您可以在创建时将所有线程放在 ArrayList 中,这样您就可以跟踪所有当前连接的客户端。您可以让 processInput 方法停止并轮询队列/变量,直到另一个类将要发送的消息放入队列/变量中。所以如何获得类的句柄取决于你对 processInput 的实现。您可以给每个线程一个 ID(这是我在我的项目中所做的),并且可能让 processInput 方法在 index=ID 处轮询一个 ArrayList。然后要将输出发送到客户端,您必须将变量设置为 index=ID。

    这种方法对我个人来说似乎有点笨拙,但我不确定我该怎么做。您可能会使用队列并让 processInput 将输入写入其队列,然后等待另一个类读取它并将其响应放入队列中。但我个人从未在 java 中使用过队列,所以你应该自己阅读。

    【讨论】:

    • 谢谢。我一直跟着你,直到服务器控制器部分。这就是我感到困惑的地方。因此,一旦您让这些客户端通过自己的线程与服务器进行通信,您如何从外部 java 类中获取句柄以向该客户端发送消息。所以假设我有客户端 1 在线程 XYZ 中运行。然后在另一个处理 RESTful web 服务的 java 类中,想要在线程 XYZ 上向客户端 1 发送消息。它是如何将句柄放入该线程的?
    • 那么服务器控制器与您的问题无关。您真正需要使其正常工作的只是侦听器、服务器线程和客户端线程。向客户端发送消息的方式将通过您对 processInput 的实现来确定。我个人在另一个类中有一个解码功能,它与服务器后端的其余部分通信,以根据客户端的输入确定服务器的输出。我将编辑帖子,并在您编辑时提供更多信息。
    • 好的,我已经添加了更多信息,我会给你一个示例实现,但我自己不确定什么是最好的方法。不过,您可能可以自己想出一种使用队列的方法。
    【解决方案2】:

    据我所知 1) 服务器托管 TCP 套接字服务器 -- 可能 2) 多个客户端通过 TCP 连接——可能 3)然后我想从服务器向客户端发起一条消息——不可能。客户端必须启动连接创建,然后服务器可能能够向您发送数据包。示例:您需要在浏览器上打开 Facebook 网站,Facebook 服务器无法自行决定将其页面发送到您的 PC,因为您的 PC 没有静态 IP 地址,而且如果 Facebook 假设编写代码来启动与您的 PC 的连接,那么它就像你的电脑是服务器,Facebook 网站/服务器充当客户端一样好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-08
      • 2012-12-26
      • 2011-09-05
      • 2012-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多