java网络编程-NIO

参考:

https://www.zhihu.com/question/29005375

https://blog.csdn.net/anxpp/article/details/51512200

https://www.cnblogs.com/Evsward/p/nio.html

NIO主要基于Linux系统的IO多路复用

java也是基于操作系统的功能做进一步的封装.下面展示基于java NIO的服务端和客户端实现.

服务端

public class NIOServer {

    private static final int port=8080;

    public static void main(String[] args) {
        ServerHandle serverHandle = new ServerHandle(port);
        new Thread(serverHandle).start();
    }
    
}

//

public class ServerHandle implements Runnable {
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private volatile boolean started;

    public ServerHandle(int port) {

        try {
            //创建选择器
            selector = Selector.open();
            //打开监听channel
            serverSocketChannel = ServerSocketChannel.open();
            //Chanel设为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            //绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(port), 1024);
            //监听客户端连接请求
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("==服务端启动==");
            started = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        //循环遍历selector
        while (started) {
            //没有就绪事件时阻塞,有一个就会唤醒
            try {
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    iterator.remove();
                    //处理一个请求
                    try {
                        handleInput(selectionKey);
                    } catch (Exception e) {
                        if (selectionKey != null) {
                            selectionKey.cancel();
                            if (selectionKey.channel() != null) {
                                selectionKey.channel().close();
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //关闭selector,释放资源
        if (selector != null) {
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    private void handleInput(SelectionKey key) {
        try {
            //判断请求是否有效
            if (key.isValid()) {
                //处理新接入的请求消息
                if (key.isAcceptable()) {
                    ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                    //创建socketChannel实例
                    SocketChannel socketChannel = channel.accept();
                    //设为非阻塞
                    socketChannel.configureBlocking(false);
                    //注册为监听读事件
                    socketChannel.register(selector, SelectionKey.OP_READ);
                }

                //如果可读
                if (key.isReadable()) {
                    SocketChannel sc = (SocketChannel) key.channel();
                    //开辟一个1024的缓冲区
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    //读取流,返回读取的字节数
                    int readBytes = sc.read(buffer);
                    if (readBytes > 0) {
                        //将缓冲区的limit设为0,用于后续对缓冲区的读取
                        buffer.flip();
                        //根据缓冲区的可读字节创建字节数组
                        byte[] bytes = new byte[buffer.remaining()];
                        //将缓冲区内容复制到新建数组中
                        buffer.get(bytes);
                        //将收到的消息打印到屏幕
                        String s = new String(bytes, "UTF-8");
                        System.out.println("server receive:" + s);
                        //应答消息
                        doWrite(sc, "hello," + s);
                    } else if (readBytes < 0) {
                        key.cancel();
                        sc.close();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //向channel写消息
    private void doWrite(SocketChannel channel, String response) throws IOException {
        //将消息转换为字节数组
        byte[] bytes = response.getBytes();
        //根据消息大小创建bytebuffer
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        //将字节数组复制到缓冲区
        writeBuffer.put(bytes);
        writeBuffer.flip();
        //发送缓冲区
        channel.write(writeBuffer);
    }

    public void stop() {
        started = false;
    }


}

客户端实现

public class NIOClient {


    private static final int port=8080;
    private static final String host="127.0.0.1";

    public static void main(String[] args) throws IOException {
        ClientHandle clientHandle = new ClientHandle(host,port);
        new Thread(clientHandle).start();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()){
            clientHandle.sendMsg(scanner.nextLine());
        }
    }

}

客户端handle

public class ClientHandle implements Runnable {
    private Selector selector;
    private SocketChannel socketChannel;
    private String host;
    private int port;
    private volatile boolean started;

    public ClientHandle(String host, int port) {
        this.host = host;
        this.port = port;
        try {
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            started = true;
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    @Override
    public void run() {

        System.out.println("==客户端启动==");

        try {
            if (socketChannel.connect(new InetSocketAddress(host, port))){

            }else {
                socketChannel.register(selector, SelectionKey.OP_CONNECT);
            }
        } catch (IOException e) {
            e.printStackTrace();
            //如果没连上,后面就不用读写
            System.exit(1);
        }

        while (started) {
            try {
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    try {
                        handleInput(key);
                    } catch (Exception e) {
                        if (key != null) {
                            key.channel();
                            if (key.channel() != null) {
                                key.channel().close();
                            }
                        }
                        e.printStackTrace();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
        if (selector != null) {
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    private void handleInput(SelectionKey key) throws IOException {
        //判断请求是否有效
        if (key.isValid()) {
            //处理新接入的请求消息
            SocketChannel sc = (SocketChannel) key.channel();
            if (key.isConnectable()) {
                if (sc.finishConnect()) {
                } else {
                    System.exit(1);
                }
            }

            //如果可读
            if (key.isReadable()) {
                //开辟一个1024的缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //读取流,返回读取的字节数
                int readBytes = sc.read(buffer);
                if (readBytes > 0) {
                    //将缓冲区的limit设为0,用于后续对缓冲区的读取
                    buffer.flip();
                    //根据缓冲区的可读字节创建字节数组
                    byte[] bytes = new byte[buffer.remaining()];
                    //将缓冲区内容复制到新建数组中
                    buffer.get(bytes);
                    //将收到的消息打印到屏幕
                    String s = new String(bytes, "UTF-8");
                    System.out.println("client receive:" + s);
                } else if (readBytes < 0) {
                    key.cancel();
                    sc.close();
                }
            }
        }

    }

    //向channel写消息
    private void doWrite(SocketChannel channel, String request) throws IOException {
        //将消息转换为字节数组
        byte[] bytes = request.getBytes();
        //根据消息大小创建bytebuffer
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        //将字节数组复制到缓冲区
        writeBuffer.put(bytes);
        writeBuffer.flip();
        //发送缓冲区
        channel.write(writeBuffer);
    }

    public void sendMsg(String msg) throws IOException {
        socketChannel.register(selector, SelectionKey.OP_READ);
        doWrite(socketChannel, msg);
    }

}

 

posted @ 2023-02-28 22:29  Mars.wang  阅读(11)  评论(0编辑  收藏  举报