Nio

一、Nio服务端程序启动和运行的详细过程:
1、打开一个serverSocketChannel(相当于ServerSocket),并绑定监听地址,设置连接为非阻塞

servChannel = ServerSocketChannel.open();//建立通道
servChannel.socket().bind(new InetSocketAddress(port), 1024);//通道绑定端口
servChannel.configureBlocking(false);//设置通道非阻塞

2、创建多路复用器selector,将serverSocketChannel注册到selector监听accept事件
(其实就是把serverSocketChannel生成的文件描述符FD和selector绑定在一起以方便以后使用通道进行数据读写)

selector = Selector.open();//创建selector
servChannel.register(selector, SelectionKey.OP_ACCEPT);//通道和selector绑定,监听accept事件

3、selector每隔一秒无限轮询准备就绪的key(客户端准备就绪可以连接服务端就向selector注册一个准备就绪的key)

selector.select(1000);//selector的等待时间是1s
        Set<SelectionKey> selectedKeys = selector.selectedKeys();//查询出就绪的key
        Iterator<SelectionKey> it = selectedKeys.iterator();
        SelectionKey key = null;
        while (it.hasNext()) {//遍历所有的key
            key = it.next();
            it.remove();
            try {
                //处理新的接入请求
            } catch (Exception e) {
                //处理异常就要处理掉这个key并关闭通道,客户端重新连接时需要重新注册key
                if (key != null) {
                    key.cancel();
                    if (key.channel() != null)
                    key.channel().close();
                }
            }
        }

4、selector监听到有新连接请求,则要处理新的连接请求,设置连接为非阻塞,并把socketChannel向selector注册,监听读操作

if (key.isAcceptable()) {
            //处理新的接入请求
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            SocketChannel sc = ssc.accept();
            sc.configureBlocking(false);
            //把socketChannel向selector注册,监听读操作
            sc.register(selector, SelectionKey.OP_READ);
        }

5、selector监听到有数据可读,就从该key对应的socketChannel中读取数据

if (key.isReadable()) {
            //读数据
            SocketChannel sc = (SocketChannel) key.channel();
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);//设置buffer的块大小
            int readBytes = sc.read(readBuffer);
            if (readBytes > 0) {
                readBuffer.flip();
                byte[] bytes = new byte[readBuffer.remaining()];
                readBuffer.get(bytes);
                String body = new String(bytes, "UTF-8");
                System.out.println("The time server receive order : "
                    + body);
                String currentTime = "QUERY TIME ORDER"
                    .equalsIgnoreCase(body) ? new java.util.Date(
                    System.currentTimeMillis()).toString()
                    : "BAD ORDER";
                doWrite(sc, currentTime);//服务端给客户端返回的结果
            } else if (readBytes < 0) {//readBytes=-1
                //对端链路关闭
                key.cancel();
                sc.close();
            }
        }

6、服务端向客户端写回的结果

private void doWrite(SocketChannel channel, String response)
            throws IOException {
            if (response != null && response.trim().length() > 0) {
                byte[] bytes = response.getBytes();
                ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
                writeBuffer.put(bytes);
                writeBuffer.flip();
                channel.write(writeBuffer);
            }
        }

二、Nio客户端程序启动和运行的详细过程:
1、和服务端类似客户端需要有selector多路复用器和socketChannel

selector = Selector.open();//创建selector
socketChannel = SocketChannel.open();//创建客户端的socketChannel
socketChannel.configureBlocking(false);//设置通道非阻塞

2、连接服务端,

// 如果直接连接成功,则注册到多路复用器上,发送请求消息,读应答
        if (socketChannel.connect(new InetSocketAddress(host, port))) {
            socketChannel.register(selector, SelectionKey.OP_READ);//连接成功,注册到selector监听读数据
            doWrite(socketChannel);
        } else
            socketChannel.register(selector, SelectionKey.OP_CONNECT);//连接不成功,注册到selector监听连接
        }
3、selector每隔一秒无限轮询准备就绪的key
selector.select(1000);
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        Iterator<SelectionKey> it = selectedKeys.iterator();
        SelectionKey key = null;
        while (it.hasNext()) {
            key = it.next();
            it.remove();
            try {
                //处理和服务端新建立的连接
            } catch (Exception e) {
                //处理异常就要处理掉这个key并关闭通道,客户端重新连接时需要重新注册key
                if (key != null) {
                    key.cancel();
                    if (key.channel() != null)
                    key.channel().close();
                }
            }
        }
4、判读key如果连接成功则注册读监听,如果可读就从Channel中读数据
if (key.isValid()) {
            // 判断是否连接成功
            SocketChannel sc = (SocketChannel) key.channel();
            if (key.isConnectable()) {
                if (sc.finishConnect()) {
                    sc.register(selector, SelectionKey.OP_READ);
                    doWrite(sc);
                } else
                    System.exit(1);// 连接失败,进程退出
            }
            if (key.isReadable()) {
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                int readBytes = sc.read(readBuffer);
                if (readBytes > 0) {
                    readBuffer.flip();
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String body = new String(bytes, "UTF-8");
                    System.out.println("Now is : " + body);
                    this.stop = true;
                } else if (readBytes < 0) {
                    // 对端链路关闭
                    key.cancel();
                    sc.close();
                }
            }
        }
5、客户端向服务端发送数据
private void doWrite(SocketChannel sc) throws IOException {
            byte[] req = "QUERY TIME ORDER".getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
            writeBuffer.put(req);
            writeBuffer.flip();
            sc.write(writeBuffer);
            if (!writeBuffer.hasRemaining())
                System.out.println("Send order 2 server succeed.");
        }

补充:
//多路复用器关闭后,所有注册在上面的Channel和Pipe等资源都会被自动去注册并关闭

selector.close();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值