| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 系统运维 -> NIO和BIO以及传统IO -> 正文阅读 |
|
[系统运维]NIO和BIO以及传统IO |
首先明白这三个概念1.NIO是非阻塞.基于网络的IO,即从网络上传过来的数据读取 2.BIO是阻塞.基于网络的IO,同上 3.传统IO,传统IO是和硬盘打交道,即读写硬盘,和网络没有关系 下文描述的是基于网络的NIO和BIO,耐心看完,前面是对网络的阐述,只有明白网络才能理解IO 1.计算机体系在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的系统调用函数 使用指令: man 2 open 查看关于open的系统调用 使用指令: man 2socket 查看关于socket的系统调用 java -> 编译成字节码(文本文件,字节存储的序列化) -> 运行在一个用c语言写的jvm或者java进程中 -> 最终去调用内核 也就是这些系统调用 网络IO: 可以是打开文件,也可以是socket这种网络通信 ? 源不一样,产生的效果是不一样的,如果是读取文件,无非就是读取的快慢问题,如果是socket网络IO的话,会有一个读取写入的阻塞概念在里面 网络: 协议,系统调用两个层面 TCP/IP协议其实是这4层的统称 应用层使用这个和百度建立连接 8 是管理员(文件描述符) 指向一个双向得分(<>),既有输入又有输出的socket文件 proc 目录,是内核在运行的时候,在文件系统中可以让你看到的 echo $$ 当前bash进程的pid 等同于$BASHPID ? Bash (GNU Bourne-Again Shell) 是大多数Linux系统以及Mac OS X默认的shell,是一个为GNU计划编写的Unix shell,是一个程序,是一个gnu软件。 (外壳程序) bash实质上是一个可执行程序,一个用户的工作环境。 进入这个bash进程中,我们主要关注这个fd,文件描述符 0 ? 1 2 输入输出以及报错流 用这种文件描述符表示IO socket表示和百度的连接,这时我们建立了连接,百度就是应用层的服务器,我们这个虚拟机就相当于客户端 这时我们要给百度发送信息,但是必须遵循http协议,我们接下来测试访问百度的主页 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这个文件描述符 8具备输入输出两个方向: 常识 网络通信就是两个方向 有输入和输出 使用cat <& 8 查看输入 ? 这时我们看不到输入,因为连接超时了 首先: 连接和发送和接收数据是两件事 先建立连接才能发送数据,从建立连接和发送数据中间一定有时间间隔,站在百度服务器的角度,服务器准备去读取客户端传过来的信息,在这个等的过程中,不能干其他的事情,这个就叫阻塞 传输控制层TCP: 面向连接的可靠传输 连接三次握手: (c发送)(s发送 c接收) (c发送 s接收)确保每一方发送的都能得到回复 然后进行连接,连接是双方都为对方开辟资源!!!! 不是真的有条线连接 发送数据 当没有数据发送了开始断开连接 回收资源!!!! 四次挥手: 因为资源是对方为开辟的 如果需要断开,必须双方都发送断开的意愿并且得到对方的回复 socket: ip-port ip-port 四个属性确定一个连接 端口范围0-65535 所有如果连接建立长时间不发送数据就会回收socket断开连接 网络层IP 全球唯一的 ifconfig 查看主机的网络 当前网络配置: networkctl status 查看某一个网络的具体信息: ifconfig 网卡名 ip地址和子网掩码做与运算 : 这台计算机所在的网段 ? 网络的通信时: 硬盘和网卡都是输入输出设备,这两个是ms毫秒级别,内存这个主存寻址时间是纳秒级别,比一切IO快了10万倍,内存可以寻址到,但是IO却不能那么快读出来,这就是瓶颈 路由表: 完成下一条 只记录当前这个主机可以访问的 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协议 数据包在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.做爬虫请求,访问主页 系统调用使用指令: man 2 socket 查看关于socket的系统调用函数 关于socket的其他命令 这表明socket可以是非阻塞的 man 2 bind 执行查看系统调用关于bind的 man 2 read 查看系统调用读取的 这里我们看到read去读取这个文件描述符: 我们之前通过读取关于socket的系统调用可以获取到,socket是有阻塞和非阻塞的 当我们去read时,如果是阻塞的,就会一直因为socket阻塞而一直等着去读 如果是非阻塞的,当读不到时,直接返回,什么时候心情好了再去读 BIOTomcat的执行: 1.启动后,先监听Kernel内核的8080端口,假设生成一个文件描述符6 2.客户端1访问操作系统建立socket连接,并给它一个文件描述符8 3.客户端2访问操作系统建立socket连接,并给它一个文件描述符9 4.Tomcat如果想要去读取客户端的信息,就要read(8) read(9) 5.在传统的BIO中,Tomcat通过每一个连接创建一个线程进行读取,因为是阻塞的,只有这样才能保证每一个客户端读取不受阻塞影响 NIOTomcat创建一个线程: 通过轮询等方式,交替访问不同的文件描述符,通过非阻塞的方式 弊端: 假设我们有1000个连接,999和是空的,那么效率是极低的,这1000次都要通过系统调用read(文教描述符),极大的消耗了内核操作cpu 一个好的程序应该是应用程序操作cpu时间多一些,系统调用操作cpu少一些 弥补NIO缺陷内核Kernel创建一个新的系统调用,来弥补多次文件描述符的交换操作 man 2 select 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(); ? ? ? ? ? ? ? } ? ? ? ? ? ? ? } ? ? ? } ? } ? } 多路复用: 通过系统调用的select实现,但是这个select不是一个NIO 通过Java设置了一个过期时间,所有的步骤最终都, 落到操作系统层面,每一步都对应着系统调用 弊端: select里面有大量文件描述符,例如1000个,每次调用select都要传递1000个文件描述符参数,会有多次拷贝的过程 epoll使用 man epoll 学习如何使用 这里面描述了关于epoll有这样三个2类系统调用 逐个打开 在这里只有一个连接创建的时候才会调用一次,换而言之,有1000次连接,只需要调用1000次epoll_ctl就行了,epoll的文件描述符,取代了之前每一个的文件描述符. 询问服务端,我给你的epoll你现在还有没有了 类似一个红黑树 零拷贝: sendfile(in,out) |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 17:35:21- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |