我正在使用SocketChannel
和Selector
来编写服务器。服务器作业是允许客户端连接,从客户端读取消息以及向客户端写入消息。
我在区分从客户端发送的消息和触发读取指令的其他数据时遇到一些困难。
离。当客户端连接时,我注意到执行了读取指令,而客户端没有发送任何数据。这很重要,因为在服务器从客户端读取消息后,它必须将该消息添加到消息队列。这些消息将由外部应用程序从队列中删除和处理。 问题是,每次读取时,这些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();
}
}
这是read函数,如果key.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
来测试它。
这个问题可以通过简单地检查每个传入消息的大小并忽略那些小的消息来解决。但是,如果发生碎片,这似乎会适得其反。
编辑: 客户端示例代码:
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甚至提示要将消息发送到服务器之前,服务器会收到一条消息,该消息是日语或一系列问号(?? ???)。
答案 0 :(得分:1)
以及其他触发读取指令的数据。
没有“其他数据”。仅来自客户端的数据。
'new ObjectOutputStream()'写入以0xAC开头的流标头。这就是你正在阅读的内容。
当对等方使用对象流时,您应该使用阻塞套接字,线程和对象输入流。不是NIO。
根据我上面的评论,应删除'this.client'成员。它假设您只有一个客户端。
关闭频道取消密钥。你很少需要key.cancel()。我从不使用它。