网络模型(Java中的IO模型)

一、IO读写原理

  1. 文件的读写还是socket读写,再Java应用层开发,都是 input或者 output处理。用户程序进行的IO读写,会用到 read&write(内核态) 两大系统调用。
    read:将数据从内核缓冲区复制到进程缓冲区
    write:把数据从进程缓冲区复制到内核缓冲区
  2. 内核缓冲区 和 进程缓存区
    用户进程(N个):处于用户态(用户空间)
    系统空间:内核态。在用户态需要访问系统资源,需要借助内核态,系统资源主要有:
    1)CPU:控制一个程序的执行
    2)输入输出:一切都是流,所有流都需要借助内核态
    3)进程管理:进程创建、销毁、阻塞、唤醒之间的调度
    4)内存:内存的申请、释放
    5)进程间通信:进程之间不能相互访问内存,所以进程
    以上所提到的系统资源,再用户进程中是无法被直接访问的,只有通过操作系统来访问,所以把操作系统访问这一功能称之为系统调用
  • 缓冲区的目的,是为了减少频繁的系统IO调用
    系统调用需要从用户态切换到内核态,切换之后保存用户进程的数据和状态等信息。结束调用之后会需要恢复之前的信息,为了减少这种损耗的时间,还有损耗性能的时间,所以出现了缓冲区。
  • 有了缓冲区,操作系统使用 read从内核缓冲区复制到进程缓冲区write从进程缓冲区复制到内核缓冲区,只有缓冲区中的数据达到一定的量,再进行IO的系统调用,提升了系统的性能。
  • Java IO读写底层的流程图:
    在这里插入图片描述

二、四种主要的IO模型

概念

  • 阻塞IO:需要内核IO操作彻底完成之后,才返回到用户空间,执行用户的操作
    非阻塞IO:不需要等待内核IO操作彻底完成之后,才返回到用户空间
    阻塞/非阻塞 指的是用户空间程序的执行状态
  • 同步IO:是用户空间线程和内核空间线程的交互,用户空间线程是主动发起IO请求的一方,内核空间线程指的是被动接收的一方
    异步IO:与上面相反

1.同步阻塞 BIO

  • jdk1.4之前,IO模型都采用的BIO模型。
    需要先在服务器端启动一个ServerSocket,然后在客户端启动Socket与服务器端进行通信,服务器端调用accept方法来接收客户端的连接请求,一旦接收上连接请求,就可以建立套接字,在这个套接字上进行读写操作,此时不能再接收其他客户端的连接请求,只能等待当前连接的客户端的执行操作完成。
  • JDK1.4开始,出现同步阻塞式IO
    处理多个客户端请求,使用多线程
    在这里插入图片描述
    优点:用户线程阻塞等待数据期间,不会占用CPU资源
    缺点BIO模型在高并发的场景下是不可用的

BIO编程

  • 服务器端
    1)创建ServerSocket实例
    2)绑定端口
    3)通过accept方法监听客户端的连接,有客户端连接会返回socket实例
    4)进行读写操作
    5)关闭资源
  • 服务器端如何设计为可处理很多客户端的连接
    主线程负责接收客户端连接,子线程负责和客户端交互
    代码实现:
//子线程
class HandlerThread extends Thread{                                                                          
    private Socket socket;                                                                                                                                                                                            
    public HandlerThread(Socket socket){                                                                     
        this.socket = socket;                                                                                
    }                                                                                                                                                                                                                  
    @Override                                                                                                
    public void run() {//重写run方法                                                                                      
        try {                                                                                                
            //读取客户端的请求信息                                                                                     
            OutputStream output = null;                                                                      
            BufferedReader reader = null;                                                                    
            while(true){                                                                                     
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));                 
                String msg = reader.readLine();                                                              
                System.out.println("客户端:"+socket.getRemoteSocketAddress()+",发送的信息为:"+msg);                   
                //给客户端回复                                                                                     
                output = socket.getOutputStream();//获取输出流                                                           
                //控制台读取数据                                                                                    
                reader = new BufferedReader(new InputStreamReader(System.in));                               
                String info = reader.readLine();                                                             
                output.write((info+"\n").getBytes());                                                        
                output.flush();                                                                              
                if("".equals(info) || "exit".equals(info)) break;                                            
            }   
            //关闭资源                                                                                             
            reader.close();                                                                                  
            output.close();                                                                                  
        } catch (IOException e) {                                                                            
            e.printStackTrace();                                                                             
        } finally {                                                                                          
            try {                                                                                            
                System.out.println("客户端:"+socket.getRemoteSocketAddress()+"关闭了");                            
                socket.close();                                                                              
            } catch (IOException e) {                                                                        
                e.printStackTrace();                                                                         
            }                                                                                                
        }                                                                                                    
    }                                                                                                        
}     
//主线程                                                                                                       
public class MyBIOServer {                                                                                   
    public static void main(String[] args) {                                                                 
        ServerSocket ssocket = null;                                                                         
        try {                                                                                                
            //创建ServerSocket实例                                                                               
            ssocket = new ServerSocket();                                                                    
            //绑定端口                                                                                           
            ssocket.bind(new InetSocketAddress(9999));                                                       
            System.out.println("服务器端启动了...");                                                                                                                                                                
            while(true){                                                                                     
                //通过accept方法监听客户端的连接,有客户端连接会返回socket实例                                                       
                Socket socket = ssocket.accept(); //这个方法是一个阻塞方法                                              
                System.out.println("有新的客户端连接:"+socket.getRemoteSocketAddress());                             
                //将socket实例交给子线程去处理                                                                          
                new HandlerThread(socket).start();                                                           
            }                                                                                                                                                                            
        } catch (IOException e) {                                                                            
            e.printStackTrace();                                                                             
        } finally {                                                                                          
            if(ssocket != null){                                                                             
                try {                                                                                        
                    ssocket.close();                                                                         
                } catch (IOException e) {                                                                    
                    e.printStackTrace();                                                                     
                }                                                                                            
            }                                                                                                
        }                                                                                                    
    }                                                                                                        
}                                                                                                            
  • 客户端
    1)创建socket实例
    2)通过connect去指定服务器端的IP和端口
    3)进行读写操作
    4)关闭资源
    代码实现:
public class MyBIOClient {
    public static void main(String[] args) {
        Socket socket = null;
        try {
            //创建Socket实例
            socket = new Socket();
            //连接服务器端,通过connect绑定IP地址和端口号
            socket.connect(new InetSocketAddress("127.0.0.1", 9999));
            System.out.println("客户端启动了...");
            //进行读写操作
            while(true){
                //写操作
                OutputStream output = socket.getOutputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                output.write((reader.readLine()+"\n").getBytes());
                output.flush();
                //读操作
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String msg = reader.readLine();
                System.out.println("服务器响应信息为:"+msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

2.同步非阻塞 NIO

NIO的内容在NIO模型中具体描述。

3.IO多路复用

  • 通过一个新的系统调用 select/epoll 系统调用,实现一个进程监视多个文件描述符(唯一ID)。一旦某个描述符就绪(内核缓冲区可读/可写),内核能够通知用户程序进行IO系统调用。
  • 线程为例:单个线程不断的轮询 select/epoll 系统调用 所负责多个socket连接,当某个socket连接有数据到达,就返回这些可以读写的连接。
    在这里插入图片描述
  • 特点
    与NIO模型类似,多路复用IO需要轮询负责select/epoll查询调用的线程,查找出可以进行IO操作的一个连接,对于每一个可以查询的socket,一般需要设置为non-blocking。
  • 优点
    select/epoll 可以同时处理成百上千的连接,与之前的一个线程维护一个连接相比,IO多路复用则不需要创建线程,也就不需要维护,从而减少系统开销
  • 缺点
    select/epoll系统调用,属于阻塞的模式,同步IO。读写事件就绪之后,用户自己进行读写,这个读写过程也是阻塞的

4.异步非阻塞 AIO

  • 用户线程发起系统调用,告知内核启动某个IO操作,用户线程直接返回。内核在整个IO操作(数据准备,数据复制完成之后,调用回调函数通知用户程序(已就绪),用户执行后续的业务操作。
    在这里插入图片描述
  • 特点
    分为两个等待过程,等待数据就绪,等待数据拷贝,而在这两个等待过程中用户线程都不是block的,等这两个操作完成之后,用户线程就会收到一个信号。所以AIO又称之为信号驱动IO。
  • 缺点
    需要事件的注册,就需要操作系统,对系统资源和内存资源消耗大。
  • java.nio.channels包:
    AsynchronousServerSocketChannel 服务器数据通道
    AsynchronousSocketChannel 客户端通道
    AsynchronousDatagramChannel 基于UDP的通道
    AsynchronousFIleChannel 操作文件
  • 异步非阻塞,服务器端实现一个一个线程,异步执行当前客户端请求,客户端的请求都是操作系统完成再去通知服务器端处理,所以这个AIO主要用于连接数多同时请求比较复杂的系统架构。

BIO NIO AIO三个网络模型的适用场景:

模型适用场景
BIO连接数目少的系统架构,同时也需要服务器资源充足,虽然效率不高,但是程序…
NIO连接数目比较多以及连接时间短的系统架构,例如聊天服务器,编程比较复杂
AIO连接数目比较多以及连接时间长的系统架构,充分调用操作系统参与并发,不管是业务逻辑,还是并发逻辑,都需要操作系统的支持,所以不同的操作系统,性能也是不同的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值