二、java网络io编程(BIO、NIO)


网络编程的基本模型是c/s模型,即客户端和服务端进行通信。
服务端通过监听自身ip和绑定的端口提供服务,客服端通过制定的ip和端口发起请求,经过三次握手请求,确认连接成功,然后进行通信;而在Java发展的历程上来看主要分为三个模式
BIO:阻塞同步通信
NIO:非阻塞同步通信 
AIO:非阻塞异步通信
1.传统IO编程(同步阻塞模式):


由图可以看出来:(上图是引用别人的图)
每一个client的访问在服务端都会产生一个线程用来处理,并在处理完后再返回clent端,最后线程销毁
缺点就是在访问量大的时候服务端的线程数量会快速增加,系统的性能则会急剧下降,最后系统崩溃(线程是操作系统宝贵的资源,windows 线程上线大于在1000左右,而Linux上线大约在2000左右,所以在连接数量大的时候,系统死的会很快,即系统无法满足高并发,高性能应用)

BIO serverSocket端

ServerSocket端方法实现
(1).创建绑定监听端口的 SeverSocket对象,用于服务端和客户端之间请求链接
(2).server.accept()  程序阻塞等待客户端的访问并返回socket对象
(3).利用 accept()返回的socket  对象进行IO的读写操作(获取数据和发送数据)
(4).完成后关闭IO流 和  释放socket对象

  1. ServerSocket server = null;
  1. Integer PORT= 9999; 
  1.    try {
  1.      //1、绑定固定的端口
  1.       server = new ServerSocket(PROT);//
  1.       System.out.println(" server start .. ");
  1.       //2、进行阻塞(防止程序启动后直接执行到底,等待客户端的链接)
  1.       Socket socket = server.accept();
  1.       
  1.     //3、获取输入流,并读取客户端信息
  1.     InputStream is = socket.getInputStream();
  1.     InputStreamReader isr =newInputStreamReader(is);
  1.     BufferedReader br =newBufferedReader(isr);
  1.     String info =null;
  1.     while((info=br.readLine())!=null){
  1.     System.out.println("Hello,我是服务器,客户端说:"+info);
  1.     }
  1.     socket.shutdownInput();//关闭输入流
  1.     //4、获取输出流,响应客户端的请求
  1.     OutputStream os = socket.getOutputStream();
  1.     PrintWriter pw = new PrintWriter(os);
  1.     pw.write("Hello World!");
  1.     pw.flush(); 
  1.     } catch (Exception e) {
  1.       e.printStackTrace();
  1.     } finally {
  1.         if(server != null){
  1.            try {
  1.                server.close();
  1.            } catch (IOException e) {
  1.                 e.printStackTrace();
  1.            }
  1.      }
  1.        server = null;
  1.    }


clent端代码
(1).用服务器的IP地址和端口号实例化Socket对象。
(2).获得Socket上的流,把流封装进BufferedReader/PrintWriter的实例,以进行读写
(3).利用Socket提供的getInputStream和getOutputStream方法,通过IO流对象,向服务器发送数据流
(4).关闭打开的流和Socket。

  1.  Socket socket = null;
  1.     BufferedReader in = null;
  1.     PrintWriter out = null;
  1.    // 尽量使用try catch 模块执行
  1.     socket = new Socket("127.0.0.1", "9999");
  1.     in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  1.     out = new PrintWriter(socket.getOutputStream(), true);
  1.     
  1.     //向服务器端发送数据
  1.     out.println("接收到客户端的请求数据...");
  1.     out.println("接收到客户端的请求数据1111...");
  1.     String response = in.readLine();
  1.     System.out.println("Client: " + response);
  1.     //下面的关闭查找放在   finally 中,保证程序完整执行
  1.     in.close();
  1.     out.close();
  1.     socket.close();
为了解决这种情况 ,可以利用线程池来管理(限制线程最大请求数量,来减少对线程的消耗,但是连接超过最大数是只能等待有空余的线程可以复用的时候才能执行)




2、NIO编程(同步非阻塞模式)
NIO在表面上看来就是在请求和服务处理中间加了个缓冲区,即
请求数据是先将数据保存到缓冲区  然后传递个处理程序 ,处理程序再将返回值保存到缓冲区中,最后在传递给请求客户端

clent请求                ------>  |server--->   缓存buff----->   程序处理Handler
程序处理Handler    ------>  |server--->   缓存buff----->   clent请求 

其核心组件有三个
Channels
Buffers
Selectors

Channel :通道,是指客服端和服务端之间的连接通道,用来判断服务端和客户端的链接以及两者之间的数据流传输。
Buffers : 缓存,存在与服务端一个组件,用于缓存接收客户端发来的数据,或者缓存服务端处理完后要发往客户端的数据,当数据完全缓存到buffers 的时候在传递给业务逻辑处理,或者将数据传输给客户端。
Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。
dmo
Server端

  1. public class NIOService {
  1.     //绑定监听的端口
  1.     private static int DEFAULT_PORT = 12345;  
  1.     //单例的ServerSocket  
  1.     private static ServerSocket server;  
  1.     //线程池 懒汉式的单例  
  1.     private static ExecutorService executorService = Executors.newFixedThreadPool(60);  
  1.     //根据传入参数设置监听端口,如果没有参数调用以下方法并使用默认值  
  1.     public static void start() throws IOException{  
  1.         //使用默认值  
  1.         start(DEFAULT_PORT);  
  1.     }  
  1.     //使用同步synchroized 
  1.     public synchronized static void start(int port) throws IOException{  
  1.         if(server != null) return;  
  1.         try{  
  1.             //通过构造函数创建ServerSocket  
  1.             //如果端口合法且空闲,服务端就监听成功  
  1.             server = new ServerSocket(port);  
  1.             System.out.println("服务器已启动,监听端口为:" + port);  
  1.             Socket socket;  
  1.             //如果没有客户端接入,将阻塞在accept操作上。  
  1.             while(true){  
  1.                 socket = server.accept();  
  1.                 //当有新的客户端接入时,会执行下面的代码  
  1.                 //然后创建一个新的线程处理这条Socket链路  
  1.                 executorService.execute(new DoSothingHandler(socket));  
  1.             }  
  1.         }finally{  
  1.             //一些必要的清理工作  
  1.             if(server != null){  
  1.                 System.out.println("服务器已关闭。");  
  1.                 server.close();  
  1.                 server = null;  
  1.             }  
  1.         }  
  1.     }  
  1. }

clent端代码
  1. public class NClient {  
  1.     //定义检测SocketChannel的Selector对象  
  1.     private Selector selector=null;  
  1.     //定义处理编码和解码的字符集  
  1.     private Charset charset=Charset.forName("UTF-8");  
  1.     //客户端SocketChannel  
  1.     private SocketChannel sc=null;  
  1.     public void init() throws IOException{  
  1.         selector=Selector.open();  
  1.         InetSocketAddress isa=new InetSocketAddress("127.0.0.1",12345);  
  1.         //调用open静态方法创建连接到指定主机的SocketChannel  
  1.         sc=SocketChannel.open(isa);  
  1.         //设置该sc以非阻塞方式工作  
  1.         sc.configureBlocking(false);  
  1.         //将Socketchannel对象注册到指定Selector  
  1.         sc.register(selector, SelectionKey.OP_READ);  
  1.         //启动读取服务器端数据的线程  
  1.         new ClientThread().start();  
  1.         //创建键盘输入流  
  1.         Scanner scan=new Scanner(System.in);  
  1.         while(scan.hasNextLine()){  
  1.             //读取键盘输入  
  1.             String line=scan.nextLine();  
  1.             //将键盘输入的内容输出到SocketChannel中  
  1.             sc.write(charset.encode(line));  
  1.         }  
  1.     }  

未完待续(如有不足之处请指出)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值