一文理清什么是BIO以及如何使用
作者:goyeer
这篇文章主要介绍了什么是BIO以及如何使用,BIO英文全名是blockingIO,也叫做阻塞IO,是最容易理解、最容易实现的IO工作方式,本文就来通过一些简单的示例为大家讲讲BIO吧,需要的朋友可以参考下
一、BIO概述
BIO(Blocking I/O)是传统java io编程既同步阻塞IO,服务器实现模式为一个连接一个线程。客户端有连接请求时服务器端就会新起一个线程进行处理。当线程空闲时为减少不必要的线程开销,可以通过线程池机制改善。BIO方式适合用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限应用中。
二、BIO工作机制
客户端
- 通过Socket对象请求与服务端建立连接。
- 从Socket中得到字节输入或者字节输出流进行数据读写操作。
服务端
- 通过ServerSocket注册端口。
- 服务端通过调用accept方法用于监听客户端的Socket请求。
- 从Socket中得到字节输入或者字节输出流进行数据读写操作。
三、同步阻塞步骤
- 服务端启动一个ServerSocket
- 客户端启动Socket对服务器进行通信,默认情况下服务器端需要对每个客户建立一个线程与之通讯。
- 客户端发出请求后,先咨询服务器是否有线程响应,如果没有则会等待,或者被拒绝。
- 如果有响应,客户端线程会等待请求结束后,在继续执行。
四、编码实现传统BIO
- 传统的同步阻塞模型开发中,服务端ServerSocket负责绑定IP地址,启动监听端口;客户端Socket负责 发起 连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。
- 基于BIO模式下的通信,客户端-服务端是完全同步,完全藕合的。
服务端代码
public static void main(String[] args) { System.out.println("===服务端启动==="); ServerSocket serverSocket=null; try { serverSocket=new ServerSocket(5000); Socket socket=serverSocket.accept(); InputStream is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String msg; if ((msg = br.readLine()) != null) { System.out.println("服务端接收客户端信息为:" + msg); } }catch (Exception exception){ System.out.println(exception.getMessage()); } }
客户端代码
public static void main(String[] args) { Socket socket = null; try { socket = new Socket("127.0.0.1",5000); OutputStream os = socket.getOutputStream(); PrintStream ps = new PrintStream(os); ps.println("Hi BIO! 与服务端通信成功"); ps.flush(); } catch (IOException e) { e.printStackTrace(); } }
总结
传统BIO服务端会一直等待客户端的消息,如果客户端没有进行消息的发送,服务端将一直进入阻塞状态,同时服务端是按照行获取消息的,这意味着客户端也必须按照行进行消息的发送,否则服务端将进 入等待消息的阻塞状态。
五、BIO编程现实多发多收
BIO简单方式是一个接收一个发送,如果实现多发多接收,下面将验证多发多收的代码
服务端代码
public static void main(String[] args) { System.out.println("===服务端启动==="); try { ServerSocket ss = new ServerSocket(9988); Socket socket = ss.accept(); InputStream is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String message; while ((message = br.readLine()) != null){ System.out.println("服务端接收客户端信息为:" + message); } } catch (IOException e) { e.printStackTrace(); } }
客户端代码
public static void main(String[] args) { try { Socket socket = new Socket("localhost",9988); OutputStream os = socket.getOutputStream(); PrintStream ps = new PrintStream(os); Scanner scanner = new Scanner(System.in); while (true){ System.out.println("请输入:"); String input = scanner.nextLine(); ps.println(input); ps.flush(); } } catch (IOException e) { e.printStackTrace(); } }
六、BIO模拟客户端服务端多对一
前面无论是一收一发,还是多发多收都仅限一个客户端,如何实现一个服务端对应多个客户端哪,这个主要是更改服务端代码
实现步骤
- 监听tcp端口
- while循环接收连接
- 对接收到的连接进行InputStream/OutputStream操作
服务端实现
public void listen() throws IOException { ServerSocket serverSocket = null; try { log.info("服务启动监听"); serverSocket = new ServerSocket(9988); //循环接收到客户端的连接 while (true) { Socket socket = serverSocket.accept(); //得到连接后,开启一个线程处理连接 handleSocket(socket); } }finally { if(serverSocket != null){ serverSocket.close(); } } } private void handleSocket(Socket socket) { HandleSocket socketHandle = new HandleSocket(socket); new Thread(socketHandle).start(); }
public void run() { BufferedInputStream bufferedInputStream = null; BufferedOutputStream bufferedOutputStream = null; try { bufferedInputStream = new BufferedInputStream(socket.getInputStream()); byte[] bytes = new byte[1024]; int len ; if ((len = bufferedInputStream.read(bytes)) > -1) { String result = new String(bytes,0,len); System.out.println("本次接收到的结果:"+result); } System.out.println("回复信息给客户端:"); bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream()); String outString = Thread.currentThread().getName()+"接收到了"; bufferedOutputStream.write(outString.getBytes()); bufferedOutputStream.flush(); System.out.println("回复完成:"); } catch (IOException e) { System.out.println("异常:"e.getLocalizedMessage()); } finally { System.out.println("关闭数据流:"); try { if (bufferedInputStream != null) { bufferedInputStream.close(); } if (bufferedOutputStream != null) { bufferedOutputStream.close(); } }catch (IOException e){ System.out.println("请输入:"e.getLocalizedMessage()); } } }
客户端实现
1.与服务端建立连接
2.发送消息给服务端
3.接收服务端返回的消息
public void start() throws IOException { Socket socket = new Socket("127.0.0.1", 8081); String msg = "Hi,This is the BioClient"; //1.拿到输出流 //2.对输出流进行处理 System.out.println("请输入:"+msg); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream()); byte[] bytes = msg.getBytes(); //3.输出msg bufferedOutputStream.write(bytes); bufferedOutputStream.flush(); System.out.println("发送完毕."); System.out.println("开始接收到消息."); //4.对输入流进行处理 BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream()); byte[] inBytes = new byte[1024]; int len; //5.读取输入流 if ((len = bufferedInputStream.read(inBytes)) != -1) { String result = new String(inBytes, 0, len); System.out.println("接收到的消息="+result); } //6.关闭输入输出流 bufferedOutputStream.close(); bufferedInputStream.close(); socket.close(); }
七、总结
- 每个Socket接收到,都会创建一个线程,线程的竞争、切换上下文影响性能;
- 每个线程都会占用栈空间和CPU资源;
- 并不是每个socket都进行lO操作,无意义的线程处理;
- 客户端的并发访问增加时。服务端将呈现1:1的线程开销,访问量越大,系统将发生线程栈溢出, 线程创建失败,最终导致进程宕机或者僵死,从而不能对外提供服务;
以上就是一文理清什么是BIO以及如何使用的详细内容,更多关于Java BIO的资料请关注脚本之家其它相关文章!