Java中的多路套接字通信

Java中的多路套接字通信,java,sockets,socket.io,Java,Sockets,Socket.io,我正在编写一个服务器程序,可以接受来自多个(但固定)客户端的通信。我想让程序保持单线程。为此,我使用非阻塞套接字迭代每个客户机,但每个客户机的通道都使用阻塞模式。这是我的服务器代码: class server { public static void main(String args[]) throws Exception { ServerSocketChannel channel = ServerSocketChannel.open();

我正在编写一个服务器程序,可以接受来自多个(但固定)客户端的通信。我想让程序保持单线程。为此,我使用非阻塞套接字迭代每个客户机,但每个客户机的通道都使用阻塞模式。这是我的服务器代码:

class server {
    public static void main(String args[])
         throws Exception {

        ServerSocketChannel channel = ServerSocketChannel.open();
        channel.configureBlocking(false);
        channel.socket().bind(new java.net.InetSocketAddress("localhost", 8005));
        System.out.println("Server attivo porta 8005");
        Selector selector = Selector.open();
        channel.register(selector, SelectionKey.OP_ACCEPT);

        for(;;) {
          selector.select();
          Set keys = selector.selectedKeys();
          Iterator i = keys.iterator();

          while(i.hasNext()) {
            SelectionKey key = (SelectionKey) i.next();

            i.remove();

            if (key.isAcceptable()) {
              SocketChannel client = channel.accept();
              client.configureBlocking(true);

              ObjectInputStream ois = new ObjectInputStream(
                    client.socket().getInputStream());
              String s = (String)ois.readObject();
              System.out.println(s);
            }
          }
        }
    }
}
客户端使用简单的阻塞I/O,如下所示:

class client {
    public static void main(String args[]) throws Exception {
        SocketChannel channel = SocketChannel.open();

        channel.configureBlocking(true);

        channel.connect(new java.net.InetSocketAddress("localhost", 8005));

        ObjectOutputStream oos = new ObjectOutputStream
              (channel.socket().getOutputStream());

        for (int i = 0; i < 100; i++) {
             oos.writeObject(new String("Hello " + i));
             System.out.println(i);
         }
    }
}
类客户端{
公共静态void main(字符串args[])引发异常{
SocketChannel通道=SocketChannel.open();
channel.configureBlocking(真);
connect(新的java.net.InetSocketAddress(“localhost”,8005));
ObjectOutputStream oos=新的ObjectOutputStream
(channel.socket().getOutputStream());
对于(int i=0;i<100;i++){
oos.writeObject(新字符串(“Hello”+i));
系统输出打印LN(i);
}
}
}
问题是,尽管客户端希望写入100次,但服务器只读取了一次消息。服务器和客户端都没有给出任何异常,但我只从服务器获得输出“Hello 0”。我在这里做什么有什么问题吗?如果是,我有什么选择

谢谢


更新:关闭服务器循环中的ObjectInputStream会导致客户端出现BrokenPipeException(服务器的行为方式与此相同)。

问题在于服务器没有等待客户端发送其所有数据。在客户机-服务器程序中,您需要做的是在两者之间建立一个明确的协议,以便它们在传输/接收数据时保持同步。这通常是通过发送指定符号或在完成连接时关闭连接来向任何一方发送结束传输的信号来完成的。

问题在于您只是在用
键检查新连接。isAcceptable()
。您还需要使用
键.isReadable()
检查读取。您只能从
键执行连接设置。isAcceptable()


请参见服务器端的

,您永远不会关闭
ObjectOutputStream
。您需要阅读直到获得
EOF
或在获得对象并继续循环后关闭它。@Baldy您是指ObjectInputStream吗?在服务器端,没有输出流,只有输入流。是的,
ObjectInputStream
。请看下面我的答案。@Baldy请检查更新。在这种情况下,它没有帮助。感谢您查看。但是在每次读取后关闭服务器中的ObjectOutputStream会在随后的写入尝试中导致BrokenPipeException。请检查更新--我已尝试关闭输出流,但它会导致管道中断异常。非常感谢您的关注。由于客户端是这里编写的人,因此客户端在完成编写后应关闭连接,这将确保服务器按预期进行,但在客户端中添加shutdownOutput也没有帮助。你能说得更具体一点吗?我从来没有得到过key.isReadable()是真的。这可能是因为客户端SocketChannel被设置为阻塞模式。此外,由于它是一个阻塞读取,我理解即使我只检查key.isAcceptable(),服务器也应该阻塞等待数据进入。您需要为读取设置取消阻塞模式。另外,请查看我发布的链接,因为为了使读取选择器真正工作,您需要在connect上执行其他设置项目。由于您试图用一个线程来完成所有操作,所以阻塞实际上没有意义。我理解您关于阻塞的说法,但在这里我将无法使用ObjectInputStream。这就是为什么我想使读写阻塞,同时启用多路复用读取。这根本不可能吗?理想情况下,您需要某种协议,以便知道何时有完整的数据集要转换。我可能建议您考虑以JSON格式发送数据,并使用非alpha字符作为消息标记。然后,您只需缓冲入站数据,直到获得标记,然后将其传递给JSON解析器以转换为对象。由于可以很容易地在线路上看到原始数据,并且更容易进行版本控制(解析器可以忽略未知字段等),因此您可以获得更容易调试的好处。由于
ObjectWriter
不节省空间,因此不应该看到明显的大小损失。