BIO编程

1、BIO介绍

  • Java BIO就是传统的Java I/O编程,其相关的类和接口在java.io。

  • BIO(BlockingI/O):同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善(实现多个客户连接服务器)。

  • BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,程序简单易理解。

  • 工作原理图如下:

    • ①服务器端启动一个ServerSocket。
    • ②客户端启动Socket对服务器进行通信,默认情况下服务器端需要对每个客户建立一个线程与之通讯。
    • ③客户端发出请求后,先咨询服务器是否有线程响应,如果没有则会等待,或者被拒绝。
    • ④如果有响应,客户端线程会等待请求结束后,才继续执行。

2、BIO实例分析

  • 实例说明:

    • 使用BIO模型编写一个服务器端,监听6666端口,当有客户端连接时,就启动一个线程与之通讯。

    • 要求使用线程池机制改善,可以连接多个客户端。

    • 服务器端可以接收客户端发送的数据(telnet方式即可)。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      public class TestBIO {
      public static void main(String[] args) throws Exception {
      //线程池机制
      //1. 创建一个线程池
      //2. 如果有客户端连接,就创建一个线程,与之通讯(单独写一个方法)
      ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
      //创建ServerSocket
      ServerSocket serverSocket = new ServerSocket(6666);
      System.out.println("服务器启动了");
      while (true) {
      //监听,等待客户端连接
      final Socket socket = serverSocket.accept();
      System.out.println("连接到一个客户端");
      //就创建一个线程,与之通讯(单独写一个方法)
      newCachedThreadPool.execute(() -> {
      //可以和客户端通讯
      handler(socket);
      });
      }
      }

      //编写一个handler方法,和客户端通讯
      public static void handler(Socket socket) {
      try {
      System.out.println("线程名字=" + Thread.currentThread().getName() + ";线程ID=" + Thread.currentThread().getId());
      byte[] bytes = new byte[1024];
      //通过socket获取输入流
      InputStream inputStream = socket.getInputStream();
      //循环的读取客户端发送的数据
      while (true) {
      System.out.println("read....");
      int read = inputStream.read(bytes);
      if (read != -1) {
      System.out.println(new String(bytes, 0, read));//输出客户端发送的数据
      } else {
      break;
      }
      }
      } catch (Exception e) {
      e.printStackTrace();
      } finally {
      System.out.println("关闭和client的连接");
      try {
      socket.close();
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }
      }

      在运行该程序并在终端输入”telnet 127.0.0.1 6666“命令连接此服务器端后紧接着运行结果如下:

  • 使用BIO实现一个简单的聊天室:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    public class GroupChatClient {
    private final String HOST = "127.0.0.1";//服务器的ip
    private final int PORT = 6666;//服务器端口
    private Socket socket;

    public GroupChatClient() {
    try {
    socket = new Socket(InetAddress.getByName(HOST), PORT);
    String inetAddress = socket.getInetAddress().getHostAddress() + ":" + socket.getLocalPort();
    System.out.println(inetAddress + "与服务端建立连接!");
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    public void start() {
    try {
    //启动线程用于读取服务端发送过来的消息
    ServerHandler handler = new ServerHandler();
    Thread t = new Thread(handler);
    t.setDaemon(true);
    t.start();

    //发送数据给服务器端
    OutputStream outputStream = socket.getOutputStream();
    OutputStreamWriter osw = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
    BufferedWriter bw = new BufferedWriter(osw);
    PrintWriter pw = new PrintWriter(bw,true);

    Scanner scanner = new Scanner(System.in);
    while(true) {
    String line = scanner.nextLine();
    if("exit".equals(line)){
    break;
    }
    pw.println(line);
    }
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    try {
    socket.close();//与服务端断开连接
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    /**
    * 该线程负责读取服务端发送过来的消息
    */
    private class ServerHandler implements Runnable {
    public void run(){
    try{
    //通过socket获取输入流读取服务端发送过来的消息
    InputStream inputStream = socket.getInputStream();
    InputStreamReader isr = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
    BufferedReader br = new BufferedReader(isr);
    String line;
    while((line = br.readLine())!=null){
    System.out.println(line);
    }
    }catch(Exception e){
    e.printStackTrace();
    }
    }
    }

    public static void main(String[] args) {
    GroupChatClient groupChatClient = new GroupChatClient();
    groupChatClient.start();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    public class GroupChatServer {
    private ServerSocket serverSocket;
    private List<PrintWriter> allClients = new ArrayList<>();

    public GroupChatServer() {
    try {
    serverSocket = new ServerSocket(6666);
    System.out.println("服务器启动成功");
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    public void start() {
    try {
    while (true) {
    Socket socket = serverSocket.accept();
    String inetAddress = socket.getInetAddress().getHostAddress() + ":" + socket.getPort();
    ClientHandler clientHandler = new ClientHandler(socket, inetAddress);
    Thread thread = new Thread(clientHandler);
    thread.start();
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    class ClientHandler implements Runnable {
    private Socket socket;
    private String clientAddress;

    public ClientHandler(Socket socket, String clientAddress) {
    this.socket = socket;
    this.clientAddress = clientAddress;
    }

    @Override
    public void run() {
    PrintWriter p = null;
    try {
    //将客户端的打印流保存在数组
    OutputStream out = socket.getOutputStream();
    OutputStreamWriter osw = new OutputStreamWriter(out, StandardCharsets.UTF_8);
    BufferedWriter buf = new BufferedWriter(osw);
    p = new PrintWriter(buf, true);//自动刷新
    synchronized (GroupChatServer.class) {
    allClients.add(p);
    }
    System.out.println(clientAddress + "上线了,当前人数:" + allClients.size());

    //将消息转发给各个客户端
    InputStream in = socket.getInputStream();
    InputStreamReader ir = new InputStreamReader(in, StandardCharsets.UTF_8);
    BufferedReader buff = new BufferedReader(ir);
    String str;
    while ((str = buff.readLine()) != null) {
    //回复给客户端
    synchronized (GroupChatServer.this) {
    for (PrintWriter pw : allClients) {
    pw.println(clientAddress + "说:" + str);
    }
    }
    }
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    //处理当客户端连接断开的操作
    synchronized (GroupChatServer.this) {
    allClients.remove(p);
    }
    System.out.println(clientAddress + "下线了,当前人数:" + allClients.size());
    try {
    socket.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }

    public static void main(String[] args) {
    GroupChatServer groupChatServer = new GroupChatServer();
    groupChatServer.start();
    }
    }