网络编程(2)——BIO

基本概念

socket相关

socket又叫做“套接字”,应用程序通常通过“套接字”向网络发出请求或者应答网络请求。

SocketServerSocket类库位于java.net包中。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的回话。对于一个网络连接来说,套接字是平等的,不因为在服务器端或在客户端而产生不同级别。不管是Socket还是ServerSocket它们的工作都是通过SocketImpl类机器子类完成的。

套接字自检的连接过程可以分为四个步骤:服务器监听,客户端请求服务器,服务器确认,客户端确认,进行通信。

  • **服务器监听:**是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
  • **客户端请求:**是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
  • **服务器端连接确认:**是指当服务器端套接字坚挺到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端。
  • **客户端连接确认:**一旦客户端确认了此描述,连接就建立好了。双方开始进行通信。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

BIO和NIO的区别

其本质就是阻塞和非阻塞的区别。

**阻塞概念:**应用程序在获取网络数据的时候,如果网络传输数据很慢,那么程序就一直等着,直到传输完毕为止。
**非阻塞概念:**应用程序直接可以获取已经准备就序号的数据,无需等待。

BIO为同步阻塞形式,NIO为同步非阻塞形式。NIO并没有实现异步。在JDK1.7之后,升级了NIO的库包,支持异步非阻塞通信模型,即AIO

同步和异步

同步和异步一般是面向操作系统与应用程序对IO操作的层面上来区别的。

同步时,应用程序会直接参与IO读写操作,并且我们的应用程序会直接阻塞到某一个方法上,直到数据准备就绪;或者采用轮训的策略实时检查数据的就绪状态,如果就绪则获取数据。

异步时,则所有的IO读写操作交给操作系统处理,与我们的应用程序没有直接关系,我们程序不需要关系IO读写,当操作系统完成了IO读写操作时,会给我们应用程序发送通知,我们的应用程序直接拿走数据即可。

  • BIO完全依赖于网络
  • NIO使用一个channel

传统的BIO编程

网络编程的基本模型是Client/Server模型,也就是两个进程直接进行相互通信,其中服务端提供配置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发送连接请求,通过三次握手建立连接,如果连接成功,则双方即可以进行通信(网络套接字socket)

这里写图片描述

BIO流程

服务器

这里写图片描述

客户端
这里写图片描述

1. Server类

这里写图片描述

2. ServerHandler类

这里写图片描述

3. client类

这里写图片描述

这是一个经典的每个连接对应一个新线程的模型,之所以使用多线程,主要原因在于,socket.accpet()socket.read()socket.write()三个主要函数都是同步阻塞的,当一个连接在处理I/O的时候,系统是阻塞的,如果是单线程的话必然就挂死在哪里;但CPU是被释放出来的,开启多线程,就可以让CPU去处理更多的事情。

使用线程池来改进传统的BIO编程

这里写图片描述

这里写图片描述

使用多线程的本质:

    1. 利用多核
    1. I/O阻塞系统,但CPU空闲的时候,可以利用多线程使用CPU资源

现在多线程一般都使用线程池,可以让线程的创建和回收成本相对较低。在活动连接数不是特别高(小鱼单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的I/O并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或者请求。

BIO的缺点

不过,这个模型最本质的问题在于,严重依赖于线程,但线程是很“贵”的资源,主要表现在:

  • 线程的创建和销毁成本很高,在Linux这样的操作系统中,线程本质上就是一个进程。创建和销毁都是重量级的系统函数。
  • 线程本身占用较大内存,像java的线程栈,一般至少分配512K——1M的空间,如果系统中的线程数过千,恐怕整个JVM的内存都会被吃掉一半。
  • **线程的切换成本是很高的。**操作系统发生线程切换的时候,需要保留线程的上下文,然后执行系统调用。如果线程数过高,可能执行线程切换的时候甚至会大于线程执行的时间,这时候带来的表现往往是系统load偏高、CPU使用率特别高(超过20%以上),导致系统几乎陷入不可用的状态。
  • **容易造成锯齿状的系统负载。**因为系统负载是用活动线程数或CPU核心数,一旦线程数量高但外部网络环境不是很稳定,就很容易造成大量请求的结果同时返回,激活大量阻塞线程从而使负载压力过大。
  • 完全依赖于网络的好坏。如果网络不好,传输的就很慢,会有很大的网络延迟。

BIO、NIO与AIO的对比

以socket.read()为例子:

  • BIO:传统的BIO里面的socket.read(),如果TCP RecvBuffer里面没有数据,函数会一直阻塞,知道收到数据,返回读到的数据。
  • NIO:如果TCP RecvBuffer有数据,就把数据从网卡读到内存,并且返回给用户;反之则直接返回0,永远不会阻塞
  • AIO:最新的AIO里面会更进一步:不但等待就绪是非阻塞的,就连数据从网卡到内存的过程也是异步的。

换句话说,BIO里面用户最关心“我要读”,NIO里用户最关心“我可以读了”,在AIO模型里用户更需要关注的是“读完了”。

NIO一个重要的特点是:socket主要的读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,真正的I/O操作是同步阻塞的(消耗CPU但性能非常高)。

参考并感谢

https://tech.meituan.com/nio.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值