netty入门-io介绍

2 篇文章 0 订阅

netty入门-io介绍

传统io样例

package com.hrf.io;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Logger;

public class TradictionIO {

    static Logger logger = Logger.getLogger(TradictionIO.class.getSimpleName());

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        while (true) {
            Socket socket = serverSocket.accept();
            logger.info("客户端连上了 ");
            hander(socket);
            closeSocket(socket);
        }

    }

    private static void closeSocket(Socket socket) throws IOException {
        logger.info("关闭客户端连接 ");
        socket.close();
    }

    private static void hander(Socket socket) throws IOException {
        InputStream in = socket.getInputStream();
        byte[] b = new byte[1024];
        while (true) {

            int result = in.read(b);
            if (result ` -1) {
                logger.info("结束了");
                break;
            }
            logger.info("read {}" + new String(b));
        }
        in.close();

    }

}

分析其中方法serverSocket.accept() 方法说明

Listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made

in.read()方法说明

The number of bytes actually read is returned as an integer. This method blocks until input data is available, end of file is detected, or an exception is thrown. 

总结:

  1. 传统io一般有2个阻塞点,一个是等待连接阻塞,一个是等待数据阻塞
  2. 传统io一般一个连接开一个socket
  3. 可用多线程开启多客户端处理,但消耗大

nio样例

package com.hrf.io;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.logging.Logger;

public class NIOServer {

    static Logger logger = Logger.getLogger(NIOServer.class.getSimpleName());

    private Selector oneSelector;

    public void initServer() throws IOException {
        ServerSocketChannel channel = ServerSocketChannel.open();
        channel.configureBlocking(false);
        channel.socket().bind(new InetSocketAddress(8080));
        this.oneSelector = Selector.open();
        channel.register(oneSelector, SelectionKey.OP_ACCEPT);

        logger.info("服务器启动了");
    }

    public void listen() throws IOException {
        while (true) {
            // This method performs a blocking selection operation. It returns only after at
            // least one channel is selected, this selector's wakeup method is invoked, or
            // the current thread is interrupted, whichever comes first.
            this.oneSelector.select();

            Iterator<SelectionKey> iterator = this.oneSelector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                handler(key);
            }
        }

    }

    private void handler(SelectionKey key) throws IOException {
        if (key.isAcceptable()) {
            handlerAccept(key);
        }
        if (key.isReadable()) {
            handlerRead(key);
        }

    }

    private void handlerRead(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();

        ByteBuffer dst = ByteBuffer.allocate(1024);
        int read = socketChannel.read(dst);
        if (read > 0) {
            byte[] arr = dst.array();
            logger.info("接受到:" + new String(arr));

            ByteBuffer retu = ByteBuffer.wrap("服务器收到了".getBytes());

            socketChannel.write(retu);
        } else {
            logger.info("客户端关闭了");
            key.cancel();

        }

    }

    private void handlerAccept(SelectionKey key) throws IOException {
        // Returns the channel for which this key was created. This method will continue
        // to return the channel even after the key is cancelled.
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
        SocketChannel socketChannel = serverSocketChannel.accept();
        socketChannel.configureBlocking(false);
        logger.info("新客户端连上了");

        socketChannel.register(oneSelector, SelectionKey.OP_READ);
    }

    public static void main(String[] args) throws IOException {
        NIOServer server = new NIOServer();
        server.initServer();
        server.listen();

    }

}

核心处理逻辑

  1. ServerSocketChannel.open()开启一个大门,serverSocketChannel

  2. Selector.open() 开启一个选择器

  3. channel.register(oneSelector, SelectionKey.OP_ACCEPT)注册事件

  4. this.oneSelector.select()阻塞等待连接到来

  5. this.oneSelector.selectedKeys()获取事件

  6. key.channel()获取事件注册的channel

    Returns the channel for which this key was created. This method will continue to return the channel even after the key is cancelled
  7. socketChannel.read读取事件

补充:

channel.register(oneSelector, SelectionKey.OP_WRITE);
这个一般不注册write事件
这个监听的是输入缓冲区状态
OP_WRITE表示底层缓冲区是否有空间,是则响应返还true

总结

传统IO ServerSocket -> socket

NIO ServerSocketChannel->socketChannel

为什么NIO被称为同步非阻塞?

按照《Unix网络编程》的划分,IO模型可以分为:阻塞IO、非阻塞IO、IO复用、信号驱动IO和异步IO,
按照POSIX标准来划分只分为两类:同步IO和异步IO。

如何区分呢?

首先一个IO操作(read/write系统调用)其实分成了两个步骤:1)发起IO请求和2)实际的IO读写(内核态与用户态的数据拷贝)

阻塞IO和非阻塞IO的区别在于第一步,发起IO请求的进程是否会被阻塞,如果阻塞直到IO操作完成才返回那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。
同步IO和异步IO的区别就在于第二步,实际的IO读写(内核态与用户态的数据拷贝)是否需要进程参与,如果需要进程参与则是同步IO,如果不需要进程参与就是异步IO。

如果实际的IO读写需要请求进程参与,那么就是同步IO。因此阻塞IO、非阻塞IO、IO复用、信号驱动IO都是同步IO,

在编程上,这种非阻塞IO一般都采用IO状态事件+回调方法的方式来处理IO操作。
如果是同步IO,则状态事件为读写就绪。此时的数据仍在内核态中,但是已经准备就绪,可以进行IO读写操作。
如果是异步IO,则状态事件为读写完成。此时的数据已经存在于应用进程的地址空间(用户态)中。

上面的Selector.select()虽然也是阻塞,但不是在io操作上阻塞
Selector.select(1000)其实也可以不阻塞
selector.wakeup();也可以唤醒selector
selector.selectNow();也可以立马返还,
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值