2012-01-06 2 views
1

Я пытаюсь создать простой сетевой клиент. Клиент должен иметь возможность записывать в очередь (буфер), а второй поток должен брать этот буфер и записывать его на сервер.Попытка заполнить буфер, пока буфер записывается на канал

Я попробовал это с java.nio и создал Thread со статическими ByteBuffer. Этот ByteBuffer используется в while(true) из Thread для записи в канал.

В моем основном цикле в какой-то момент я помещаю несколько байтов в статический буфер с помощью метода put().

В режиме отладки я приостановил поток записи канала, а затем заполнил буфер через мой основной программный цикл (просто нажал «A» для записи в буфер).

После трех или четырех нажатий кнопки я снова начал запись канала, и все получилось просто отлично.

Но когда я пробую программу просто нормально, я получаю ошибку переполнения буфера в потоке основного цикла. Я считаю, что моя программа пытается помещать данные в буфер, в то время как буфер обращается к моей канальной записи. Я попытался использовать синхронизированное ключевое слово вокруг обеих частей в обоих потоках, но это не помогло.

Основной цикл:

[...] 
    if(Gdx.app.getInput().isKeyPressed(Input.Keys.A) && (now.getTime() - lastPush.getTime()) > 1000) 
     { 
      lastPush = now; 
      //synchronized (PacketReader.writeBuffer) 
      //{ 
       PacketReader.writeBuffer.put(("KeKe").getBytes()); 
      //} 
} 
[...] 

Моя тема под названием "PacketReader" (ну это на самом деле чтение и письмо):

class PacketReader implements Runnable 
{ 
public static ByteBuffer writeBuffer = ByteBuffer.allocate(1024); 
[...] 
public void run() 
{ 
    while (true) { 
[...] 
     if (selKey.isValid() && selKey.isWritable()) 
     { 
      SocketChannel sChannel = (SocketChannel)selKey.channel(); 

      //synchronized (PacketReader.writeBuffer) 
      //{ 
       if(PacketReader.writeBuffer.hasRemaining()) 
       { 
        PacketReader.writeBuffer.flip(); 
        int numBytesWritten = sChannel.write(PacketReader.writeBuffer); 
        PacketReader.writeBuffer.flip(); 
       } 
      //} 
     } 
[...] 

Любая идея, как создать такой буферной системы записи? Я думаю, что это обычная проблема, но я не знаю, что искать. Все учебники NIO, похоже, считают, что буфер заполнен в петле канала.

В конце концов, я пытаюсь создать программу, в которой сетевой компонент запускался один раз и внутри моей программы. Я просто хотел использовать некоторый статический метод отправки для отправки пакетов, не задумываясь о обработке очереди или ожидании очереди ,

Может быть, где-то учебник? Большинство игр должны использовать аналогичную концепцию, но я не мог найти никаких простых Java-игр с открытым исходным кодом с реализацией NIO (я буду использовать его для Android, поэтому я пробую его без рамки)

ответ

1

Не прямой ответ на ваш вопрос, но вы должны рассмотреть возможность использования существующей инфраструктуры NIO, чтобы сделать это проще. Netty и Grizzly - популярные примеры. Я бы лично использовал Netty вместо того, чтобы писать собственный сервер с нуля, используя NIO.

Возможно, вы также можете посмотреть, как Netty обрабатывает чтение/запись в буферы, поскольку я предполагаю, что они оптимизировали их реализацию.

+0

Благодарим за предложение. Я думал о системах LightWight, таких как Kryonet или Pyronet, но поскольку я использую это на Android, я думал, что прямое программирование будет лучше для производительности. С другой стороны, я хотел научиться NIO, так почему бы не мучить себя? : D –

2

Возможно, вы захотите сохранить буфер (скажем, ConcurrentLinkedQueue) буферов для записи вместо того, чтобы вставлять в тот же буфер, который вы отправляете на канал.

Чтобы епдиеее что-то для отправки:

ByteBuffer buff = /* get buffer to write in */; 
buff.put("KeKe".getBytes()); 
queue.add(buff); 

Затем в выберите петлю, когда канал доступен для записи:

for(ByteBuffer buff = queue.poll(); buff != null; buff = queue.poll()) { 
    sChannel.write(buff); 
    /* maybe recycle buff */ 
} 

Вы также должны установить/удалить проценты записи на канале в зависимости от того, пуста ли очередь или нет.

+0

Спасибо Рассел :) Я использовал ваш пример для своего приложения. У меня все еще есть вопросы относительно вашего решения: не означает ли это, что я создаю много буферов (для каждой отправки пакетов?)? Может ли это привести к проблемам с реестром на устройствах Android? В качестве альтернативы я думал о том, чтобы поместить байт [] Массивы в очередь и создать массивы байтов через ByteArrayOutputStream. Итак, вопрос здесь: ByteArrayOutputStream vs ByteBuffer, что лучше для устройств Android? –

0

Весь смысл NIO в том, что вам не нужны отдельные потоки. Нить, выполняющая заполнение, также должна писать.

+0

Извините, я не понимаю этого полностью. Я делаю это для маленькой игры на Android - по вашему мнению, я должен делать все заполнение и запись материала в рендеринге? Как раз перед графикой и логикой? Но что, если отправка или чтение из сокета занимает много времени? Мне пришлось бы ждать с рендерингом, поэтому игра будет медленной ... И что, если мой рендеринг займет слишком много времени, разве у моего буфера чтения не возникнет риск переполнения? Или, что еще хуже, я потерял бы пакеты? –

+0

@ Thom- я ничего не говорил о нити визуализации. – EJP

Смежные вопросы