Java ServerSocketChannel SocketChannel(回调)

发布于 2024-08-29 22:20:03 字数 236 浏览 6 评论 0原文

我正在努力学习Java。我想实现一个简单的联网连接 4 游戏以及聊天功能。

我希望我的网络逻辑是非阻塞的,所以经过大量研究,我发现 SocketChannel 就是我重新调整我的需求后的样子。

仍然没有意义的是 SocketChannel 中缺少 CallBack 函数。就像 C# 中的情况一样。

这次我的疑问是:如何将收到的数据传递到聊天或游戏表单(JFrame)?

一些指导是最受欢迎的。

I am trying to learn Java. I would like to implement a simple networked connect 4 game as well as a chat feature.

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.

What has not made sense still is the lack of CallBack functions in SocketChannels.. Like one finds in C#.

My query for this time is: How do I deliver the data received to the Chat or Game form (JFrame)?

Some guidance is most welcome.

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

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

发布评论

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

评论(1

软糯酥胸 2024-09-05 22:20:03

您需要使用选择器。首先创建一个选择器来接收事件:

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

请注意,如果缓冲区已满,您将需要适当的代码来管理类中的缓冲区,或者如果缓冲区中的所有数据未写出到套接字,则需要在写入挂起方法中对其进行适当修改,以及过程中可能抛出的各种异常。希望这有助于您入门。

You need to use a Selector. First create a Selector to receive the events:

Selector selector = Selector.open()

Then you need to register the ServerSocketChannel with the selector:

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:

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(). For an accept key the selection key will contain the information to create the new 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.

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.

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