Java ServerSocketChannel SocketChannel (обратный вызов) - PullRequest
5 голосов
/ 21 апреля 2010

Я пытаюсь выучить Java. Я хотел бы реализовать простую сетевую игру connect 4, а также функцию чата.

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

Что еще не имело смысла, так это отсутствие функций CallBack в SocketChannels .. Как можно найти в C #.

Мой запрос на это время: Как доставить полученные данные в форму чата или игры (JFrame)?

Некоторые рекомендации приветствуются.

1 Ответ

14 голосов
/ 21 апреля 2010

Вам нужно использовать селектор.Сначала создайте селектор для получения событий:

Selector selector = Selector.open()

Затем вам нужно зарегистрировать ServerSocketChannel с помощью селектора:

SelectionKey acceptKey = server.register(selector, SelectionKey.OP_ACCEPT);

Затем вам нужно использовать селектор для обработки событий по мере ихвойти (вы можете думать об этом как о части «обратного вызова» процесса:

while(true){
  //how many channel keys are available
  int available = selector.select(); 
  //select is blocking, but should only return if available is >0, this is more of a sanity check
  if(available == 0) continue;

  Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
  while(keys.hasNext()){
    SelectionKey key = keys.next();
    keys.remove();
    //someone is trying to connect to the server socket
    if(key.isAcceptable())  doAccept(key); 
    //someone is sending us data
    else if(key.isReadable()) doRead(key); 
    //we are trying to (and can) send data
    else if(key.isWritable()) doWrite(key);
}

Мясо будет в doAccept (), doRead () и doWrite (). Для ключа принятияключ выбора будет содержать информацию для создания нового Socket.

doAccept(SelectionKey key){

//create the new socket
SocketChannel socket = ((ServerSocketChannel)key.channel()).accept(); 
//make it non-blocking as well
socket.configureBlocking(false);

...
//here you would likely have some code to init your game objects / communication protocol, etc. and generate an identifier object (used below).
//and be able to find the socket created above
...

//Since it is non blocking it needs a selector as well, and we register for both read and write events
SelectionKey socketKey = socket.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
// so we can identify the events as they come in
socketKey.attach(someSocketIndentifier);
}

В последней строке добавляется некоторый объект к ключу, чтобы события, полученные от селектора, можно было отнести к соединению (например, это может бытьигрок в вашей игре). Так что теперь вы можете принимать новые подключения, и вам просто нужно будет читать и писать.

doRead(SelectionKey key){
  //here we retrieve the key we attached earlier, so we now what to do / wheer the data is coming from
  MyIdentifierType myIdentifier = (MyIdentifierType)key.attachment();
  //This is then used to get back to the SocketChannel and Read the Data
  myIdentifier.readTheData();
}

аналогично для записи

doWrite(SelectionKey key){
  //here we retrieve the key we attached earlier, so we now what to do / wheer the data is coming from
  MyIdentifierType myIdentifier = (MyIdentifierType)key.attachment();
  //This is then used to get back to the SocketChannel and Read the Data
  myIdentifier.getSocketHandler().writePendingData();
}

Чтение довольно простоевы просто создаете ByteBuffer и затем вызываете чтение SocketChannels (ByteBuffer) (или один из его вариантов), чтобы подготовить данные на канале до тех пор, пока он не будет пустым.

Запись - это биЭто сложнее, так как вы обычно хотите буферизовать данные для записи, пока не получите событие записи:

class MyNetworkClass{
  ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
  SocketChannel commchannel; //from the server accept processing

  ...

  public void write(byte[] data){
    //here the class writeBuffer object is filled with the data
    //but it isn't actually sent over the socket
    ...
  }

  public void writePendingData(){
    //here actually write the data to the socket
    commchannel.write(writeBuffer);
  }
}

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

...