【注:】
除了BIO、NIO之外,还有一种IO叫AIO,即异步IO(同样分为阻塞和飞阻塞两种),我个人的理解是BIO和NIO属于server端的IO处理方式,而AIO则是client端的IO处理方式(比如Rocket MQ的client端可以注册异步监听器来处理broker的响应)

缓冲区本质是一个可以读写数据的内存块,可以理解为一个数组容器对象,该对象提供了方法来管理缓冲区,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化。Channel提供从文件、网络读取数据的渠道,但是读取或写入的数据必须经由BufferBuffer视频讲解推荐
https://www.bilibili.com/video/BV1DJ411m7NR?p=11&vd_source=8be621c052fd9f705308579363b67881

| 属性 | 描述 |
|---|---|
| capacity | 私有,表示当前Buffer的容量,创建后不可变 |
| limit | 私有,表示读写的最大值,<=capacity |
| position | 私有,索引位,初始值0 |
| mark | 私有,标记,初始值-1 |

debug代码观察上述属性的变化
- public class BasicBuffer {
- public static void main(String[] args) {
- // 创建一个大小为5个int值的buffer
- IntBuffer intBuffer = IntBuffer.allocate(5);
- for(int i=0;i
- intBuffer.put(i*2);
- }
- // 如何从buffer读取数据
- // 将buffer转换,读写切换
- intBuffer.flip();
- while(intBuffer.hasRemaining()){
- System.out.println(intBuffer.get());
- }
- }
- }
相关方法
方法 功能 public final int capacity() 获取buffer容量 public final int position() 获取索引位置 public final Buffer position(int newPosition) 设置缓冲区的位置 public final int limit() 返回此buffer的限制 public final Buffer limit(int newLimit) 设置缓冲区的限制 public final Buffer mark() 在缓冲区的位置设置标记 public final Buffer reset() 将缓冲区的位置重置为mark的值 public final Buffer clear() 清除缓冲区,将各个标记恢复到初始态,数据并未清除 public final Buffer flip() 读写切换 public final Buffer rewind() 重绕此缓冲区 public final int remaining() 返回当前位置和限制之间的元素数 public final boolean hasRemaining() 告知缓冲区当前位置和限制之间是否有元素 public abstract boolean isReadOnly() 是否为只读缓冲区 public abstract boolean hasArray() 告知缓冲区是否具有可访问的底层实现数组 public abstract Object array() 返回此缓冲区的底层实现数组 public abstract int arrayOffset() 返回此缓冲区的底层实现数组中第一个缓冲区元素的偏移量 public abstract boolean isDirect() 告知此缓冲区是否为直接缓冲区
selector

1、当serverSocketChannel连接上客户端后生成socketChannel;
2、当socketChannel注册到selecor上时同时也会注册一个selectorKey,register(Selector sel, int ops)#SelectorKey
BIO代码
传统的网络io模式面向流,一个线程对接一个会话,因此高并发会因线程阻塞而性能低效
- public class BIO implements Connector {
-
- private Integer port = 8080;
- @Override
- public void start() throws IOException {
- ServerSocket serverSocket = new ServerSocket(port);
- LogUtil.getLogger().debug("端口"+port+"启动");
-
- while (true){
- final Socket socket = serverSocket.accept();
- ThreadUtil.threadPool.execute(new Runnable() {
- @Override
- public void run() {
- handler(socket);
- }
- });
- }
- }
-
- public static void handler(Socket socket){
- try {
- byte[] bytes = new byte[1024];
- InputStream inputStream = socket.getInputStream();
- while (true){
- int status = inputStream.read(bytes);
- if(status!=-1){
- LogUtil.getLogger().debug(new String(bytes,0,status));
- }else {
- break;
- }
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- if(Objects.nonNull(socket)){
- try {
- socket.close();
- LogUtil.getLogger().debug("关闭链接");
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
-
- }
代码逻辑
1、实现客户端tcp连接:Socket socket = serverSocket.accept(port)
2、调用额外线程实现socket监听和读写:如socket.read();
【注:】
accept方法的原理
1、(调用os的接口)告诉操作系统,当前程序的socket指定了8888端口,当端口收到连接信息之后,会把消息通过DMA的方式放到一个缓存区中,并唤醒注册在os中的阻塞线程,让这个线程到缓存区处理连接消息。(或者缓存区由socket程序指定)
2、端口有连接进来时,给cpu发中断,然后由该方法所在线程处理连接