一些基本概念
1 Socket
1) 同一个名词Socket有多种不同意思。
2) 在计算机网络知识体系中,运输层的TCP(传输控制协议)把连接作为最基本的抽象。TCP的连接有两个端点,被称为Socket,通过IP地址+端口号来区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。Client进程和Server进程之间是通过Socket读写数据进行通信的。
3) JDK的java.net包下有两个类:Socket
和ServerSocket
,在Client和Server建立连接成功后,两端都会产生一个Socket实例,操作这个实例,完成所需的会话,而程序员就通过这些API进行网络编程。 Socket连接过程分为三个步骤:服务器监听,客户端请求,连接确认。需要注意的是,Socket自己并不是一种协议,而是对TCP/IP协议的抽象,并提供最基本的函数接口方便开发者调用。
2 Netty
Netty.io官网介绍:
Netty是一个异步非阻塞的事件驱动型的网络应用程序框架。满足高性能协议服务器和客户端的快速发展需求。
Netty是一个高性能、方便开发的NIO框架,使用它可以简单快速地开发网络应用程序,比如客户端和服务端的协议。Netty大大简化了网络编程比如TCP和UDP的 Socket的开发。
“快速和简单”并不意味着应用程序会有难维护和性能低的问题,Netty是一个精心设计的框架,它从FTP、SMTP、HTTP、许多二进制和基于文本的传统协议实现中吸收了经验,为我们提供了一个高开发效率、高性能、稳定、可伸缩的解决方案。
3 NIO
刚刚提到:
Netty是一个NIO框架。
理解NIO框架的基本概念有助于学习Netty提供给开发者使用的API。
NIO 是jdk1.4 里提供的新API,全称即java new I/O。Sun 官方标榜的特性如下: 为所有的原始类型提供缓存(Buffer)支持。
首先我们来回忆一下Java的IO
- IO可以简单理解为 数据流方向:程序→硬盘写数据(Output)以及 硬盘→程序读数据(Input),同时Java IO是基于流(Stream)的,所有的API都继承自
InputStream/OutputStream
。意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。 - IO的过程是阻塞的,也就是说当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。
然后我们再来看NIO:
NIO包括三个核心:
- Channels
FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel
这些通道涵盖了UDP 和 TCP 网络IO,以及文件IO。
Java NIO的通道类似流,但又有些不同:
1)流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类),而通道可以用于读、写或者同时用于读写,是全双工的。
2)通道可以异步地读写。
3)通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。
实际上Channel可以分为两大类,分别是用于网络读写的SelectableChannel
和用于文件操作的FileChannel
。
Netty中ServerSocketChannel和SocketChannel都是SelectableChannel的子类 。 - Buffers
所有数据都是用缓冲区进行处理的。在读取数据时,它是直接读到缓冲区中;在写入数据时,它也是写入到缓冲区中。任何时候访问 NIO 中的数据,我们都是通过缓冲区进行读写操作。
缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。
NIO中Buffer的实现包括ByteBuffer、CharBuffer、DoubleBuffer
等等,这些Buffer覆盖了你能通过IO发送的基本数据类型:byte, short, int, long, float, double 和 char。
- Selectors
Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。
Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。
Selector会不断的轮询注册在其上的Channel,如果某个Channel上面有新的TCP连接接入、读和写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合进行后续的IO操作。
所以,与IO相比,
- NIO是从缓冲区(Buffer)读写数据的,而不是面向流(Stream)。例如在读取硬盘数据时,并不是仅从一个InputStream上逐字节读取,而是数据必须先读入缓冲区再处理。
- NIO在一个线程从某通道发送请求读取数据,并不会保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
最后
抱歉这是一篇搬运后排版出来的文章,花了点时间整理了下Netty框架相关的知识。尤其是NIO这一块,虽然网上的言论都是不建议直接基于JDK的NIO类库进行开发,几乎都推荐使用Netty开发NIO服务端,但是了解NIO的一些核心概念,肯定有助于对Netty整个代码模式以及API使用的学习。这两天会再整理一下在Android客户端开发中Netty的具体使用。