顺序通道写入在Java.NIO中发送损坏的数据

时间:2013-05-08 18:00:32

标签: java serialization nio

我有一个使用非阻塞套接字的服务器,nio。服务器在一个单独的线程中工作,还有另一个名为Game的线程。游戏线程持有服务器对象并使用server.sendMessage,服务器线程只读取数据。当我在一个while循环中顺序调用sendMessage两次2个数据包时,片刻之后我在客户端得到“java.io.StreamCorruptedException:invalid stream header: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);
}

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

在写之前需要flip(),之后需要compact(),你需要停止假设一个write()写入整个缓冲区。它返回一个值的原因。您需要循环,或者如果您处于非阻塞模式,则需要按如下方式进行:

  1. 写入。
  2. 如果写入未完全完成,请注册OP_WRITE的通道并返回选择循环。
  3. 当频道变为可写时,再次尝试写入,如果仍然没有完成,只需保持循环。
  4. 否则取消注册OP_WRITE。