BIO

Java BIO 基本介绍

  • Java BIO 就是传统的 java io 编程,其相关的类和接口在 java.io
  • BIO(blocking I/O) : 同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善(实现多个客户连接服务器).

Java BIO 工作机制

在这里插入图片描述
对 BIO 编程流程的梳理

  1. 服务器端启动一个 ServerSocket,注册端口,调用accpet方法监听客户端的Socket连接。
  2. 客户端启动 Socket 对服务器进行通信,默认情况下服务器端需要对每个客户 建立一个线程与之通讯

传统的BIO编程实例回顾

​1.网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信(绑定IP地址和端口),客户端通过连接操作向服务端监听的端口地址发起连接请求,基于TCP协议下进行三次握手连接,连接成功后,双方通过网络套接字(Socket)进行通信。
​2.传统的同步阻塞模型开发中,服务端ServerSocket负责绑定IP地址,启动监听端口;客户端Socket负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。
​ 基于BIO模式下的通信,客户端 - 服务端是完全同步,完全耦合的。

过程:
创建客户端对象:
(1)创建一个Socket的通信管道,请求与服务端的端口连接。
(2)从Socket管道中得到一个字节输出流。
(3)把字节流改装成自己需要的流进行数据的发送
创建服务端对象:
(1)注册端口
(2)开始等待接收客户端的连接,得到一个端到端的Socket管道
(3)从Socket管道中得到一个字节输入流。
(4)把字节输入流包装成自己需要的流进行数据的读取。

客户端案例如下

package one;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;

public class client {
    public static void main(String[] args) {
        try {
            Socket socket=new Socket("127.0.0.1",9999);
            OutputStream outputStream=socket.getOutputStream();
            PrintStream printStream=new PrintStream(outputStream);
            printStream.println("hello_world");
            printStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端案例

package one;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class server {
    public static void main(String[] args) {
        ServerSocket ss;
        {
            try {
                ss = new ServerSocket(9999);
                Socket socket=ss.accept();
                InputStream inputStream=socket.getInputStream();
                BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
                String msg;
                while((msg = bufferedReader.readLine())!=null){
                    System.out.println("服务端收到"+msg);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

结果:
在这里插入图片描述

小结

  • 在以上通信中,服务端会一致等待客户端的消息,如果客户端没有进行消息的发送,服务端将一直进入阻塞状态。
  • 同时服务端是按照行获取消息的,这意味着客户端也必须按照行进行消息的发送,否则服务端将进入等待消息的阻塞状态!

BIO模式下多发和多收消息

在上述案例中,只能实现客户端发送消息,服务端接收消息,并不能实现反复的收消息和反复的发消息,我们只需要在客户端案例中,加上反复按照行发送消息的逻辑即可!案例代码如下:

服务端案例

package two;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class server {
    public static void main(String[] args) {
        ServerSocket ss;

        {
            try {
                ss = new ServerSocket(9999);
                Socket socket=ss.accept();
                InputStream inputStream=socket.getInputStream();
                BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
                String msg;
                while((msg = bufferedReader.readLine())!=null){
                    System.out.println("服务端收到"+msg);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端案例

package two;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class client {
    public static void main(String[] args) {
        try {
            Socket socket=new Socket("127.0.0.1",9999);
            OutputStream outputStream=socket.getOutputStream();
            PrintStream printStream=new PrintStream(outputStream);

            while(true){
                Scanner scanner=new Scanner(System.in);
                System.out.println("请输入消息:");
                String s = scanner.nextLine();
                printStream.println(s);
                printStream.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
在这里插入图片描述

小结

  • 本案例中确实可以实现客户端多发多收
  • 但是服务端只能处理一个客户端的请求,因为服务端是单线程的。一次只能与一个客户端进行消息通信。

BIO模式下接收多个客户端

在这里插入图片描述

客户端

package three;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class client {
    public static void main(String[] args) {
        try {
            Socket socket=new Socket("127.0.0.1",9999);
            OutputStream outputStream=socket.getOutputStream();
            PrintStream printStream=new PrintStream(outputStream);

            while(true){
                Scanner scanner=new Scanner(System.in);
                System.out.println("请输入消息:");
                String s = scanner.nextLine();
                printStream.println(s);
                printStream.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端

package three;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class server {
    public static void main(String[] args) {
        ServerSocket ss;

        {
            try {
                ss = new ServerSocket(9999);
                while(true){
                    Socket socket=ss.accept();
                    new ServerThreadReader(socket).start();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

package three;

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

public class ServerThreadReader extends Thread{
    private Socket socket;
    public ServerThreadReader(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run() {
        InputStream inputStream= null;
        try {
            inputStream = socket.getInputStream();
            BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
            String msg;
            while((msg = bufferedReader.readLine())!=null){
                System.out.println("服务端收到"+msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

小结

  • 1.每个Socket接收到,都会创建一个线程,线程的竞争、切换上下文影响性能;
  • 2.每个线程都会占用栈空间和CPU资源;
  • 3.并不是每个socket都进行IO操作,无意义的线程处理;
  • 4.客户端的并发访问增加时。服务端将呈现1:1的线程开销,访问量越大,系统将发生线程栈溢出,线程创建失败,最终导致进程宕机或者僵死,从而不能对外提供服务。

伪异步I/O编程

概述

​ 在上述案例中:客户端的并发访问增加时。服务端将呈现1:1的线程开销,访问量越大,系统将发生线程栈溢出,线程创建失败,最终导致进程宕机或者僵死,从而不能对外提供服务。
采用线程池和任务队列实现,当客户端接入时,将客户端的Socket封装成一个Task(该任务实现java.lang.Runnable线程任务接口)交给后端的线程池中进行处理。JDK的线程池维护一个消息队列和N个活跃的线程,对消息队列中Socket任务进行处理,由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。
在这里插入图片描述

客户端

package four;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class client {
    public static void main(String[] args) {
        try {
            Socket socket=new Socket("127.0.0.1",9999);
            OutputStream outputStream=socket.getOutputStream();
            PrintStream printStream=new PrintStream(outputStream);

            while(true){
                Scanner scanner=new Scanner(System.in);
                System.out.println("请输入消息:");
                String s = scanner.nextLine();
                printStream.println(s);
                printStream.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端

package four;

import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {
        try{
            ServerSocket socket=new ServerSocket(9999);
            HandleSocketServer handleSocketServer=new HandleSocketServer(6,10);
            while(true){
                Socket socket1=socket.accept();
                Runnable target=new ServerRunnableTarget(socket1);
                handleSocketServer.execute(target);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

package four;

import java.io.*;
import java.net.Socket;

public class ServerRunnableTarget implements Runnable{
    private  Socket socket;
    public ServerRunnableTarget(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run() {
        InputStream inputStream= null;
        try {
            inputStream = socket.getInputStream();
            BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
            String msg;
            while((msg = bufferedReader.readLine())!=null){
                System.out.println("服务端收到"+msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

package four;

import java.util.concurrent.*;

public class HandleSocketServer {
    private ExecutorService executorService;
    public HandleSocketServer(int maxThread,int QueueSize){
        executorService=new ThreadPoolExecutor(3,maxThread,2, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(QueueSize));
    }
    public void execute(Runnable target){
        executorService.execute(target);
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值