Проблема с буферизацией / отправкой сообщений в javaNIO - PullRequest
0 голосов
/ 26 ноября 2010

Моя проблема связана с передачей сообщений клиентскому серверу 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 () + ""+ порт, с);но это не так, кажется, что он ожидает вызова другого метода, т.е. 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 голосов
/ 26 ноября 2010

возможно, вы намеревались

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