ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NIO和BIO以及传统IO

2021-07-31 23:34:22  阅读:184  来源: 互联网

标签:BIO 调用 NIO 文件 描述符 IO 客户端 连接 socket


首先明白这三个概念

1.NIO是非阻塞.基于网络的IO,即从网络上传过来的数据读取

2.BIO是阻塞.基于网络的IO,同上

3.传统IO,传统IO是和硬盘打交道,即读写硬盘,和网络没有关系

下文描述的是基于网络的NIO和BIO,耐心看完,前面是对网络的阐述,只有明白网络才能理解IO

1.计算机体系

image-20210722224416023

在linux系统中,一切皆文件,例如:摄像头,打印机,将他们两个抽象成文件,就是读取和输出文件

文件描述符: 在linux没有面向对象的概念,把一切抽象成文件了,当想操作一个输入输出设备,Socket等,连接文件以后,就有一个数字代表它,叫文件描述符,就像面向对象的引用变量一样

使用虚拟机查看系统调用

ubuntu中man的手册默认没有装。
​
第一步,打开虚拟机,打开超级终端
​
第二步,输入下面命令,一定要在联网的情况下,要不安装包下载不下来
​
用下面几条命令就行了:
​
#sudoapt-get install manpages #sudo apt-get install manpages-de #sudo apt-get install manpages-de-dev #sudo apt-get install manpages-dev
​
第三步,没有错误提示的话,就可以使用man了
​
Linux 的man手册共有以下几个章节:
​
1、Standard commands (标准命令)2、System calls (系统调用函数)3、Libraryfunctions (库函数)4、Specialdevices (设备说明)5、File formats (文件格式)6、Games andtoys (游戏和娱乐)7、Miscellaneous(杂项)8、AdministrativeCommands (管理员命令)
​

使用指令: man 2 read 查看关于read的系统调用函数

image-20210722231642056

使用指令: man 2 open 查看关于open的系统调用

image-20210722232237770

使用指令: man 2socket 查看关于socket的系统调用

image-20210722232851809

java -> 编译成字节码(文本文件,字节存储的序列化) -> 运行在一个用c语言写的jvm或者java进程中 -> 最终去调用内核 也就是这些系统调用

网络

IO: 可以是打开文件,也可以是socket这种网络通信
​
源不一样,产生的效果是不一样的,如果是读取文件,无非就是读取的快慢问题,如果是socket网络IO的话,会有一个读取写入的阻塞概念在里面

网络: 协议,系统调用两个层面

image-20210723200144868

TCP/IP协议其实是这4层的统称

image-20210723200401231

应用层

image-20210723201021126

使用这个和百度建立连接

8 是管理员(文件描述符) 指向一个双向得分(<>),既有输入又有输出的socket文件

image-20210723201617724

proc 目录,是内核在运行的时候,在文件系统中可以让你看到的

image-20210723213422219

echo $$  当前bash进程的pid 等同于$BASHPID
​
Bash (GNU Bourne-Again Shell) 是大多数Linux系统以及Mac OS X默认的shell,是一个为GNU计划编写的Unix shell,是一个程序,是一个gnu软件。 (外壳程序)
bash实质上是一个可执行程序,一个用户的工作环境。

image-20210723214652951

进入这个bash进程中,我们主要关注这个fd,文件描述符

image-20210723214902204

0   1  2 输入输出以及报错流  用这种文件描述符表示IO
socket表示和百度的连接,这时我们建立了连接,百度就是应用层的服务器,我们这个虚拟机就相当于客户端
这时我们要给百度发送信息,但是必须遵循http协议,我们接下来测试访问百度的主页

image-20210723215620685

image-20210723223801372

echo "GET / HTTP/1.0"   将这个HTTP协议输出到本地   HTTP协议必须要请求方式(GET)  版本(1.0)
echo -e "GET / HTTP/1.0\n"  HTTP还需要有换行符   用-e来处理
echo -e "GET / HTTP/1.0\n" >& 8   >向外输出   直接使用>是输出到一个文件 >&(加数字) 才是输出到8这个文件描述符

image-20210723220335835

8具备输入输出两个方向: 常识  网络通信就是两个方向  有输入和输出
使用cat <& 8 查看输入  
这时我们看不到输入,因为连接超时了

image-20210723223833357

image-20210723223841815

首先: 连接和发送和接收数据是两件事
先建立连接才能发送数据,从建立连接和发送数据中间一定有时间间隔,站在百度服务器的角度,服务器准备去读取客户端传过来的信息,在这个等的过程中,不能干其他的事情,这个就叫阻塞

传输控制层

image-20210723224712760

TCP:  面向连接的可靠传输
连接三次握手: (c发送)(s发送  c接收) (c发送 s接收)确保每一方发送的都能得到回复
然后进行连接,连接是双方都为对方开辟资源!!!!  不是真的有条线连接
发送数据 当没有数据发送了开始断开连接
回收资源!!!!
四次挥手:  因为资源是对方为开辟的 如果需要断开,必须双方都发送断开的意愿并且得到对方的回复 

socket:

ip-port ip-port 四个属性确定一个连接 端口范围0-65535 所有如果连接建立长时间不发送数据就会回收socket断开连接

网络层

IP 全球唯一的

image-20210723232325453

ifconfig  查看主机的网络

当前网络配置:

networkctl status

image-20210724114010490

查看某一个网络的具体信息:

ifconfig 网卡名

image-20210724114052446

ip地址和子网掩码做与运算 : 这台计算机所在的网段  

image-20210724120549082

网络的通信时:

硬盘和网卡都是输入输出设备,这两个是ms毫秒级别,内存这个主存寻址时间是纳秒级别,比一切IO快了10万倍,内存可以寻址到,但是IO却不能那么快读出来,这就是瓶颈

路由表:  完成下一条
只记录当前这个主机可以访问的

image-20210724122021977

route -n   显示和操作路由表
Destination: 目标地址
Gateway: 网关
GenMask: 掩码
Iface: 本机地址
一个连接怎么实现:
首先我这个主机的IP是192.168.76.135,我们要访问一个IP目标地址,例如百度(61.135.169.125)
有一个判定,就是拿着我们的要访问的目标地址去和Genmask相与,得到的ip再去和Destination做比较, 默认条目
得到这个目标地址匹配后,我们要把数据发给网关,也就是路由器,路由器在发到运营商等
​
在这里先和局域网的掩码做与运算,在和其他的,因为如果是局域网和0.0.0.0与运算也能到达网关
这时出现一个问题,百度地址和网关地址不一样呀,我们通信要发送数据包,那么发送数据包应该发送谁的地址,发送网关地址,那么达到网关就结束了,网关拿到这个会认为是它的局域网,没办法继续传递,如果数据包封装百度地址,那么目标地址就不对了呀,网关就收不到了
所以IP地址 数据包只会封装百度的地址

链路层

arp协议

image-20210724133644317

数据包在IP地址上再封装arp协议,里面有下一跳网关的mac地址,路由器拿到这个数据包进行解析,根据ip判断下一跳的地址,把数据包传给下一个路由,这时ip一直是目标地址百度的,那么改变的是arp协议中的下一个网关的mac地址

下面进行抓包测试:

tcpdump -n -i ens33 arp or port 80
抓取tcp连接 通过ens33网卡的关于 arp 和80端口的数据包
arp -d 192.168.76.2 && curl www.baidu.com 80
这个指令为两条  1删除 192.168.76.2的所请求回来的mac地址   2.做爬虫请求,访问主页

image-20210724135807305

image-20210724135819284

image-20210724140512646

系统调用

使用指令: man 2 socket 查看关于socket的系统调用函数

image-20210724193608482

关于socket的其他命令

image-20210724193959719

这表明socket可以是非阻塞的

image-20210724194312120

man 2 bind 执行查看系统调用关于bind的

image-20210724194953599

man 2 read  查看系统调用读取的

image-20210724195202629

这里我们看到read去读取这个文件描述符:
我们之前通过读取关于socket的系统调用可以获取到,socket是有阻塞和非阻塞的
当我们去read时,如果是阻塞的,就会一直因为socket阻塞而一直等着去读
             如果是非阻塞的,当读不到时,直接返回,什么时候心情好了再去读

BIO

image-20210724200055857

Tomcat的执行:
1.启动后,先监听Kernel内核的8080端口,假设生成一个文件描述符6
2.客户端1访问操作系统建立socket连接,并给它一个文件描述符8
3.客户端2访问操作系统建立socket连接,并给它一个文件描述符9
4.Tomcat如果想要去读取客户端的信息,就要read(8) read(9)
5.在传统的BIO中,Tomcat通过每一个连接创建一个线程进行读取,因为是阻塞的,只有这样才能保证每一个客户端读取不受阻塞影响

NIO

image-20210724203204191

Tomcat创建一个线程:
通过轮询等方式,交替访问不同的文件描述符,通过非阻塞的方式
弊端:
假设我们有1000个连接,999和是空的,那么效率是极低的,这1000次都要通过系统调用read(文教描述符),极大的消耗了内核操作cpu
一个好的程序应该是应用程序操作cpu时间多一些,系统调用操作cpu少一些

弥补NIO缺陷

内核Kernel创建一个新的系统调用,来弥补多次文件描述符的交换操作

man 2 select

image-20210724221110299

package com.bjmashibing.system.io;
​
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
​
public class SocketNIO {
​
    //  what   why  how
    public static void main(String[] args) throws Exception {
​
        LinkedList<SocketChannel> clients = new LinkedList<>();
​
        ServerSocketChannel ss = ServerSocketChannel.open();  //服务端开启监听:接受客户端
        ss.bind(new InetSocketAddress(9090));
        ss.configureBlocking(false); //重点  OS  NONBLOCKING!!!  //只让接受客户端  不阻塞
​
//        ss.setOption(StandardSocketOptions.TCP_NODELAY, false);
//        StandardSocketOptions.TCP_NODELAY
//        StandardSocketOptions.SO_KEEPALIVE
//        StandardSocketOptions.SO_LINGER
//        StandardSocketOptions.SO_RCVBUF
//        StandardSocketOptions.SO_SNDBUF
//        StandardSocketOptions.SO_REUSEADDR
​
​
​
​
        while (true) {
            //接受客户端的连接
            Thread.sleep(1000);
            SocketChannel client = ss.accept(); //不会阻塞?  -1 NULL
            //accept  调用内核了:1,没有客户端连接进来,返回值?在BIO 的时候一直卡着,但是在NIO ,不卡着,返回-1,NULL
            //如果来客户端的连接,accept 返回的是这个客户端的fd  5,client  object
            //NONBLOCKING 就是代码能往下走了,只不过有不同的情况
​
            if (client == null) {
             //   System.out.println("null.....");
            } else {
                client.configureBlocking(false); //重点  socket(服务端的listen socket<连接请求三次握手后,往我这里扔,我去通过accept 得到  连接的socket>,连接socket<连接后的数据读写使用的> )
                int port = client.socket().getPort();
                System.out.println("client..port: " + port);
                clients.add(client);
            }
​
            ByteBuffer buffer = ByteBuffer.allocateDirect(4096);  //可以在堆里   堆外
​
            //遍历已经链接进来的客户端能不能读写数据
            for (SocketChannel c : clients) {   //串行化!!!!  多线程!!
                int num = c.read(buffer);  // >0  -1  0   //不会阻塞
                if (num > 0) {
                    buffer.flip();
                    byte[] aaa = new byte[buffer.limit()];
                    buffer.get(aaa);
​
                    String b = new String(aaa);
                    System.out.println(c.socket().getPort() + " : " + b);
                    buffer.clear();
                }
​
​
            }
        }
    }
​
}

image-20210724230635149

多路复用:
通过系统调用的select实现,但是这个select不是一个NIO
通过Java设置了一个过期时间,所有的步骤最终都, 落到操作系统层面,每一步都对应着系统调用
弊端:
select里面有大量文件描述符,例如1000个,每次调用select都要传递1000个文件描述符参数,会有多次拷贝的过程

epoll

使用 man epoll 学习如何使用
这里面描述了关于epoll有这样三个2类系统调用

image-20210724233202782

逐个打开

image-20210724233619438

image-20210724233913901

在这里只有一个连接创建的时候才会调用一次,换而言之,有1000次连接,只需要调用1000次epoll_ctl就行了,epoll的文件描述符,取代了之前每一个的文件描述符.

image-20210724235548348

询问服务端,我给你的epoll你现在还有没有了

image-20210724235840809

类似一个红黑树
零拷贝: sendfile(in,out) 

标签:BIO,调用,NIO,文件,描述符,IO,客户端,连接,socket
来源: https://blog.csdn.net/weixin_56892092/article/details/119282996

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有