一、IO模式之-BIO深入解析

1.1、BIO基本介绍

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

1.2、BIO的工作机制

  • 客户端
  1. 通过Socket对象请求与服务端建立连接
  2. 从Socket中得到字节输入或者字节输出流进行数据读写操作
  • 服务端
  1. 通过ServerSocket注册端口
  2. 服务端通过调用accept方法用于监听客户端的Socket请求
  3. 从Socket中得到字节输入或者字节输出流进行数据读写操作

1.3、BIO传统编程代码实现

  • 传统的同步阻塞模型开发中,服务端ServerSocket负责绑定IP地址,启动监听端口;客户端Socket负责 发起 连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。
  • 基于BIO模式下的通信,客户端-服务端是完全同步,完全藕合的。

客户端代码如下:

public class ClientTest {
    public static void main(String[] args) {
        //创建Socket对象请求服务端的连接
        Socket socket = null;
        try {
        socket = new Socket("127.0.0.1",8889);
        // 从Socket对象中获取一个字节输出流
         OutputStream os = socket.getOutputStream();
        //把字节输出流包装成一个打印流
         PrintStream ps = new PrintStream(os);
         ps.println("hello World! 与服务端通信成功");
         ps.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端代码:

public class ServerTest {
    public static void main(String[] args) {
        System.out.println("===服务端启动===");
        //定义一个ServerSocket对象进行服务端的端口注册
        ServerSocket ss = null;
        try {
            ss = new ServerSocket(8889);
        //监听客户端的Socket连接请求
        Socket socket = ss.accept();
        //从socket管道中得到一个字节输入流对象
        InputStream is = socket.getInputStream();
        //把字节输入流包装成一个缓存字符输入流
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String msg;
        if ((msg = br.readLine()) != null) {
            System.out.println("服务端接收客户端信息为:" + msg);
        }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

启动服务端:测试输出(注意需要先启动服务端),服务端请求成功之后,等待客户端发送消息,如果客户端没有发送消息,服务端一直等待

 启动客户端

 小结:

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

1.4、BIO编程现实多发多收

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

客户端代码

public class ClientTest01 {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",8889);
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            Scanner scanner = new Scanner(System.in);
            while (true){
                System.out.println("请输入:");
                String s = scanner.nextLine();
                ps.println(s);
                ps.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端代码

public class ServerTest01 {
    public static void main(String[] args) {
        System.out.println("===服务端启动===");
        try {
            ServerSocket ss = new ServerSocket(8889);
            Socket socket = ss.accept();
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String msg;
            while ((msg = br.readLine()) != null){
                System.out.println("服务端接收客户端信息为:" + msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

启动服务端

 启动客户端

 客户端(客户端一直处于运行中,可以持续输入)输入需要传送的信息

 服务端接收客户端传输的数据

 1.5、BIO模拟客户端服务端多对一

在上述的案例中,一个服务端只能接收一个客户端的通信请求, 那么如果服务端需要处理很多个客户端 的消 息通信请求应该如何处理呢 ,此时我们就需要在服务端引入线程了,也就是说客户端每发起一个请求,服务端就创 建一个新的线程来处理这个客户端的请求,这样就实现了一个客户端一个线程的模型

 

客户端一

public class ClientTest02 {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",8889);
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            Scanner scanner = new Scanner(System.in);
            while (true){
                System.out.println("请输入:");
                String s = scanner.nextLine();
                ps.println(s);
                ps.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端二

public class ClientTest03 {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",8889);
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            Scanner scanner = new Scanner(System.in);
            while (true){
                System.out.println("请输入:");
                String s = scanner.nextLine();
                ps.println(s);
                ps.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端三

public class ClientTest04 {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",8889);
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            Scanner scanner = new Scanner(System.in);
            while (true){
                System.out.println("请输入:");
                String s = scanner.nextLine();
                ps.println(s);
                ps.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端代码

public class ServerTest020304 {
    public static void main(String[] args) {
    try {
        System.out.println("服务端开始-------------------------");
        ServerSocket ss = new ServerSocket(8889);
        while (true){
            Socket accept = ss.accept();
            new ServerThreadReader(accept).start();
        }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //静态内部类
    static class ServerThreadReader extends Thread{

        public Socket socket;

        public ServerThreadReader() {
        }
        public ServerThreadReader(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                InputStream is = socket.getInputStream();
                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                String msg;
                while ((msg = br.readLine()) != null){
                    System.out.println("当前线程名称"+Thread.currentThread().getName()+",,,服务端接收到:" + msg);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

启动服务端

 分别启动客户端1,2,3

分别输入对应的msg,效果如下

 

 服务端结果

服务端开始-------------------------
当前线程名称Thread-0,,,服务端接收到:ClientTest0 测试客户端1
当前线程名称Thread-1,,,服务端接收到:ClientTest03 测试客户端2
java.net.SocketException: Connection reset
	at java.net.SocketInputStream.read(SocketInputStream.java:210)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
	at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
	at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
	at java.io.InputStreamReader.read(InputStreamReader.java:184)
	at java.io.BufferedReader.fill(BufferedReader.java:161)
	at java.io.BufferedReader.readLine(BufferedReader.java:324)
	at java.io.BufferedReader.readLine(BufferedReader.java:389)
	at testDemo.src.coolstore.io.nio.ServerTest020304$ServerThreadReader.run(ServerTest020304.java:46)
当前线程名称Thread-3,,,服务端接收到:ClientTest04 测试客户端3

BIO总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值