当前位置: 首页 > news >正文

传统java io_Java IO编程全解(二)——传统的BIO编程

网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建立连接,如果连接建立成功,双方就可以通过网络套接字(Socket)进行通信。

在基于传统同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。连接成功之后,双方通过输入和输出流进行同步阻塞式通信。

以经典的时间服务器(TimeServer)为例,通过代码分析来回顾和熟悉下BIO编程。

1. BIO通信模型图

如下图BIO的服务端通信模型:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的一请求一应答通信模型。

def1e8d0181f82341a258dddccf6db8a.png

图1  同步阻塞I/O服务端通信模型(一客户端一线程)

该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1 的正比关系,犹豫线程是Java虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。

2. 同步阻塞式I/O创建的TimeServer

packagejoanna.yan.bio;importjava.io.IOException;importjava.net.ServerSocket;importjava.net.Socket;public classTimeServer {public static voidmain(String[] args) {int port=9090;if(args!=null&&args.length>0){try{

port=Integer.valueOf(args[0]);

}catch(Exception e) {//采用默认值

}

}

ServerSocket server=null;try{

server=newServerSocket(port);

System.out.println("The time server is start in port:"+port);

Socket socket=null;while(true){//通过一个无限循环来监听客户端的连接

socket=server.accept();//如果没有客户端接入,则主线程阻塞在ServerSocket的accept操作上。

new Thread(newTimeServerHandler(socket)).start();

}

}catch(IOException e) {

e.printStackTrace();

}finally{if(server!=null){

System.out.println("The time server close");try{

server.close();

server=null;

}catch(IOException e) {

e.printStackTrace();

}

}

}

}

}

packagejoanna.yan.bio;importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.io.PrintWriter;importjava.net.Socket;importjava.util.Date;public class TimeServerHandler implementsRunnable{privateSocket socket;publicTimeServerHandler(Socket socket) {this.socket =socket;

}

@Overridepublic voidrun() {

BufferedReader in=null;

PrintWriter out=null;try{

in=new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

out=new PrintWriter(this.socket.getOutputStream(), true);

String currentTime=null;

String body=null;while(true){

body=in.readLine();if(body==null){break;

}

System.out.println("The time server receive order:"+body);//如果请求消息为查询时间的指令"QUERY TIME ORDER"则获取当前最新的系统时间。

currentTime="QUERY TIME ORDER".equalsIgnoreCase(body) ?

new Date(System.currentTimeMillis()).toString() : "BAD ORDER";

out.println(currentTime);

}

}catch(IOException e) {

e.printStackTrace();

}finally{if(in!=null){try{

in.close();

}catch(IOException e) {

e.printStackTrace();

}

}if(out!=null){

out.close();

out=null;

}if(this.socket!=null){try{this.socket.close();this.socket=null;

}catch(IOException e) {

e.printStackTrace();

}

}

}

}

}

3. 同步阻塞式I/O创建的TimeClient

客户端通过Socket创建,发送查询时间服务器的“QUERY TIME ORDER”指令,然后读取服务端的响应并将结果打印出来,随后关闭连接,释放资源,程序退出执行。

packagejoanna.yan.bio;importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.io.PrintWriter;importjava.net.Socket;importjava.net.UnknownHostException;public classTimeClient {public static voidmain(String[] args) {int port=9090;if(args!=null&&args.length>0){try{

port=Integer.valueOf(args[0]);

}catch(Exception e) {//采用默认值

}

}

Socket socket=null;

BufferedReader in=null;

PrintWriter out=null;try{

socket=new Socket("127.0.0.1",port);

in=new BufferedReader(newInputStreamReader(socket.getInputStream()));

out=new PrintWriter(socket.getOutputStream(),true);

out.println("QUERY TIME ORDER");

System.out.println("Send order 2 server succeed.");

String resp=in.readLine();

System.out.println("Now is:"+resp);

}catch(UnknownHostException e) {

e.printStackTrace();

}catch(IOException e) {

e.printStackTrace();

}finally{if(out!=null){

out.close();

out=null;

}if(in!=null){try{

in.close();

}catch(IOException e) {

e.printStackTrace();

}

in=null;

}if(socket!=null){try{

socket.close();

}catch(IOException e) {

e.printStackTrace();

}

socket=null;

}

}

}

}

我们发现,BIO主要的问题在于每当有一个新的客户端请求接入时,服务端必须创建一个新的线程处理新接入的客户端链路,一个线程只能吃力一个客户端连接。在高性能服务器应用领域,往往需要面向成千上万个客户端的并发连接,这种模型显然无法满足高性能、高并发接入的场景。

为了改进一线程一连接模型,后来又演进出了一种通过线程池或者消息队列实现1个或者多个线程处理N个客户端的模型,由于它的底层通信机制依然使用同步阻塞I/O,所以被称为“伪异步”。后面我们将通过对伪异步代码的分析,看看伪异步能否满足我们对高性能、高并发接入的诉求。

如果此文对您有帮助,微信打赏我一下吧~

相关文章:

  • 喝啤酒可测出性格
  • java. new对象 堆栈_Java 堆内存与栈内存详细介绍
  • 最有用的网络命令
  • 手机谜语
  • app指纹登录 java后端_Android应用实现指纹登录
  • 有一家银行每天早上都在我的帐户里存入¥86,400
  • java 解析 树_java-斯坦福大学nlp:解析树
  • mysql 行号 hibernate_Web项目从Oracle转为Mysql,fluentnhibernate-1.0和NHibernate2.1.0升级到NHibernate3.3的注意事项...
  • 快速访问系统文件夹
  • 让“龙头”与“长尾”共舞 - 长尾理论在Web2.0网站中的应用
  • mysql数据库部署回退文档_mysql数据库主从复制部署笔记
  • 感谢Candy
  • java包装类转换12_JAVA_包装类的使用与包装类数据类型转换
  • 关于JMS的编程模型
  • Java如何把md转为html_Java使用PegDown将markdown文件转成html格式
  • 【RocksDB】TransactionDB源码分析
  • 03Go 类型总结
  • 10个最佳ES6特性 ES7与ES8的特性
  • Docker入门(二) - Dockerfile
  • es6(二):字符串的扩展
  • Fundebug计费标准解释:事件数是如何定义的?
  • Hibernate最全面试题
  • JavaScript 奇技淫巧
  • js面向对象
  • Node项目之评分系统(二)- 数据库设计
  • orm2 中文文档 3.1 模型属性
  • Python打包系统简单入门
  • Sass Day-01
  • win10下安装mysql5.7
  • Xmanager 远程桌面 CentOS 7
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 第2章 网络文档
  • 翻译:Hystrix - How To Use
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 基于遗传算法的优化问题求解
  • 今年的LC3大会没了?
  • 浅谈web中前端模板引擎的使用
  • 如何优雅地使用 Sublime Text
  • 一个项目push到多个远程Git仓库
  • 做一名精致的JavaScripter 01:JavaScript简介
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • #宝哥教你#查看jquery绑定的事件函数
  • (145)光线追踪距离场柔和阴影
  • (2022 CVPR) Unbiased Teacher v2
  • (33)STM32——485实验笔记
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (强烈推荐)移动端音视频从零到上手(上)
  • (一)Dubbo快速入门、介绍、使用
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (转)大道至简,职场上做人做事做管理
  • .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
  • .NET Micro Framework初体验(二)
  • .Net程序帮助文档制作
  • .NET建议使用的大小写命名原则