1

我正在使用SocketChannelSelector编写服务器。服务器的工作是允许客户端连接,从客户端读取消息并将消息写入客户端。

我在区分从客户端发送的消息和触发读取指令的其他数据时遇到了一些困难。

前任。当客户端连接时,我注意到执行了读取指令,而客户端没有发送任何数据。这很重要,因为服务器从客户端读取消息后,必须将该消息添加到消息队列中。这些消息将由外部应用程序从队列中删除和处理。问题是,每次读取触发时,这些 UFM(未识别消息)在尝试解码它们时都会破坏外部应用程序。

很抱歉,如果这个问题已经得到解答,我找不到完整的答案。

这是我的接受方法,如果我没记错的话,它会告诉选择器在数据可供读取时通知我们。

private void accept(SelectionKey key) throws IOException {
    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();

    // Accept the connection and make it non-blocking
    SocketChannel socketChannel = serverSocketChannel.accept();
    socketChannel.configureBlocking(false);

    socketChannel.register(this.selector, SelectionKey.OP_READ);
}

这是等待事件的选择器方法。

while (should_run) {
    try {
        //Load all pending operations to key
        while (!operations.isEmpty()) {
            SelectionKey key = client.keyFor(getSelector());
            if (!key.isValid()) {
                operations.remove();
                throw new Exception("Key not valid");
            }
            key.interestOps(operations.remove());
        }

        selector.select();

        Iterator selectedKeys = selector.selectedKeys().iterator();
        while (selectedKeys.hasNext()) {
            SelectionKey key = (SelectionKey) selectedKeys.next();
            //Check if key is valid
            if (!key.isValid()) {
                throw new Exception("Key not valid");
            }

            if (key.isAcceptable()) {
                accept(key);
            } else if (key.isReadable()) {
                read(key);
            } else if (key.isWritable()) {
                write(key);
            }

            selectedKeys.remove();
        }
    } catch (Exception e) {
        System.err.println("Something went wrong: " + e.getMessage());
        e.printStackTrace();
    }
}

这是读取函数,调用 ifkey.isReadable()

private void read(SelectionKey key) {     
    System.out.println("Read fired");

    //Clear the buffer for new Data
    this.readBuffer.clear();

    int count;
    try {
        count = this.client.read(this.readBuffer);
        if (count == -1) {
            throw new IOException("Socket closed");
        }
    } catch (IOException e) {
        key.cancel();
        return;
    }

    //PROBLEM OCCURRING HERE
    this.worker.give(this.readBuffer.array(), count);
}

读取者应该读取消息,并将消息传递给工作线程,在那里消息被解码并发生其他好事。

每次连接新客户端后都会调用 read 方法。计数通常很小,在 4 到 10 之间,当我使用 解码时 new String(data[], "UTF-8"),结果是日语之类的..

count我通过在每次调用 read 时简单地打印来测试这一点。

这个问题可以通过简单地检查每条传入消息的大小并忽略那些很小的消息来解决。但是,如果发生碎片化,这似乎会适得其反。

编辑:客户端示例代码:

Socket socket = new Socket("localhost", 3000);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
while (true) {
    String input = JOptionPane.showInputDialog("Send something to the server");
    if (input == null)
            break;
    oos.writeObject(input);
}

注意:我没有对等方的源代码,但是这个非常简单的示例产生了完全相同的结果。在 MessageDialog 甚至提示向服务器发送消息之前,服务器已收到一条消息,该消息要么是日语,要么是一系列问号 (?? ???)。

4

1 回答 1

1

和其他触发读取指令的数据。

没有“其他数据”。仅来自客户端的数据。

'new ObjectOutputStream()' 写入以 0xAC 开头的流标头。这就是你正在阅读的内容。

由于对等方使用对象流,您应该使用阻塞套接字、线程和对象输入流。不是蔚来。

根据我上面的评论,应该删除“this.client”成员。它假设您只有一个客户。

关闭通道会取消密钥。你很少需要 key.cancel()。我从不使用它。

于 2014-03-31T23:05:49.993 回答