Java网络编程。关于套接字的问题

3
我有一个服务器,有两个客户端通过TCP连接到它。 这些客户端不断向服务器发送信息。 这是一个代理服务器,它会中继来自客户端的消息。 但是它必须交替地中继消息。也就是说,从客户端A的消息开始,然后是客户端B的消息,接着是客户端A的消息,然后是B的消息,如此循环。 我可以通过检查消息来自哪个客户端并交替中继消息,并忽略同一客户端连续的消息来实现这一点。
然而,如果任何一个客户端断开连接或者没有发送消息,我也不希望服务器被卡住。 如果发生这种情况,代理将会永远等待来自已经断开连接的客户端(或因某种原因没有发送消息的客户端)的消息。在这种情况下,我希望服务器中继来自仅剩连接的客户端的消息。
因此我想知道是否有可能像这样做。如果我得到了来自同一客户端的2条连续消息,我想检查另一个客户端是否准备好向我发送消息。 我的问题是,是否可以从另一个客户端的套接字中检查是否有已缓冲且准备好发送的消息。 在这种情况下,可以忽略来自同一客户端的连续消息,而是首先发送检查过的另一个客户端的消息。
这可行吗?希望我已经清楚地提出了问题。
谢谢。
8个回答

2
我理解您的问题如下:
您有:
1个服务器
2个客户端
您的服务器会从客户端1和2接收消息并将它们转发。
这些客户端是不同的生产者,也就意味着它们发送的消息可能是不同的。您想要的是从客户端发送的消息可以交替地从您的服务器中发送出去,但如果一个客户端掉线了,就不需要等待。
在这种情况下,我建议在您的服务器上设置两个队列(client1queue和client2queue)。
您需要在两个不同的线程中读取sockets,当一条消息进来时,将其添加到相应的队列中。 client1Socket -> client1queue client2Socket -> client2queue
然后,在第三个线程中,让服务器按照轮流的方式从client1queue和client2queue中提取消息并转发。
为了解决“不等待”队列为空的问题,只需跳过该队列的“轮次”。这确保了以最快的速率发送所有消息,同时仍然能够传递所有消息。缺点是仅在准备发送消息时才轮流。当然,如果系统无论客户端状态如何都应正常工作,那么您可以等待X个数量来查看其他队列是否有消息,但我不认为这样做有必要。

1
问题在于:要测试客户端是否断开连接,必须在套接字上进行读取操作,然而,Java的Socket类不允许在套接字上进行异步读取。因此,如果您在没有设置超时的情况下进行读取,并且没有任何内容被发送,应用程序将一直等待读取内容,从而使进程无法继续执行。
因此,在实例化套接字之后,您需要使用:Socket.setSoTimeout(int)来为其设置一个“等待”超时时间。每次尝试读取时都会超时,如果读取返回-1,则表示客户端已断开连接。
    Socket clientSocket=theServerSocket.accept();
    clientSocket.setSoTimeout(1);
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

while(serverIsRunning){
       try{
           if(bufferedReader.read()==-1){
                  logger.info("The server has disconnected!");
                  //Do whatever clean up you need, ect
           }
       }
       catch (SocketTimeoutException e){
           //the server is still running, yay?!
       }
}

1
你可以使用setSoTimeout()方法在套接字上设置读取超时时间,比如一秒钟或者你想等待每个客户端的时间。这样,当你从客户端A获取第二条消息时,你可以直接从客户端B的套接字中读取(),如果你收到SocketTimeoutException异常,则可以处理A的消息。
但是你需要修改你当前的代码,以捕获任何可能出现的SocketTimeoutExceptions,每当你从套接字中读取数据时。

0
由于我将在下面解释的原因,我只想将有趣的事件视为消息是否已经到达您的服务器或消息的一部分已经到达您的服务器。所以:
  • 对于每个客户端,都要有一个线程读取数据
  • 还有另一个线程执行消息汇总
  • 当以下情况发生时,请让客户线程向消息汇总线程发出信号:从客户端接收到新消息的第一部分时;从客户端接收到完整的消息时(此时,将实际消息传递给汇总线程)
  • 然后,如果您的汇总线程没有收到“消息的第一部分已到达”的最初信号,就将其视为“客户端尚未准备好发送消息”。

正如其他帖子中提到的那样,您可能需要在套接字上设置超时,尽管使用这种线程模型,我认为它并不是必要的 - 这实际上取决于您想要施加的策略。

你提出的另一个建议是询问客户端,这种方法可能可行,但这可能不是你真正想要的解决方案。你打开了另一个与客户端交流的渠道来询问“你刚刚发送了一些数据吗?”但如果客户端已经发送了数据,而你还没有收到,那么通常情况下就意味着出现了问题,而且不清楚这种知识是否会对你有所帮助。或者客户端可能会回答“没有”,然后在那个时刻发送数据。而且,与客户端的另一个渠道会起作用吗?如果客户端说它已经发送了一些东西,你怎么知道它是即将到达还是已经“丢失”并永远不会到达...?


0
我考虑的做法是: 如果我从客户端A收到2个连续的消息,我将获取连接到客户端B的套接字的inputStream。然后调用inputStream上的available()方法,查看是否有可读取的字节。 如果有可读取的字节,则从客户端B的套接字中读取。否则,转发来自客户端A的消息(第二个连续的消息)。
对此有什么意见吗?

所以,客户端A和客户端B应该同时发送相同的消息?您只是想将其中一个作为备份服务器? - Alex
实际上,它们正在发送不同的消息,我希望服务器交替中继消息。但是,如果一个客户端由于某种原因延迟发送消息,我不希望其他客户端的消息被阻止中继。 - Anand

0
如果客户端A在客户端B发送消息之前发送第二条消息,您会打印来自客户端A的第二条消息吗?在这种情况下,它根本不是交替的,您只是按照它们到达的顺序打印任何消息。或者,您想等一会儿看看是否有来自客户端B的消息?
我认为无论哪种方式,您都需要为每个客户端创建一个单独的线程,除了“主”线程外运行。每个客户端线程不断尝试从其各自的客户端读取消息,直到接收到消息为止。当套接字线程接收到消息时,它将其放入一个同步队列中,其中对于所有三个线程都有一个实例。此put操作将阻塞,直到主线程在同步队列上调用take
主线程可以简单地循环并在同步队列上调用take。这将阻塞,直到其中一个客户端线程调用put
如果您想要交替的消息,则可以使用两个同步队列,每个客户端一个。您需要在主循环中引入一些延迟,以便在转发消息之前给两个客户端一个写入消息的机会。

0
我会查找available()方法的javadoc。它并不是你想象中的那样。

0

你有两个不同的问题:

1)从多个客户端读取消息,而不会阻塞。

2)以公平的方式将消息写入输出。

如果您为每个客户端套接字使用单独的线程,则可以解决第一个问题。如果您有许多客户端,则可以考虑使用SocketChannel和Selector使用非阻塞io。

请记住,TCP / IP流可能会在每个读取操作中传递多个或仅部分消息,这取决于网络和套接字选项,因此您必须注意。

第二个问题可以通过为每个客户端保留队列或使用优先级队列并根据客户端活动为每个消息附加优先级来解决。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接