1

我有一个使用非阻塞套接字的服务器 nio。服务器在一个单独的线程中工作,并且还有另一个名为 Game 的线程。游戏线程持有服务器对象并使用server.sendMessage,服务器线程只读取数据。当我在一个while循环中为2个数据包顺序调用sendMessage两次时,过了一会儿我在客户端收到“java.io.StreamCorruptedException:无效流标头:6B6574B4”错误。

服务器代码的一部分:

public void write(SelectionKey channelKey, byte[] buffer) {
    if (buffer != null) {
        int bytesWritten;
        try {
            SocketChannel channel = (SocketChannel) channelKey.channel();
            synchronized (channel) {
                bytesWritten = channel.write(ByteBuffer.wrap(buffer));
            }
            if (bytesWritten == -1) {
                resetKey(channelKey);
                disconnected(channelKey);
            }
        } catch (Exception e) {
            resetKey(channelKey);
            disconnected(channelKey);
        }
    }
}

public void broadcast(byte[] buf, SelectionKey fr) {
    synchronized (clientList) {
        Iterator<SelectionKey> i = clientList.iterator();
        while (i.hasNext()) {
            SelectionKey key = i.next();
            if (fr != key)
                write(key, buf);
        }
    }
}

public synchronized void sendMessage(Packets pk) {
    broadcast(pk.toByteArray(), null);
}
4

2 回答 2

1

我的猜测(根据您包含的少量代码)是您根本没有描述您的消息。即使您分别发送 2 条消息,io 层也可能以各种方式拆分/组合这些消息,以便接收者获得附加到前一条消息的一条消息的一部分。您应该使用某种“消息”协议来向接收者准确指示要消耗多少字节,以便它可以正确解析每个传入消息(例如,首先写入消息字节长度,然后写入消息字节)。

作为旁注,该write()方法不能保证在一次调用中写入所有字节,因此您应该处理返回值并根据需要写入剩余字节。

于 2013-05-08T18:20:03.153 回答
1

您需要flip()在写入之前和compact()之后,并且您需要停止假设一个人write()写入了整个缓冲区。它返回一个值是有原因的。您需要循环,或者如果您处于非阻塞模式,则需要执行以下操作:

  1. 写。
  2. 如果写入没有完全完成,则为 OP_WRITE 注册通道并返回到选择循环。
  3. 当通道变为可写时,再次尝试写入,如果仍未完成,则继续循环。
  4. 否则注销 OP_WRITE。
于 2013-05-09T02:16:20.960 回答