Java IO编程全解(二)——传统的BIO编程
前面讲到:Java IO编程全解(一)——Java的I/O演进之路
网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建立连接,如果连接建立成功,双方就可以通过网络套接字(Socket)进行通信。
在基于传统同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。连接成功之后,双方通过输入和输出流进行同步阻塞式通信。
以经典的时间服务器(TimeServer)为例,通过代码分析来回顾和熟悉下BIO编程。
1. BIO通信模型图
如下图BIO的服务端通信模型:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的一请求一应答通信模型。

图1 同步阻塞I/O服务端通信模型(一客户端一线程)
该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1 的正比关系,犹豫线程是Java虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。
2. 同步阻塞式I/O创建的TimeServer
package joanna.yan.bio; import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket; public class TimeServer { public static void main(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=new ServerSocket(port);
System.out.println("The time server is start in port:"+port);
Socket socket=null;
while(true){//通过一个无限循环来监听客户端的连接
socket=server.accept();//如果没有客户端接入,则主线程阻塞在ServerSocket的accept操作上。
new Thread(new TimeServerHandler(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();
}
}
}
}
}
package joanna.yan.bio; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date; public class TimeServerHandler implements Runnable{
private Socket socket; public TimeServerHandler(Socket socket) {
this.socket = socket;
} @Override
public void run() {
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”指令,然后读取服务端的响应并将结果打印出来,随后关闭连接,释放资源,程序退出执行。
package joanna.yan.bio; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException; public class TimeClient {
public static void main(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(new InputStreamReader(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 IO编程全解(二)——传统的BIO编程的更多相关文章
- Java IO编程全解(四)——NIO编程
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7793964.html 前面讲到:Java IO编程全解(三)——伪异步IO编程 NIO,即New I/O,这 ...
- Java IO编程全解(五)——AIO编程
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7794151.html 前面讲到:Java IO编程全解(四)--NIO编程 NIO2.0引入了新的异步通道的 ...
- Java io流详解二
原文地址https://www.cnblogs.com/xll1025/p/6418766.html 一.IO流概述 概述: IO流简单来说就是Input和Output流,IO流主要是用来处理设备之间 ...
- Java IO编程全解(三)——伪异步IO编程
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7723174.html 前面讲到:Java IO编程全解(二)--传统的BIO编程 为了解决同步阻塞I/O面临 ...
- Java IO编程全解(六)——4种I/O的对比与选型
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7804185.html 前面讲到:Java IO编程全解(五)--AIO编程 为了防止由于对一些技术概念和术语 ...
- Java IO编程全解(一)——Java的I/O演进之路
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7419117.html JDK1.4之前的早期版本,Java对I/O的支持并不完善,开发人员在开发高性能I/O ...
- JAVA IO 类库详解
JAVA IO类库详解 一.InputStream类 1.表示字节输入流的所有类的超类,是一个抽象类. 2.类的方法 方法 参数 功能详述 InputStream 构造方法 available 如果用 ...
- Java IO流详解(二)——File类
在上一章博客中简单的介绍了Java IO流的一些特征.也就是对文件的输入输出,既然至始至终都离不开文件,所以Java IO流的使用得从File这个类讲起. File类的描述:File类是文件和目录路径 ...
- netty学习(二)--传统的bio编程
网络编程的基本模型是Client/Server模型.也就是两个进程之间进行相互通信,当中服务端提供位置信息( 绑定ip地址和监听port),client通过连接操作向服务端监听的地址发送连接请求,通过 ...
随机推荐
- gzframework demo搭建
感谢框架作者,这里给出他的博客 http://www.cnblogs.com/GarsonZhang/ 背景:由于作者对代码的持续开发,导致了以前博客中的下载地址和构建方法和目前的项目不对应,这里给出 ...
- JDK 1.8源码阅读 HashSet
一,前言 类实现Set接口,由哈希表支持(实际上是一个 HashMap集合).HashSet集合不能保证的迭代顺序与元素存储顺序相同.HashSet集合,采用哈希表结构存储数据,保证元素唯一性的方式依 ...
- phpstorm 破解版
原链接https://blog.csdn.net/gu_wen_jie/article/details/79136475
- IP地址和子网划分学习笔记之《IP地址详解》
2018-05-03 18:47:37 在学习IP地址和子网划分前,必须对进制计数有一定了解,尤其是二进制和十进制之间的相互转换,对于我们掌握IP地址和子网的划分非常有帮助,可参看如下目录详文. ...
- [Android] macOS的Android Studio快捷键
- 快速输入serialVersionUID - - 设置,左上角,Android Studio -> Preferences -> 搜索“Serializable class witho ...
- ASP.net MVC5 Code First填充测试数据到数据库
问题的产生 最近在看Adam Freeman的“Pro ASP.NET MVC5”,于是在工作机上面搭建了相应的运行环境,但是在自己的机器上面只有代码,没有数据库.记得在code first中可以新 ...
- qemu 对虚机的地址空间管理
转载:http://huchh.com/2015/06/22/qemu-%E5%AF%B9%E8%99%9A%E6%9C%BA%E7%9A%84%E7%BA%BF%E6%80%A7%E5%9C%B0% ...
- windows安装composer总结
1.直接去网吧下载windows安装EXE程序,傻瓜式安装,so easy. 2.通过命令行安装,可以直接在php目录跑起来 php -r "readfile('https://getcom ...
- eclipse之常用快捷键
开发常用: 清理浏览器缓存:ctrl+shift+delete 切换窗口:alt+tab 同一个浏览器切换窗口:ctrl+shift+tab 刷新页面:ctrl+F5 前端调试debug: debug ...
- 在vue项目中 如何定义全局变量 全局函数
如题,在项目中,经常有些函数和变量是需要复用,比如说网站服务器地址,从后台拿到的:用户的登录token,用户的地址信息等,这时候就需要设置一波全局变量和全局函数 定义全局变量 原理: 设置一个专用的的 ...