javaNIO中的缓冲区写入/发送消息问题

时间:2010-11-26 20:46:48

标签: java sockets network-programming buffer nio

我的问题是关于JAVANIO客户端服务器消息传递,我不确定在技术上定义问题但是: 似乎缓冲区正在缓存数据,当它完成时,它会一起发送所有这些令人不安的逻辑:

private void sendCreate(String line,SocketChannel from)
 /* A new client wishes to join the world.

      This requires the client to find out about the existing
      clients, and to add itself to the other clients' worlds.

      Message format: create name xPosn zPosn

      Store the user's name, extracted from the "create" message
  */
 { StringTokenizer st = new StringTokenizer(line);
 st.nextToken();                  // skip 'create' word
 userName = st.nextToken();
 String xPosn = st.nextToken();   // don't parse
 String zPosn = st.nextToken();   // don't parse

 // request details from other clients
 sendBroadcastMessage( "wantDetails " + achannel.socket().getInetAddress() + " " + port,from);

 // tell other clients about the new one
 sendBroadcastMessage( "create " + userName + " "+xPosn+" "+zPosn,from);

 } // end of sendCreate()

负责从服务器广播消息的方法:

private void sendBroadcastMessage(String mesg, SocketChannel from) {
  prepWriteBuffer(mesg);
  Iterator i = clients.iterator();
  while (i.hasNext()) {
   SocketChannel channel = (SocketChannel) i.next();
   if (channel != from)
    channelWrite(channel, writeBuffer);
  }
 }

我假设这应该发送第一条消息,即sendBroadcastMessage(“wantDetails”+ achannel.socket()。getInetAddress()+“”+ port,from);但事实并非如此,它似乎在等待其他方法调用,即sendBroadcastMessage(“create”+ userName +“”+ xPosn +“”+ zPosn,from);然后将这两条消息作为影响应用程序逻辑的消息发送。理想情况下,它应该或者应该在第一次调用sendBroadcastMessage之后发送第一条消息,然后当客户端重新启动第一条消息时,应该处理其他调用。

这些是在sendBroadcastMessage()中使用的方法:

private void prepWriteBuffer(String mesg) {
  // fills the buffer from the given string
  // and prepares it for a channel write
  writeBuffer.clear();
  writeBuffer.put(mesg.getBytes());
  writeBuffer.putChar('\n');
  writeBuffer.flip();
 }

 private void channelWrite(SocketChannel channel, ByteBuffer writeBuffer) {
  long nbytes = 0;
  long toWrite = writeBuffer.remaining();

  // loop on the channel.write() call since it will not necessarily
  // write all bytes in one shot
  try {
    nbytes += channel.write(writeBuffer);

  } catch (ClosedChannelException cce) {
   cce.printStackTrace();
  } catch (Exception e) {
   e.printStackTrace();
  }
  // get ready for another write if needed
  writeBuffer.rewind();
 }

请提出一些解决方案。

感谢,

jibby lala

编辑: 怎么样,我从一些聊天应用程序中得到了这个补丁:

private void prepWriteBuffer(String mesg) {
        // fills the buffer from the given string
        // and prepares it for a channel write
        writeBuffer.clear();
        writeBuffer.put(mesg.getBytes());
        writeBuffer.putChar('\n');
        writeBuffer.flip();
    }


// called needs to remove the channel if it fails, otherwise it will fail forever.
        private void channelWrite(SocketChannel channel, ByteBuffer writeBuffer)  {    
            long nbytes = 0;
            long toWrite = writeBuffer.remaining();
            // loop on the channel.write() call since it will not necessarily
            // write all bytes in one shot
            try {
            while (nbytes != toWrite) {
                nbytes += channel.write(writeBuffer);

                try {
                    Thread.sleep(CHANNEL_WRITE_SLEEP);
                } catch (InterruptedException e) {
                }
            }
        } catch (ClosedChannelException cce) {
        } catch (Exception e) {
        }
        // get ready for another write if needed
        writeBuffer.rewind();
    }

1 个答案:

答案 0 :(得分:0)

也许你打算

 while(writeBuffer.remaining()>0)
      channel.write(writeBuffer);

但是,问题似乎是您假设消息之间存在某种类型的魔术标记。但是,不存在这样的分隔符。流只是一个字节流。当您以阻塞模式读取时,您将获得至少一个字节,您可能会获得更多可能跨越多次写入但除非您在流中包含您期望消息开始和结束的内容,否则您将无法知道。

一种简单的方法是在消息开头写入消息的长度,并读取最多单个消息,直到获得所有消息。像。的东西。

private void prepWriteBuffer(String mesg) {    
  // fills the buffer from the given string    
  // and prepares it for a channel write    
  writeBuffer.clear();
  byte[] bytes = mesg.getBytes());
  writeBuffer.putInt(bytes.length);    
  writeBuffer.put(bytes);
  writeBuffer.flip();    
 } 


// called needs to remove the channel if it fails, otherwise it will fail forever.
private void channelWrite(SocketChannel channel, ByteBuffer writeBuffer) throws IOException {    
 while(writeBuffer.remaining()>0)
      channel.write(writeBuffer);
 writeBuffer.rewind();
}