简体   繁体   English

Java ServerSocketChannel SocketChannel(回调)

[英]Java ServerSocketChannel SocketChannel (Callback)

I am trying to learn Java. 我正在努力学习Java。 I would like to implement a simple networked connect 4 game as well as a chat feature. 我想实现一个简单的网络连接4游戏以及聊天功能。

I want my network logic to be non blocking so after much study I found that SocketChannel is what I am after regrading my needs. 我希望我的网络逻辑能够无阻塞,所以经过多次研究后我发现SocketChannel是我重新考虑我需求之后的事情。

What has not made sense still is the lack of CallBack functions in SocketChannels.. Like one finds in C#. 没有意义的是SocketChannels中缺少CallBack函数。就像在C#中找到的一样。

My query for this time is: How do I deliver the data received to the Chat or Game form (JFrame)? 我对这个时间的查询是:如何将收到的数据传递给聊天或游戏表单(JFrame)?

Some guidance is most welcome. 一些指导是最受欢迎的。

You need to use a Selector. 您需要使用选择器。 First create a Selector to receive the events: 首先创建一个Selector来接收事件:

Selector selector = Selector.open()

Then you need to register the ServerSocketChannel with the selector: 然后,您需要使用选择器注册ServerSocketChannel:

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

Then you need to use the Selector to process events as they come in (you can think of this as the "callback" part of the process: 然后你需要使用Selector来处理事件(你可以将其视为流程的“回调”部分:

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

The meat will be in doAccept(), doRead(), and doWrite(). 肉将在doAccept(),doRead()和doWrite()中。 For an accept key the selection key will contain the information to create the new Socket. 对于接受键,选择键将包含用于创建新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);
}

The last line adds some object to the key so that the events received from the selector can be attributed to a connection (for example it might be a player in your game). 最后一行向键添加了一些对象,以便从选择器接收的事件可以归因于连接(例如,它可能是游戏中的玩家)。 So now you can accept new connections and you will just need to read and write. 所以现在你可以接受新的连接,你只需要读写。

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

similarly for write 类似的写作

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

Reading is fairly straight forward, you just create a ByteBuffer and then call the SocketChannels read(ByteBuffer) (or one of its variants) to get the data ready on the channel until its empty. 读取是相当简单的,您只需创建一个ByteBuffer,然后调用SocketChannels读取(ByteBuffer)(或其中一个变体),以便在通道上准备数据,直到它为空。

Writing is a bit trickier as you will usually want to buffer the data to be written until you recieve the write event: 写入有点棘手,因为在收到写入事件之前,通常需要缓冲要写入的数据:

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

Note that you will need appropriate code to manage the buffer in the class in the event it becomes full, or to modify it appropriately in the write pending method if not all of the data in the buffer is written out to the socket, as well as the various exceptions that can be thrown during the process. 请注意,如果不是缓冲区中的所有数据都写入套接字,则需要适当的代码来管理类中的缓冲区(如果它已满),或者在写入暂挂方法中适当地修改缓冲区,以及在此过程中可能抛出的各种异常。 Hope this helps to get you started. 希望这有助于您入门。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM