一、Netty介绍
Netty是由JBOSS提供的开源框架,是一个异步、基于事件驱动的网络应用框架,针对TCP协议下面向客户端的高并发应用、或Peer-To-Peer 场景下的大量数据传输的应用
二、IO模型
1、三种IO模型
就是用什么样的通道进行数据的发送和接收。 Java共支持三种网络编程模型I/O模式:BIO、NIO和AIO
- 1)BIO(Blocking IO):同步阻塞,传统模式,服务器端实现模式为一个链接一个线程,即客户端有连接请求时,服务器端就需要启动一个线程进行处理,如果这个连接不做任何事就会造成不必要的线程开销,可以通过线程池机制改进,实现多个客户连接服务器;适用于连接数目较小且固定的框架,这种方式对服务器资源要求比较高,并发局限于应用中,多出现于JDK1.4以前的版本,但程序简单易理解。
- 2)NIO:同步非阻塞,服务器端实现一个线程处理多个连接请求,即客户端发送的连接都会注册到多路复用器上,多路复用器轮询所有连接有I/O的请求进行处理,适用于连接数目多且连接较短的架构,比如聊天服务器、弹幕系统,服务器间通讯等;但编程比较复杂,需要JDK1.4以上版本的支持。
- 3)AIO:异步非阻塞,引入了异步通道的概念,采用了Proactor模式,简化程序编写,有效的请求才启动线程,其特点为先由操作系统完成后才通知服务端程序启动线程去处理,一般用于连接数较多且连接时间较长的架构,如相册服务器,充分调用OS参与并发操作,编程比较复杂,需要JDK7以上版本的支持。
2、BIO工作机制
1)服务器启动ServerSocket 2)客户端启动ClientSocket,与服务器进行通讯,默认情况下服务器端需要对每个客户建立一个线程与之通讯 3)客户端发出请求后,先咨询是否有线程响应,若没有则会等待(阻塞)或被拒绝 4)如果线程有响应,客户端线程会等待(阻塞)请求结束后才继续执行 3、BIO示例 1)先创建一个线程池 2)如果有客户链接则创建线程与之通讯
public class Server {
public static void main(String[] args) throws Exception{
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
ServerSocket serverSocket = new ServerSocket(8001);
System.out.println("Server Starting-----");
while (true){
Socket accept = serverSocket.accept();
System.out.println("连接到一个客户端");
newCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
handler(accept);
}
});
}
}
public static void handler(Socket socket){
try{
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();
while (true){
System.out.println("线程 ID:" + Thread.currentThread().getId() +
"名字:" + Thread.currentThread().getName());
int read = inputStream.read(bytes);
if(read!=-1){
System.out.println(new String(bytes, 0, read));
}else{
break;
}
}
}catch (Exception e){
System.out.println(e.getMessage());
}finally{
System.out.println("关闭和client的连接");
try {
socket.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
}
使用 telnet 127.0.0.1 8001 ,然后 Ctrl+] ,使用 send message 就可以向服务端发送数据。
Server Starting-----
连接到一个客户端
线程 ID:11名字:pool-1-thread-1
hello 100
线程 ID:11名字:pool-1-thread-1
连接到一个客户端
线程 ID:12名字:pool-1-thread-2
hello 200
线程 ID:12名字:pool-1-thread-2
三、NIO
Java NIO,即 non-blocking IO,JDK1.4以后开始支持,位于java.nio包下,有三大核心部件:Channel、Buffer 和 Selector;NIO 是面向缓冲区或数据块的编程,将数据先读取到一个缓冲区中,然后处理线程从缓冲区读取数据进行处理,实现非阻塞。Channel 对应一个buffer,用于支持读写。NIO 实现了一对多的连接处理,使得可以支持更多的客户端连接请求
selector会优先选取有IO请求的活跃的客户连接(通道)。
1、Buffer
1.1、Buffer 的四个重要属性
Buffer 有四个重要的属性:Capacity、Limit、Position 和 Mark
- 1)Capacity:容量,在创建缓冲区时指定,并且之后不能更改,类似于静态数组。
- 2)Limit:标识缓冲区的终点,不能对缓冲区超过极限的位置进行读写操作,允许进行修改
- 3)Position:位置,写一个要被读或写的元素的索引,每次读写后数值会发生改变
- 4)Mark 标记,调用Mark 来设置 position,在调用reset 可以让position恢复原来的位置
1.2、Buffer Demo
public class BufferTest {
public static void main(String[] args) {
IntBuffer intBuffer = IntBuffer.allocate(5);
for(int i=0; i<intBuffer.capacity(); i++){
intBuffer.put(i*2);
}
intBuffer.flip();
while (intBuffer.hasRemaining()){
System.out.println(intBuffer.get());
}
}
}
1.3 NIO VS BIO
- 1)BIO 以流的方式处理数据,NIO 则以数据块为单位进行处理,从而 NIO 的效率更高
- 2)BIO 是阻塞的,NIO 是非阻塞的
- 3)NIO 基于字节流和字符流进行操作,NIO 基于Channel 即通道和 Buffer 即缓冲区进行操作,数据总是从通道读取到缓冲区中,或相反,从缓冲区写入到通道中,Selector 用于监听多个通道中的事件,从而实现单个线程可以处理多个客户连接请求
2、Selector、Channel和Buffer之间的关系
- 1)channel 和 buffer 一一对应
- 2)selector 对应多个线程,一个线程对应多个channel
- 3)程序切换到哪个Channel 由事件决定
- 4)Selector 会根据不同的事件在不同的通道上进行切换
- 5)Buffer 本质就是一个内存块(数组)
- 6)数据的读取是通过Buffer进行,读写的切换需要使用flip进行操作
- 7)Chann 也是双向非阻塞通道,可以反映底层操作系统的情况,比如Linux 底层的操作系统通道(epoll)就是双向的
|