一个人至少拥有一个梦想,有一个理由去坚强

心若没有栖息的地方,到哪里都是在流浪

NIO入门(一)

了解NIO前,先要了解传统的BIO和伪异步IO编程

一、传统的BIO编程

网络编程的基本模型是Clent/Server模型。也就是两个进程进行通信,客户端像服务器端发送请求,通过三次握手建立连接,双方确认连接后,通过网络套接字(Socket)进行通信。

关于三次握手,可通过下图简单了解,想要深入了解的请自行找度娘。

《NIO入门(一)》

《NIO入门(一)》

简单说就是:客户端通过服务器端发送请求,服务器端接收到请求后要给客户端通知说已经收到请求了,客户端收到服务器端的确认信息后再次像服务器端发送请求,表明已经收到消息知道客户端连接到了。接下来才进行互相通信。

在基于传统同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口,Socket负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞通信。

一)BIO通信模型图

《NIO入门(一)》

这就是典型的一请求一应答的方式,一客户端一线程。采用BIO通信模型的服务器,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端的连接请求后为每一个客户端创建一个新的线程进行链路处理,处理完成后,通过输出流返回响应给客户端。

请求量大,并发高的情况存在如下问题:

因为服务器端线程个数和服务器端并发访问个数是1:1的关系,线程是java虚拟机非常宝贵的资源,当线程膨胀后,系统的性能逐渐下降,随着并发访问量加大,会发生线程堆栈溢出,最终导致进程宕机或者僵死,不能对外提供服务。

二)同步阻塞I/O的TimeServer代码

public class TimeServer {

  public static void main(String[] args) {
    int port = 8080;
    if (args != null && args.length > 0) {
      try {
        port = Integer.valueOf(args[0]);
      } catch (NumberFormatException e) {
        // 采用默认值
      }
    }
    ServerSocket server = null;
    try {
      server = new ServerSocket(port);
      System.out.println("The time server is start in port:" + port);
      Socket socket = null;
      while (true) {
        socket = server.accept();
        new Thread(new TimeServerHandler(socket)).start();
      }

    } finally {
      if (server != null) {
        System.out.println("The time server close.");
        server.close();
        server = null;
      }
    }
  }
}

三)同步阻塞I/O的TimeClient代码

package com.nio.demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class TimeClient {

  public static void main(String[] args) {
    int port = 8080;
    if (args != null && args.length > 0) {
      try {
        port = Integer.valueOf(args[0]);
      } catch (NumberFormatException e) {
        // 采用默认值
      }
    }
    Socket socket = null;
    BufferedReader in = null;
    PrintWriter out = null;

    try {
      socket = new Socket("127.0.0.1", port);
      in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      out = new PrintWriter(socket.getOutputStream(), true);
      out.println("query time order");
      System.out.println("Send order 2 server succeed.");
      String resp = in.readLine();
      System.out.println("Now is:" + resp);
    } catch (Exception e) {
      // 不需要处理
    } finally {
      if (out != null) {
        out.close();
        out = null;
      }

      if (in != null) {
        try {
          in.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (socket != null) {
        try {
          socket.close();
          socket = null;
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }

}

上述代码也是体现了一连接一线程的模型。

为了改进,后来又演进通过线程池或许消息队列实现一个或者线程处理N个客户端的模型。由于底层通信机制依然采用同步阻塞I/O,所以称为“伪异步”。下文讲解“伪异步”。

 

 

 

 

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注