demoyi 2021-03-14 10:20 采纳率: 0%
浏览 6

为啥ServerSocketChannel用ACCEPT,DatagramChannel不是?

当我在DatagramChannel中使用OP.ACCEPT时,会提示报错

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 写回答

1条回答 默认 最新

  • 你知我知皆知 2024-07-24 02:32
    关注

    以下回答参考 皆我百晓生券券喵儿 等免费微信小程序相关内容作答,并由本人整理回复。

    在Java的DatagramChannel类中,我们不能直接使用register方法来注册一个选择键(SelectionKey)。因为DatagramChannel是基于Selector实现的,而Selector是基于EventLoop组的。这意味着,你必须通过EventLoop组来操作DatagramChannel。

    然而,在SocketChannel中,你可以直接使用register方法来注册一个选择键。这是因为SocketChannel是基于事件循环的,所以可以直接通过事件循环管理SocketChannel的行为。

    因此,如果你想要在一个DatagramChannel上进行监听,并且你想让DatagramChannel接收来自客户端的数据,你需要创建一个新的Selector实例,然后将这个Selector传递给你的DatagramChannel实例。这样,你就可以像在SocketChannel上那样使用register方法了。

    以下是一个简单的例子:

    import java.net.InetSocketAddress;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class DatagramListener {
        private Selector selector;
        private ServerSocketChannel serverSocketChannel;
        private ExecutorService executorService = Executors.newSingleThreadExecutor();
    
        public DatagramListener() throws Exception {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().bind(new InetSocketAddress(0));
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        }
    
        public void start() throws Exception {
            while (true) {
                if (!selector.select()) {
                    continue;
                }
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                for (SelectionKey key : selectedKeys) {
                    handle(key);
                }
            }
        }
    
        private void handle(SelectionKey key) throws Exception {
            if (!key.isAcceptable()) {
                return;
            }
            ServerSocketChannel channel = (ServerSocketChannel) key.channel();
            SocketChannel client = channel.accept();
            executorService.execute(() -> {
                try {
                    String message = new String(client.readAllBytes());
                    System.out.println("Received message: " + message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    
        public static void main(String[] args) throws Exception {
            DatagramListener listener = new DatagramListener();
            listener.start();
        }
    }
    

    在这个例子中,我们在主函数中创建了一个DatagramListener对象,并在它的start方法中启动了它。在start方法中,我们首先获取当前的选择器,然后检查是否有任何选择键被触发。如果有,我们就处理这些选择键。

    如果选择键不是接受模式(即OP_ACCEPT),那么我们就会跳过它。否则,我们就创建一个新的SocketChannel并将其注册到我们的选择器上。然后,我们就在后台执行一个线程,以便我们可以从新创建的SocketChannel中读取数据。

    注意:在实际应用中,你应该为每个新的连接创建一个新的线程,而不是让现有的线程去处理所有的连接。这可以防止过多的并发请求导致内存溢出。

    评论

报告相同问题?