Java ServerSocketChannel SocketChannel(回调)

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

我希望我的网络逻辑是非阻塞的,所以经过多次研究后我发现SocketChannel是我在重新考虑我的需求之后。

没有意义的是SocketChannels中缺少CallBack函数。就像在C#中找到的一样。

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

一些指导是最受欢迎的。

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

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

请注意,如果不是缓冲区中的所有数据都写入套接字,则需要适当的代码来管理类中的缓冲区(如果它已满),或者在写入暂挂方法中适当地修改缓冲区,以及在此过程中可能抛出的各种exception。 希望这有助于您入门。