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

发布于 2024-10-04 10:33:53 字数 3607 浏览 2 评论 0原文

我的问题是关于 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();
 }

请建议一些解决方案。

谢谢,

吉比拉拉

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

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();
    }

My problem is concerning JAVANIO client server message passing,i m unsure about defining the problem technically but:
it seems that buffer is caching the data and when it is done then it is sending all together which is disturbing logic:

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()

method responsible for broadcasting messages from server:

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);
  }
 }

i m assuming that this should send the first message i.e sendBroadcastMessage( "wantDetails " + achannel.socket().getInetAddress() + " " + port,from); but this is not,it seems that it is waiting for other method call i.e sendBroadcastMessage( "create " + userName + " "+xPosn+" "+zPosn,from);and then sending both message as one message which is affecting application logic.ideally it should or it should send the first message after first call to sendBroadcastMessage and then when client recive the first then other call should be processed.

these are methods which are using in 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();
 }

please suggest some solution.

thanks,

jibby lala

Edit :
what about this,i got this patch from some chat app:

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();
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

超可爱的懒熊 2024-10-11 10:33:53

也许您是有意的

 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();
}

perhaps you intended

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

However, you problem appears to be that you assume there is some type of magic marker between messages. However, no such divider exists. A stream is just a stream of bytes. When you read in a blocking mode you will get atleast one byte, you may get more this may span what was multiple writes but unless you include in the stream where you expect a message to start and end, you will have no way of knowing.

A simple approach is to write the length of the message at the start of the message and read at most single message until you get all of it. Something like.

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();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文