网络编程之SocketIO基础
Posted 蚂蚁与咖啡的故事
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程之SocketIO基础相关的知识,希望对你有一定的参考价值。
继上一篇文章的讲解,已经知道了网络编程的基本方式,今天将继续进行网络编程相关概念的深入讲解。
1.基本概念
1.1 IO(BIO)
Blocking IO,同步阻塞式IO(传统的网络编程模型),完全依靠网络,具有以下特点:
采用线程池和任务队列可以实现伪异步的IO通信框架
传统IO缺点:占用资源多,耗时长
1.2 NIO
Non-Blocking IO 同步非阻塞,多了一个管道的概念,以空间换时间的概念。
1.0版本:只实现非阻塞,未实现异步
2.0版本:JDK1.7之后异步非阻塞(AIO)
NIO与BIO一个最大的区别就是,NIO中的selector采用了epoll代替了传统的select实现,性能大大提升
1.2 AIO
NIO2.0,异步+非阻塞 (JDK1.7以后实现)
1.3 Socket
套接字,应用程序间的TCP通信通常通过Socket向网络发出请求或者应答网络请求
Socket和ServerSocket为java.net包中,ServerSocket用于服务端,-
Socket是在建立网络连接时候用的。在连接成功时,应用程序的两端(客户端和服务端)都会产生一个Socket实例,操作这个实例来完成所需的会话。
对于一个网络连接来说,Socket和ServerSocket是平等的,不管是在服务端还是在客户端,他们的工作都是通过SocketImpl类及其子类完成的。
2.阻塞和非阻塞
程序在等待调用结果(消息,返回值等)时的状态(具体的技术,接收数据的方式、状态),针对网络传输而言。
阻塞:调用结果返回前,当前的线程会被挂起,直到得到结果之后才会返回。(也就是说,应用程序在获取网络数据的时候,如果网络传输数据的时候很慢,那么程序就一直等着,知道传输完毕为止)
非阻塞:在不能立刻得到结果之前,该调用不会阻塞当前线程。(应用程序可以直接获取已经准备就绪好的数据,无需等待)
可参见上一篇文章的详细讲解。
3.同步和异步
关注的是消息通信机制(synchronous communication/asynchronous communication),一般是面向操作系统与应用程序对IO操作的层面上来区分(server端应用程序的执行方式),针对程序层面而言。
同步:应用程序发出了一个“调用”时,在没有得到结果之前,该“调用”就不会返回。直到调用返回。(应用程序会直接参与IO读写操作,并且我们的应用程序会直接阻塞到某个方法上,直到数据准备好了,或者采用轮询的策略检查数据的就绪状态,如果就绪则获取数据)
异步:一个异步过程调用发出之后,调用者不会立即得到结果,而是“被调用者”通过状态、通知等来通知调用者,或者通过回调函数来通知应用程序。(所有的IO读写操作交给操作系统处理,与我们的应用程序没有直接关联,我们的程序不关心IO读写,当操作系统完成了IO读写操作时,会给我们的应用程序发送通知,我们的应用程序直接拿走数据即可)
典型的异步编程模型Node.js
4.Socket的连接过程
Socket的连接过程分为四个步骤:服务器监听、客户端请求服务器、服务器确认、进行通信。
服务器监听:服务器端的套接字并不定位具体的客户端套接字,而是处理等待连接的状态(阻塞),实时监控网络状态
服务端连接确认:服务端套接字监听到客户端的连接请求,就会响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的情况发送给客户端
客户端连接确认:一旦客户端确认了服务端的描述,连接就建立好了。双方就可以开始通信了。并且服务器端的套接字进行处于监听状态,继续监听其他的客户端套接字的请求
5.NIO详解
一般称作Non-Blocking IO,同步非阻塞,在传统的TCP直接建立连接的基础,做了一层抽象,把客户端(SocketChannel)和服务端(ServerSocketChannel )注册到多路复用器上,然后Selector回去轮询所有注册到服务器上的SocketChannel的通道,根据通道的状态(connect/连接、Accept/阻塞、Read/可读、Write/可写)执行相关操作。(概念好理解,编程不好实现)。
这边涉及几个核心概念:Buffer(缓冲区,填充数据的结构)、Channel(管道)、Selector(选择器,多路复用器),如下图所示:
5.1 Buffer
Buffer是一个包含一些要写入或者读取的数据的对象,在NIO类库中加入Buffer对象,体检了新库与原IO的一个重要的区别:
在面向流的IO中,可以直接将数据写入或者读取到Stream对象中
在NIO库中,所有的数据都用缓冲区处理读写操作
缓冲区本质上是一个数组,通常是一个字节数组(ByteBuffer),为缓冲区提供了数据访问的读写操作,如位置、容量、上限等概念
5.2 Channel
网络数据通过Channel进行读写操作,通道与流不同之处在于,通道是双向的(可以用于读写或者二者同时进行,最关键的可以与Selector结合起来),而流是单方向的(一个流必须是InputStream或者OutputStream的子类)。通道分为两大类
网络读写(SelectableChannel),子类:
SocketChannel(客户端)
ServerSocketChannel(服务端)
文件操作(FileChannel)
5.3 Selector
NIO编程的基础,核心。提供了已经选择就绪的任务的能力,Selector会不断的轮询注册到其上的Channel,如果某个通道发生了读写操作,这个通道就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以取得就绪的Channel的集合(可能有多个channel),从而进行后续的IO操作
一个Selector理论上可以负责无数个Channel通道,因为JDK使用了epoll代替了传统的select实现,获得的连接句柄没有限制。
Selector线程就类似一个管理者,只不过是轮询哪个管道的数据已经准备好,然后通知CPU去读写数据
当Channel注册到Selector后,Selector会分配给Channel一个Key值,Selector是以轮询的方式进行查找注册的Channel,当Channel准备好了,Selector就会识别,通过KEY值找到相应的Channel进行相关的数据处理操作。
Selector只负责轮询注册到其上的Channel的状态位,不负责具体操作,轮询出来的SocketChannel一定是准备就绪好的数据(缓冲好的数据,非阻塞状态),而在BIO中是一个字节一个字节的传输(阻塞状态)
NIO编程需要注意的问题:TCP拆包粘包的问题(解码的时候才会出现这个问题)
6.AIO(NIO2.0)
异步非阻塞,在NIO的基础上,引入了异步通道的概念,并提供了异步文件和异步套接字通道的实现,从而在真正意义上实现了异步非阻塞。AIO不需要通过Selector注册Channel进行轮询操作即可实现异步读写,从而简化了NIO编程模型。(概念不好理解,编程好实现)
AsynchronousSocketChannel
AsynchronousServerSocketChannel
7.IO多路复用的系统调用使用epoll代替了select,主要有以下几方面原因(整理自《netty权威指南》):
支持一个进程打开的socket描述符(FD)不受限制(仅受限于操作系统的最大文件句柄数)
I/O效率不会随着FD数目的增加而线性下降
使用mmap加速内核与用户空间的消息传递
epoll的API更简单
以上是关于网络编程之SocketIO基础的主要内容,如果未能解决你的问题,请参考以下文章