通信协议
网络模型OSI(Open System Interconnnection)
- 该模型把把网络通信分为7层(物理层,数据链路层,网络层,传输层,会话层,表示层,应用层),是设计和描述计算机网络通信的基本架构。
- 在实际应用中一般只是用到4层。
TCP/IP网络模型
这个模型是一系列网络协议的总层,是互联网的基础
4个层次
- 链接层
- 对应的是物理层和数据链路层,主要负责监视数据在主机和网络之间的交换
- 主要协议
- 网络层
- 对应的是网络层,主要解决主机与主机之间的通信问题,所含网络通信协议设计数据包在网络行的逻辑传输。还负责数据包在多种网络之间的路由。
- 主要协议
- 网际协议(IP)
- 互联网管理协议(IGMP)
- 互联网控制报文协议(ICMP)
- 传输层
- 对应传输层,为应用实体提供端对端的通信功能,保证数据包的顺序传送和数据的完整性。
- 主要协议
- 应用层
- 对用会话层,表示层,应用层,提供用户所需的各种服务
- 主要协议
- FTP
- Telnet
- DNS
- SMTP
- HTTP
- Socket
这些通信协议,保证了端对端网络通信的流畅,常用的TCP和UDP协议是传输层协议,区别很大
1. 面向连接的TCP
一种基于连接的协议,在双方通信传输数据之前,必须建立可靠的连接。(如双方通话前必须把电话接通)
- 通过三次对话建立连接(三次”握手“)Client A,Server B
- 客户端A询问服务器B: 发送请求建立连接数据包
- 服务器B应答客户端A:返回同意或者拒绝建立连接的数据包
- 若服务器同意,客户端则发送建立连接数据包,拒绝则会话结束。
经过上述的三次握手,双方建立了一条隐形的通信通道,这样双方的连接就不会断开,从而进行收发信号。
- 特点
- 面向连接:通信前要三次”握手“建立连接。
- 安全可靠:每一次通信都必须得到对方的应答,否则认为数据报丢失,需要重发。
- 全双工通信:一旦建立连接,双方都可以通过通道进行数据传输。
- 一对一:通信只能建立在两个点之间
- 面向流通信: 通信传输是通过流的形式进行。
2. 面向数据报的UDP
无需建立连接,只要指定目标地址,即可通过UDP向目标地址发送数据报。
由于没有建立可靠的连接,不保证数据包可以送到目的地,所以数据报可能丢失。
比TCP可以发送更大的数据报,并进行一对多的广播发送。
- 特点:
- 无连接:通信之前无需建立可靠的连接
- 数据无保障: UDP不对数据排序,数据报文头部无报文顺序信息,而且无需按顺序到达,可能造成报文混乱。
- 开销小:无连接,不保证报文送达和报文顺序,开销较小,而且速度更快。
- 一对一,一对多,多对多:无需连接,可以进行一对一通信,也可以进行一对多的广播通信和多对多的通信。
UDP和TCP的比较和应用场景
- 大多数情况下,如登录,支付,上传等,都需要服务器返回具体的执行结果以及判断是否成功,需要TCP。
- 在电视直播中,如果每一个帧或者几个帧画面都要直播服务器确认,会使得画面卡顿和一直占用网络带宽,应使用UDP。
3.HTTP编程
超文本传输协议,是应用最广泛的网络协议,几乎所有的www 文件都要遵守这个协议的标准。最初是为了提供一种HTML页面发布和接收的方法。
在这里插入图片描述
工作原理
- 客户端请求服务器建立连接并发出请求数据
- 服务器接收请求并发出应答数据,服务器接收到并进行处理,处理之后就返回数据并断开连接。

拓展知识:
- URL和URN 是URI的子集, 最常用的是URL。
- URI : Universal Resource Identifier 统一资源标识符
- 在某一规则下能把一个资源独一无二地标志出来。(像人们的身份证号一样,确定一个唯一的人)
- Web上,假设所有的Html文档都有唯一的编号,记作html:xxxxx,xxxxx是一串数字,即Html文档的身份证号码,这个能唯一标识一个Html文档,那么这个号码就是一个URI。
- URI一般由三部组成
①访问资源的命名机制
②存放资源的主机名
③资源自身的名称,由路径表示,着重强调于资源。
- RUL :Universal Resource Locator 统一资源定位符
- 是一个地址,唯一确定一个资源(括文件、服务器的地址和目录等),在Web上通过描述是哪个主机上哪个路径上的文件来唯一确定一个资源,也就是定位的方式来实现的URI。
- URN : Universal Resource Name 统一资源名称
访问网站的范例
- 客户端在浏览器输入网址,按回车键,浏览器封装URL到HTTP请求体并发送请求包给Web服务器。
- Wub服务器接收请求后建立连接,将请求体中的head和body体中的信息经过服务器逻辑处理后,返回HTML页面给客户端。
- 客户端接收到服务器返回信息结构体,从中读取HTML代码,断开连接。
- 服务器通过浏览器解析页面并呈现在浏览器上。
使用Java的net包进行简单的HTTP请求
- 接收外界传入的目的网站
- 的网址字符串根据网址创建包含地址信息的URL对象
- 将URL强制类型转换获取HttpURLConnection对象
- 然后对对象进行连接信息设置
- 获取到输入流,用流对象读取服务器传过来的相应内容。
- 在对应答数据执行客户端的业务逻辑
标志的HTTP支持六种请求方法:GET,POST,HEAD,PUT,DELETE,OPTIONS,最常用的是GET和POST方法。
GET和POST
- 客户端
- GET请求默认HttpURLCnnection的设置
- 接收目的服务器的网络地址字符串
- 构造URL对象
- 设置为GET请求方式
- 发起连接
- 创建流对象并从通道中读取服务器的应答信息
- 执行业务逻辑
- POST请求
- 接收目的服务器的网络地址字符串
- 调用HttpURLConnection的setRequestMethod方法为POST,并将SetDoOutput为true,并通过write方法写入参数到body体。(因为消息POST消息必须通过body传递参数)
- 服务端
- 添加一个类,Server继承servlet,重写doGet 和 doPost 方法分别接收GET和POST,最后在web端添加此Servert的部署。
4,Socket 编程
-
socket(套接字)用于描述IP地址和端口,是个通信链的句柄,可以用来实现不同虚拟机之间或者不同计算机之间的通信。网络上的主机一般会运行多个服务器,每个服务器上的每一种服务都会打开一个Socket并绑定到一个端口,不同端口对应着不同的服务。
-
IP地址和端口号组成了Socket,Socket 是网络上运行的程序之间双向通信链路的终结点,是TCP和UDP协议的基础。
- IP对应着网络上的计算机,而端口对应着计算机上某个具体的进程或者服务。(就好像寄信一样,IP地址对应着具体的居民房,端口则对应于人名,根据地址和人名就能实现寄信)
-
根据连接启动的方式和本地Socket要连接的目标,Socket之间连接分为三个步骤:
- 服务器监听、
- 客户端请求
- 连接确认。
-
服务器监听
- 服务器端Socket只需要在程序启动之后处于等待连接的状态,并实时监控网络状态,等待其他客户端Socket连接。
-
客户端请求
客户端Socket需要先创建一个Socket,并在Socket中描述服务器Socket的信息(IP地址和端口号),然后向服务器Socket提出连接请求。
-
连接确认
* 当服务器Socket监听到或者接到客户端Socket的连接请求时,就会响应客户端Socket的请求,建立一个新的线程,把服务器Socket的描述发送给客户端,一旦客户端确认此描述,连接就建立好。从此这个线程就为该客户端服务。
* 服务器的Socket 继续处于监听状态(主线程),继续接收其他客户端的Socket的连接请求,接收其他客户端Socket的连接请求。

TCP服务端
- 设置独立存在的端口号,创建一个服务端Socket绑定端口并启动,进入等待状态等待用户连接。
- 堵塞方式accept()方法接收并读取客户端发来的流信息
- 从Socket的缓冲区中读取信息,读取完毕后,处理事务
- 将处理结果发送到Socket的缓冲区,再传送到客户端的Socket的缓冲区,等待用户读取。
- 释放Socket资源。
- 循环等待下一个连接
TCP客户端
- 客户端通过带有服务器IP和端口号的Socket类对象连接到服务器,建立连接。
- 发请求信息写入Socket缓存区,发送到服务器
- 再读取服务器的应答信息
- 关流,释放资源
UDP发送端
- 创建发送方(自己)的Socket对象,指定端口
- 构造数据包(发给指定主机上的指定端口号)
- 发送数据包给目的接收者
- 接收目的接收者返回的数据
- 释放资源
UDP接收端
- 创建接收方(自己)的Socket对象(该socket对象绑定了的当前进程的IP地址和端口)。
- 构造接收数据包,存储在字节数组
- receive()方法,堵塞等待接收数据包,直到接收到为止
- 获取接收到的数据包,从中获取发送者的各种信息(IP,消息文本,端口)
- 处理事务,构造应答数据包,指定目的接收者的socketaddress地址(告诉他接收完毕)
- 发送数据包
- 关闭资源。
WebSocket
- 是一种随着HTML5的兴起而出现的新协议,在它之前,只能通过HTTP实现 单通道通信,而WebSocket实现了浏览器与服务器之间的全双工通信,它的通信方式类似TCP的连接方式,先通过“握手”协议通信,然后再建立通信传输数据,可以实现服务器对客户端的消息推送。
- 在WebSocket之前,要在Web开发中实现即时通信,会采用轮询方式:在特定的时间间隔内不断发出HTTP请求来请求服务器的数据,然后服务端返回客户端请求时的最新数据给浏览器。但是每一个请求都有一个很长的header头部信息,很占用带宽。而WebSocket协议占用很小的header,并可以实现服务器主动推送。
服务端
重写OnOpen,OnClose,OnMessage三个方法是实现WebSocket服务端的建立连接,断开连接,消息接收功能,并在消息接收中实现自己的业务逻辑。也可以主动推送消息给客户端。
HTML5客户端
创建WebSocket类对象,同样重写OnOpen,OnClose,OnMessage三个方法是实现WebSocket客户端的建立连接,断开连接,消息接收功能。
总结:以上是HTTP,Socket,WebSocket 的思路和Java实现的理解,不过在实际项目中,要考虑很多问题,如HTTP响应超时,头部消息设置,Socket,WebSocket的心跳机制,断线重连机制,或者多线程下的网络编程等。
Java NIO基础
网络编程模型基础
- 同步
- 用户进程触发IO操作并等待或者轮询去查看IO操作是否就绪。(如发送方和接收方处于同一时钟,发送多少接收多少,协调合作。如果IO还没轮到当前进程使用,则当前进程一直等待IO的准备就绪)
- 异步
- 用户进程触发IO操作以后做其他操作,当IO操作完成后会得到IO完成的通知。
阻塞和非阻塞是进程在访问数据的时候,根据IO的操作的就绪状态来采取的不同方式,实际就是执行数据操作之后是否立即返回值。
- 阻塞
*用户进程访问数据时,如果尚未就绪,当前线程就一直等待数据。(如函数需要返回数据,但是数据还没读取完毕,阻塞直到将数据返回才执行其他的函数)
- 非阻塞
- 用户进程访问数据时,数据未准备就绪时,当前线程可以做其他事情,间隔一段时间检查数据是否准备就绪。(函数需要返回值,但是并不等待,继续往下执行以下的函数)
区别
- 同步和异步对应消息通知机制,指线程发起一个功能调用的时候是否立即返回结果,主要针对IO操作
- 阻塞和非阻塞对应等待消息通知的状态,指线程在得到调用结果前是否挂起,主要针对线程。
1,BIO编程(Blocking-IO 阻塞式IO)
BIO是同步阻塞型IO,在服务器的实现模式:每一个连接都要对应一个线程,当接收到客户端的请求时,服务器需要启动一个新的线程与之交互。
- BIO通信模式要使用连接数目比较少且固定的服务器框架,并要求服务器有较好的资源。
- 缺陷:当新的线程不做任何处理时处于挂起状态,会给服务器造成不必要的线程开销。

BIO经典模型 客户端-服务器模型
- 传统的网络编程中,服务器绑定IP并监听端口,客户端通过IP和端口与服务器建立连接,双方就可以通过Socket进行通信,ServerSocket 负责绑定IP地址并监听独立端口,Socket就发起连接请求,接收器负责接入请求,连接成功后双方就可以通信,服务器端创建一个新线程,处理客户端请求,再通过输出流返回给客户端,销毁线程,释放资源。
- 这种IO称为同步阻塞式IO,每当一个客户端接入,服务器就建立一个新线程与之交互,线程数与客户端数相同,当线程增多而高并发,整个BIO网络程序就会占用大量JVM线程,导正服务器性能下降,甚至跑出堆栈溢出的异常,最终程序崩溃。
- 典型应用就是TCP协议通信方式: 服务器通过accept() 方法实现阻塞,直到有客户端的请求到达。
2,NIO编程(Non-Blocking IO 非阻塞式IO)
特点
- 创建一个线程负责处理IO事件和IO事件的分发。
- 事件驱动机制非同步监听事件,而是事件到达后触发事件
- 线程之间通过wait,notify等方式通信,减少了线程切换。
NIO是同步非阻塞型IO,服务器实现模式为:一个请求一个线程,客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。适用于连接数目多且连接比较短(轻操作)的架构(如聊天服务器),并发限于局部,编程复杂。
原理
- 多个客户端向服务器发送连接请求,服务端将请求注册到Rector多路复用器,它查询到有IO请求时将启动一个线程,将其分发到该线程处理。每个线程的处理流程都相同:读取数据,对数据编码,进行计算处理,处理后将响应消息编码,将其发送出去给客户端。


- NIO服 务端只需启动一个专门的线程来处理所有的IO事件,采用双向通道(channel)进行数据传输,在通道上注册感兴趣事件(服务端接收客户端连接事件,客户端连接服务端事件,读事件,写事件),即事件驱动机制。
- NIO的服务端和客户端都需要维护一个管理通道的对象(selector),该对象能检测到一个或者多个channel的事件,服务器的selector上注册了事件,当某时刻,客户端给服务器发送一些数据,阻塞IO会调用read()方法阻塞地读取数据,而NIO的服务端会在selector中添加一个读事件。添加事件后,服务端的处理线程会轮询访问selector(遍历是否有事件触发),如果访问时发现有感兴趣的事件到达,则触发事件,没有事件到达,处理线程就一直阻塞到感兴趣的事件到达为止。
服务端
主方法:(1)创建服务器对象(2)初始化服务器,(3)监听端口
1,初始化服务器方法:
- 获取一个通道,设置为非阻塞,
- 将通道对应的ServerSocket绑定端口
- 获取一个通道管理器,为通道注册接收用户请求的事件(当事件到达时,listen()中的selector.select()会返回,否则它会一直等待。)
2,监听方法:轮询一个selector,如果有事件到达则立即处理
- while(true)实现阻塞,直到有事件到达才返回
- 每一次轮询selector,获取它中的选中的项(注册的事件)的迭代器
- 当有请求事件到达则获取和客户端连接的通道,给客户端发信息,并为通道设置读权限
3,服务器读取方法
- 获取事件发生的Socket通道
- 创建读取的缓冲区
- 从通道中读取数据到缓冲区字符串,并将其转换为字节数组
- 处理数据后,把应答信息写回通道,,回送客户端。
客户端
主方法:创建客户端对象,初始化对象,监听端口
1, 初始化方法
- 获取Socket通道,并设置为非阻塞
- 获取通道管理器
- 客户端连接服务器(完成一部分,要在listen才能完全完成)
- 将管理器绑定通道并注册连接服务器事件
2,轮询监听通道管理器方法:
- while(true)循环中
- 调用select()方法选择一组I/O操作的事件置于管理器(该方法不阻塞,当一个通道被选中时,seletor的wakeup方法被调用而返回,对于客户端,通道一直都是被选中的)
- 获取通道管理器的迭代器,遍历每个注册的事件,看是否有事件到达
- 有连接事件则获取通道并设置非阻塞,向服务器发送消息
- 连接成功后,赋予通道可读权限,读取服务器返回的数据
3,读取服务器信息方法
- 获取通道对象,读取数据到缓冲区
- 处理信息并将应答信息写入通道中。
3.2.3AIO编程(Async IO/NIO.2,异步IO)
异步阻塞型IO,在服务端的实现方法通常是一个有效的请求对应一个线程,来自客户端的I/O请求都是由OS先完成再通知服务器应用去启动线程进行处理,使用于连接数据较多和比较长的架构。如相册服务器,充分调用OS参与并发操作。
服务端类

主方法:创建服务器对象,调用listen方法。
- 1,构造方法
- 2,listen()方法
- 打开一个服务通道,绑定服务端口
- 通道对象通过accept()方法绑定一个消息处理类
- 创建新线程打印消息
- 启动新线程
- 3,accept到一个请求时的回调类
- completed ()连接并读取客户端信息
- 调用read()函数,调用请求数据的回调类
- 递归调用,监听新的请求
- 4,Read到请求数据的回调类
- 构造函数
- completed()方法,读取请求,处理数据,响应结果
- write()方法,调用响应完请求的回调类
- 5,Write响应完请求的回调类
- 构造函数
- completed()方法,清理缓冲区,关闭服务端
- fail() ,失败后关闭服务端
- 6,close()方法 ,关闭通道
客户端
使用TCP的客户端
- 总结:服务端启动之后,没有阻塞等待客户端的连接,而是做其他事情;服务端也不用等待IO操作完成再去执行其他操作,只需要IO操作交给操作系统,然后去其他操作,等IO操作完了,系统会回调通知结果,大大节省了系统的内存时间。
Mina
Apache Mina时基于Java NIO 的网络应用框架,底层基于TCP/IP和UDP/IP的,是一款协议栈的通信框架,可以快速开发高性能,高扩展性的网络应用。
Mina提供了事件驱动和 异步操作的模型,IO默认是java NIO 作为底层支持。
总体架构

IoService
- Mina的最底层,负责底层的IO的相关工作。如IoSocketAcceptor和IoSocketChannel ,对应着TCP下的服务端与客户端的IoService(隐藏了所有底层的细节,只对上层提供统一的基于事件驱动的异IO接口)。
- 当有数据到达时,IoService 会调用底层的IO接口读取数据并封装为IoBuffer,然后再以事件的形式通知上层代码,将Java NIO 的同步IO接口转换为异步IO。
IoFilterChain
- 代码分离是Mina的设计之一,Mina的业务代码和数据处理分别负责不同的内容。
- 开发者的业务代码只需要专注于业务逻辑,而他无关的逻辑(数据包的解析,过滤,封装等),交给IoFilterChain处理。
- FilterChain 是其处理流程的扩展点,使得Mina的结构划分更清晰,代码分工明确。开发者要增加处理流程而不不影响后续的业务逻辑代码,只需要向Chain添加IoFilter。
IoHandler
InHandler实现业务逻辑,开发时,开发者需要自己实现IoHandler接口,是Mina处理流程的终点,每个IoService都要指定一个IoHandler。
IoSession
- Mina底层的连接封装成IoSession,每个IoSession对应一个客户端与服务端的底层IO连接。开发者需要管理好每一个客户端的IoSession,通过它可以获取连接的相关信息,向客户端发送数据。
- 通过IoSession发送数据是一个异步过程,发送操作首先通过IoFilterChain到达 IoService,然后IoService将发送操作封装成WriteRequest,并放入Session的writeRequest中,最后交给IoProcessor统一调度flush发送出去,整个发送操作不会引起调用线程阻塞。
工作原理

Acceptor与Connector线程
- 服务端绑定端口之后,Mina就创建Acceptor线程,专门负责监听,这个线程的工作就是调用 java NIO 的 select connect 事件 ,在获取新建立的连接之后,将去封装成IoSession,并交给IoProcessor线程处理。
- 在客户端会有一个对应的Connector线程,两个线程是一对一关系。
- 一旦建立,外界则无法控制其线程的数量,直到连接断开。
Processor线程
- Processor 线程主要负责IO读写操作与执行IoFilterChain和IoHandler逻辑,它默认会有CPU数量+1个线程,并且这个数量可以通过配置参数进行控制,每一个进来的IoSession都会分配到这些线程中,默认策略是session id 绝对值对 N 取模 来分配。
- Processor线程维护一个selector,并对维护着的IoSession 进行select 和遍历,然后读取数据,以事件的形式通知IoFilterChain,并对请求队列进行flush操作。
- 把IoSession均分到多个Processor线程中进行处理,可以充分利用计算机多核的处理能力,减轻select操作的压力。虽然默认的Processor的线程数量足够满足大部分情况下的需求,实际项目中需要根据实际线上环境修改。
线程模型
- 每一个Processor的线程内部看,IO请求的处理是按顺序处理中,即按照单线程进行处理。
- 当Processor线程select到一批就绪的IO请求后,就会在线程内部遍历并对这些请求一一处理(处理流程包括IoFilter和IoHandler的逻辑)
- 当就绪IO前面的IO请求处理完毕,才会取出下一个请求进行处理。当IoFilter和IoHandler的逻辑有耗时操作时,Processor会被阻塞,后续的请求将不会处理。高并发的Mina使用线程池解决这个问题。
线程三种模型
-
单线程模型
Mina默认线程模型,Processor处理了从底层IO到上层IoHander逻辑所执行的所有工作,这种模式适用逻辑不复杂且快速返回的情况。
-
单线程池模型:
在IoFilterChain中加入Thread Pool Filter ,当Processor线程读取数据之后,执行IoFilterChain逻辑,当进行到Thread Pool Filter的时候,此Filter将后续处理封装至Runnable中,并交给Filter自身的线程池执行,Processor则立即返回处理下一个IO请求。
如果在IoFilter或者IoHandler中出现了阻塞操作,只会让执行此操作的Filter阻塞,而不会阻塞负责读取数据的Processor线程,从而提高服务器的并发及处理能力。
Mina 提供 Thread Pool Filter 的实现 : ExecutorFilter
-
多线程池线程模型
多线程模型是在上面的两种模型的基础上,将单个Thread Pool Filter 变成多个Thread Pool Filter。
请求的处理顺序
加入线程池,可以提高服务器的并发吞吐量,但是也存在请求处理顺序的问题。
- 在单线程模型中,多个IO操作是可以按顺序依次进行处理的,但加入线程池之后,同一个IoSession的多个请求可能被ExecutorFilter进行并行处理。实际开发中也会有对请求处理有顺序要求饿IO请求(如数据库的顺序操作)
- 在Mina中,默认的实现是保证同一个IoSession中的IO请求的顺序的,ExecutorFilter默认采用Mina提供的OrderedThreadPoolExectutor作为其内置的线程池。线程池从Runnable对象中获取关联的IoSession信息,并将其Runnable加入Session的任务列表中,而不是立即执行加入的Runnable对象。线程池的OrderedThreadPoolExectutor
就会按照session的任务列表处理请求,保证请求的执行顺序。
- 没有执行顺序要求的IO请求,可以为ExecutorFilter指定一个Executor来替代默认的的OrderedThreadPoolExectutor,实现多个session被并行处理。
Mina编程实现

服务端
初始init()方法
- 创建acceptor对象
- 设置解析器IoFilterChain,并设置其读取数据方式
- 绑定处理器Handler
- 绑定端口
- 创建Acceptor线程专门负责监听。
Mina服务端的消息处理类
继承IoHandler有关的类
- 接收消息处理方法
- 捕获异常处理方法
- 发送信息处理方法
- 关闭资源处理方法
- 创建任务处理方法
- 开启任务处理方法
等
客户端
运行客户端方法Run()
- 创建连接线程对象 connector
- 创建接收数据的过滤器IiFilterChain对象,并设置读取信息方法
- 设置服务器的消息处理器
- 连接到服务器
- 发送数据
客户端处理类
继承IoHandler有关类
- 构造方法
- 异常处理方法
- 接收信息方法
- 发送信息方法
- 关闭客户端 方法
- 创建客户端方法
- 接入客户端方法
总结
-
Mina服务端的实现
- 实例化SocketAcceptor
- 设置解析器
- 绑定处理器Handler(重写需要的方法进行相应的逻辑处理)
- 绑定端口
-
Mina客户端开发
- 创建NioSocketConnector对象
- 设置过滤器和处理器Handler
- 调用connect方法建立连接
- 在Handler处理相应的业务逻辑
可调用IoSession的write方法发送消息
Mina框架总结
服务端接收数据
- 服务端启动,创建Acceptor线程,监听端口,select connect事件,当获取新建的连接后将其封装成IoSession,并交给IoProcessor线程处理。
- Processor处理IoSession,维护到一个selector对其Session进行select和遍历。
- IoService 负责Mina底层的IO的相关操作,它调用底层IO接口读取数据,并封装成IoBuffer,以事件的形式通知IoFilterChain对数据进行解析处理。对请求的Filter队列flush操作。
- 数据解析后,交给IoHandler去实现业务逻辑。
服务端发送数据
- 业务逻辑进行发送操作
- 发送操作首先通过IoFilterChain到达IoService
- IoService将发送操作封装成WriteRequest,放入Session的writeRequestQueue中
- 交给IoProcessor统一调度flush发送出去。
Netty框架
一款提供异步事件驱动的网络应用框架,主要用于开发高效,可靠的网络程序或客户端程序。它实现了网络应用开发的简单化与流线化,如TCP或UDP的Socket开发。吸收了多种协议的经验,经过精细的设计,形成一套既易开发又保证性能。

零拷贝
- Netty的接收和发送采用ByteBuffer,ByteBuffer采用Direct Buffers(即ByteBuffer直接使用堆外内存进行Socket读写,而不用进行字节缓冲区的二次拷贝)。相比传统的使用堆内存(Heap Buffers)的Socket读写,JVM再拷贝一份Buffer到内存中,然后再写入Socket,会少一次缓冲区的内存拷贝。
- Netty的组合Buffer对象,能聚合多个ByteBuffer对象,用户如果要操作多个Buffer,可以先把这些Buffer组合,然后操作这个组合Buffer,而传统的操作多个Buffer则只能进行内存拷贝成一个大的Buffer,更省内存。
- 采用transferTo进行文件传输,可以直接把文件缓冲区的数据发送到目标的Channel,从而避免的传统方式通过循环write导致的内存拷贝问题。
- NEtty使用自建的Buufer API,来表示一个连续的字节序列
- Netty新的BUffer类型ChannelBuffer被设计从底层解决ByteBuffer内存问题,可满足大多数 网络应用中需要的缓冲类型,并且ByteBuffer允许自定义缓冲类型。
- ByteBufffer还可以通过内置的缓冲类型实现同透明的零拷贝,并提供开箱即用的动态缓冲类型,容量还可以根据需求进行扩充。
- Netty的自建ByteBuffer通常比Java NIO 的 ByteBuffer更快。
Codec框架
对应请求的编解码,Netty中封装了很多实用的Codec框架。
- 1,FrameDecoder:
- 通过维护DynamicChannelBuffer存储接收的数据,通过抽象的模板(内部已经写好了整个解码过程),使用它只需要在子类实现decode方法,
- 它直接的实现类有DelimiterBasedFrameDecoder(基于分隔符(\r\n)的解码器,可以在自己的构造方法指定自己的分隔符) 和 LengthFieldBaseFrameDecoder(基于长度字段的解码器)。
- 2,ReplayingDecoder
- ReplayingDecoder是FrameDecoder的非阻塞解码,如果读到的数据不完整,使用ReplayingDecoder就可以假设读到了全部的数据。
- 3,ObjectEncoder 和 ObjectDecoder
- 4,HttpRequestEncoder和HttpRequestDecoder
- Netty中的还能实现 Http服务器,通过HttpRequestEncoder 和HttpRequestDecoder能实现HTTP请求和响应的编解码。
Channel
Channel在Netty实现的功能:
(1)Channel记录了当前的状态信息,即Channel为打开或关闭。
(2)通过ChannelConfig得到Channel的配置信息。
(3)Channel支持read,write,bind,connect等操作。
(4)通过Channel可以得到处理该Channel的ChannelPipeline。
Netty通过NioServerSocketChannel 封装ServerSocketChannel, ServerSocketChannel封装SocketChannel,实现了Java NIO 中的Channel的功能。
ChannelEvent
Netty的最大特色-事件驱动主要通过ChannelEvent实现。通过它,Netty可以确定事件流的方向,ChannelEvent依赖Channel的ChannelPipeline调用Channel Handler进行处理,在ChannelHandler中使用继承了ChannelEvent的MessageEvent,并调用它的getMessage()方法获得读到的ChanelBuffer或者已经被转化的对象。
ChannelPipeline
Netty控制事件流是通过ChannelPipeline处理,通过调用在ChannelPipeline中注册的一系列ChannelHandler来处理事件,这其实是很典型的拦截器模式。事件流分为Upstream和DownStream事件(上行流和下行流)。在ChannelPipeline中,被注册的ChannelHandler就是ChannelUpstreamHandler或ChannelDownstreamHandler,在ChannelPipeline的传递过程中,事件只会调用匹配流的ChannelHandler。
ChannelUpstreamHandler或ChannelDownstreamHandler在事件流的过滤器链中既可以终止整个流程,也可以通过调用ChannelHandlerContext.sentUpstream(ChannelEvent)或者ChannelHandlerContext.sentDownstream(ChannelEvent),将当前事件继续传递下去。
Upstream Event 是被UpStreamHandler从下往上依次处理的,而Downstream Event是被DownstreamHandler从上往下依次处理的,这种上下关系实质上就是在初始化的时候,往ChannelPipeline里添加的Handler的先后顺序。也就是说,Upstream Event就是请求外部请求的过程,而Downstream Event用于处理服务器向外发送请求的过程。
Netty编程
- 服务端实现
- 创建Netty的服务端,首先需要创建一个boss 线程组和一个work线程组。boss线程组用于接收客户端的连接,而work线程组用于连接消息的处理,然后创建ServerBootstrap来绑定线程组,绑定ServerChannel(通过NioServerSocektChannel实现),设置option和绑定ChannelHandler之(后,再调用bind绑定端口,完成服务端的创建,ChannelHandler中根据需求重写父类方法来
- 服务端的消息处理类
- channelRead()
- write()
- disconnect()
- channelAcitve()
- exceptionCaught()
- 客户端
- 通过Bootstrap来绑定ServerChannel,设置option和绑定ChannelHandler之后调用connect方法与服务器建立异步连接。在Netty中通过调用Channel的write方法可发送消息。
- 客户端的消息处理类
- channelRead()
- write()
- disconnect()
- channelActive()
- exceptiionCaught(
总结
介绍了Java网络编程相关知识,TCP,UDP,HTTP,Socket,WebSocket等协议的理论,特点,BIO,NIO,AIO等网络通信模型的原理,最后Mina和Netty网络通信框架,它们都是同一人设计,只是简单了解主要特征,现在很多分布式框架(如阿里Dubbo的底层)基于高效稳定的Netty。