NIO同步网络编程

NIO核心三大组件Channel、Buffer和Selector,传送门:

Java NIO核心三大组件Channel、Buffer和Selector(一)

Java NIO核心三大组件Channel、Buffer和Selector(二)

NIO主要有:

  • FileChannel   连接到文件的通道,它无法设置为非阻塞模式,它总是运行在阻塞模式下。
  • ServerSocketChannel  监听新进来的TCP连接的通道,对应前面的服务器端ServerSocket
  • SocketChannel    连接到TCP网络套接字的通道,对应前面的客户端Socket
  • DatagramChannel  连接到UDP包的通道

FileChannel在NIO上面认识过了,这里主要用ServerSocketChannel 和SocketChannel 来进行网络编程。

一、ServerSocketChannel和SocketChannel类

1、ServerSocketChannel 对象

NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道, 类似于ServerSocket,它们的用法比较类似。

accept() 方法监听新进来的连接,返回一个新进来的连接的 SocketChannel。

1. 阻塞模式

通常在while循环中调用 accept()方法,一直监听,因此, accept()方法会一直阻塞到有新连接到达。如:

        while (true) {
            SocketChannel socketChannel = serverSocketChannel.accept();
            //do something
        }

2. 非阻塞模式

ServerSocketChannel可以设置成非阻塞模式。

在非阻塞模式下,accept() 方法会立刻返回,如果还没有新进来的连接,立即返回null。 因此,需要检查返回的SocketChannel是否是null。如:

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1", 9999));
        serverSocketChannel.configureBlocking(false);
        while (true) {
            SocketChannel socketChannel = serverSocketChannel.accept();
            if (socketChannel != null) {
                //do something 
            }
        }

2、SocketChannel 对象

NIO中的SocketChannel是一个连接到TCP网络套接字的通道。类似于ServerSocket,它们的用法比较类似。

connect() 连接服务器端。

1. 非阻塞模式

如果SocketChannel在非阻塞模式下,此时调用connect(),该方法可能在连接建立之前就返回了。

为了确定连接是否建立,可以调用finishConnect()的方法。如:

        socketChannel.configureBlocking(false);
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
        while (!socketChannel.finishConnect()) {
            //wait 等待连接
        }

 

3、非阻塞模式与选择器

非阻塞模式与选择器搭配会工作的更好,通过将一或多个SocketChannel注册到Selector,可以询问选择器哪个通道的兴趣事件来进行连接就绪,读取,写入等。

 

二、NIO同步网络编程

服务端:

public class SimpleServer {
    public static void main(String[] args) {
        try {
            // 获取一个serverSocketChannel,并设置为非阻塞模式,绑定ip和port
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            ServerSocket serverSocket = serverSocketChannel.socket();
            serverSocket.bind(new InetSocketAddress("127.0.0.1", 9999));

            // 获取Selector,注册,并将 ServerSocketChannel 的接收就绪事件通道注册到 Selector
            Selector selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            // 缓冲区, 写点数据
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            ByteBuffer writeBuffer = ByteBuffer.allocate(100);
            writeBuffer.put("服务器端收到了".getBytes());
            writeBuffer.flip();

            // 使用循环不断地监听来自客户端的连接
            while (true){
                int count = selector.select(1000);
                if (count == 0) {
                    continue;
                }
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> it = keys.iterator();

                while(it.hasNext()){
                    SelectionKey key = it.next();
                    it.remove();// 手动移除

                    if(key.isAcceptable()){
                        // 接收就绪,创建新的连接,并且把连接注册到Selector上,并且指定感兴趣的事件是 Accept
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        // 这里我们指定 Server先读,Client先写
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }else if (key.isReadable()){
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        readBuffer.clear();
                        socketChannel.read(readBuffer);
                        readBuffer.flip();
                        System.out.println("服务器received:" + new String(readBuffer.array()));

                        key.interestOps(SelectionKey.OP_WRITE);
                    }else if(key.isWritable()){
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        writeBuffer.rewind();
                        socketChannel.write(writeBuffer);
                        key.interestOps(SelectionKey.OP_READ);
                    }
                }
            }
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }

    }
}

客户端:

public class SimpleClient {

    public static void main(String[] args) {
        try {
            // 创建一个SocketChannel,并设置为非阻塞模式,绑定ip和port
            // 获取Selector,注册,并将 socketChannel 的连接事件通道注册到 Selector
            // 并向服务器发出连接请求
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            Selector selector = Selector.open();
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));

            // 缓冲区, 写点数据
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            ByteBuffer writeBuffer = ByteBuffer.allocate(100);

            // 使用一个循环来连接服务器
            while (true) {
                int count = selector.select(5000);
                if (count == 0) {
                    continue;
                }
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                while (it.hasNext()) {
                    SelectionKey selectionKey = it.next();
                    it.remove();
                    if (selectionKey.isConnectable()) {
                        SocketChannel sc = (SocketChannel) selectionKey.channel();
                        if (socketChannel.isConnectionPending()) {
                            if (sc.finishConnect()) {
                                writeBuffer.put("客户端向你问好".getBytes());
                                writeBuffer.flip();
                                sc.write(writeBuffer);
                                // 改变选择器对通道的兴趣事件, 两种方式都可以,二选一
                                //socketChannel.register(selector, SelectionKey.OP_READ);
                                selectionKey.interestOps(SelectionKey.OP_READ);
                            } else {
                                System.exit(1);
                            }
                        }
                    } else if (selectionKey.isReadable()) {
                        int readBytes = socketChannel.read(readBuffer);
                        if (readBytes > 0) {
                            socketChannel.read(readBuffer);
                            readBuffer.flip();
                            System.out.println("客户端received:" + new String(readBuffer.array()));
                        }
                    }

                }
            }
        } catch (
                IOException ioException) {
            ioException.printStackTrace();
        }
    }
}

 

  这篇博主通过线程的实例代码不错,可以参考:IO模型之NIO代码及其实践详解

 

—— Stay Hungry. Stay Foolish. 求知若饥,虚心若愚。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值