【JAVA基础】- BIO介绍及其使用
文章目录
- 【JAVA基础】- BIO介绍及其使用
- 一、BIO概述
- 二、BIO工作机制
- 客户端
- 服务端
- 三、同步阻塞步骤
- 四、编码实现传统BIO
- 服务端代码
- 客户端代码
- 总结
- 五、BIO编程现实多发多收
- 服务端代码
- 客户端代码
- 六、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.输出msgbufferedOutputStream.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的线程开销,访问量越大,系统将发生线程栈溢出, 线程创建失败,最终导致进程宕机或者僵死,从而不能对外提供服务;