UDP的Nio实现

UDP的Nio实现

net 包下DatagramSocket / DatagramPacket 实现的udp我倒是蛮熟悉,一直没去学习nio里面提供的udp实现。我一直觉得udp不存在连接,根本没必要使用selector轮询。但是Java确实提供了这样的实现。今天经过一番学习,才弄明白。原来它的意义是可以通过一个Selector(一个线程)来轮询多个udp的服务。

Server

import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

public class NioUdpServer {

    public static void main(String[] args) throws Exception {

        Selector selector = Selector.open();
        DatagramChannel datagramChannel = DatagramChannel.open();

        try{
            datagramChannel.bind(new InetSocketAddress(1024));

            // 非阻塞模式
            datagramChannel.configureBlocking(false);
            // 一些socket的选项
            datagramChannel.setOption(StandardSocketOptions.SO_REUSEADDR, Boolean.TRUE);

            // 只关心读事件
            datagramChannel.register(selector, SelectionKey.OP_READ);

            // 预定义缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(65507);

            while (selector.select() > 0){
                Iterator<SelectionKey> selectionKeyIterator = selector.selectedKeys().iterator();
                while (selectionKeyIterator.hasNext()){// 读事件
                    SelectionKey selectionKey = selectionKeyIterator.next();
                    try{
                        if(selectionKey.isReadable()){

                            DatagramChannel channel = (DatagramChannel) selectionKey.channel();

                            // 客户端地址信息
                            InetSocketAddress inetSocketAddress = (InetSocketAddress) channel.receive(byteBuffer);
                            String hostName = inetSocketAddress.getHostName();
                            int port = inetSocketAddress.getPort();
                            String hostString = inetSocketAddress.getHostString();
                            System.out.println(hostName + "[" + hostString +"]:" + port);

                            // 数据信息
                            byteBuffer.flip();
                            byte[] bytes = new byte[byteBuffer.remaining()];
                            byteBuffer.get(bytes);
                            byteBuffer.clear();
                            System.out.println(new String(bytes, StandardCharsets.UTF_8));

                        }else if(selectionKey.isWritable()){
                        }else if(selectionKey.isAcceptable()){
                        }else if(selectionKey.isConnectable()){
                        }else{ }
                    }finally {
                        selectionKeyIterator.remove();
                    }
                }
            }

        }finally {
            selector.close();
            datagramChannel.close();
        }
    }
}

Client

import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.charset.StandardCharsets;

public class NioClient {
    public static void main(String[] args) throws Exception{

        DatagramChannel datagramChannel = DatagramChannel.open();
        datagramChannel.setOption(StandardSocketOptions.SO_REUSEADDR, Boolean.TRUE);
        // 绑定发送端口
        datagramChannel.bind(new InetSocketAddress(7725));

        ByteBuffer byteBuffer = ByteBuffer.allocate(65507);
        byteBuffer.put("SpringBoot中文社区".getBytes(StandardCharsets.UTF_8));
        byteBuffer.flip();

        // 返回已经成功发送的字节数量, 可能一次性没发送完毕
        int count = datagramChannel.send(byteBuffer, new InetSocketAddress("127.0.0.1", 1024));

        System.out.println(count);
    }
}

优秀