小Q:什么是 IO 模型?
慢慢:我们先来了解下 IO。在 linux 系统中,一切皆是文件,而文件又是一串二进制流。操作系统中有很多的流,我们需要通过一个符号表才能够管理这些流,这个符号表为 fd,即文件描述符表。
所以,Linux 的内核将所有外部设备都看做一个文件来操作,每一个文件的读写操作都会返回一个 fd。
我们每次操作文件,都需要用户空间,内核空间,设备空间进行紧密的配合。那么如何让他们更高效的配合呢?就提出了多种的设计方案,也叫做 IO 模型。
小Q:能具体讲下 BIO 模型吗?
BIO 模型即同步阻塞模型。我们以网络 IO 模型为例。
网络编程的基本模型是 Client/Server 模型,也就是两个进程间相互通信,其中服务端提供位置信息,客户端通过连接操作向服务端监听的地址发送连接请求,通过三次握手建立连接,如果连接成功,则双方可以通过网络套接字进行通信。
在传统的 BIO 模式下,Server 必须先开启一个线程用来监听是否有连接的请求,每当有连接建立时,通过创建线程去处理该链接,从而保证有一个线程是用来专门监听请求,而不会被业务功能所耽误。
小Q:懂了懂了,赶快上代码吧!
服务端程序
public class Server {
public static void main(String[] args) {
ServerSocket server = new ServerSocket(8080);
Socket socket = null;
while (true) {
socket = server.accept();
new Thread(new ServerHandler(socket)).start();
}
server.close();
server = null;
}
}
请求处理
public ServerHandler implements Runnable {
private Socket socket;
public ServerHandler (Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedRead in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
out = new PrintWriter(this.socket.getOuputStream(), true);
String body = null;
while (true) {
body = in.readLine();
if (body == null)
break;
out.println(body);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
in = null;
} catch (Exception e1) {
e1.printStackTrace();
}
}
if (out != null) {
out.close();
out = null;
}
if (this.socket != null) {
try {
this.socket.close();
} catch (Exception e1) {
e1.printStackTrace();
}
this.socket = null;
}
}
}
}
客户端:
public class Client {
public static void main(String[] args) {
Socket socket = null;
BufferReader in = null;
PrintWriter out = null;
try {
socket = new Socket("127.0.0.1", 8080);
in = new BufferReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
out.println("QUERY TIME ORDER");
String s = in.readLine();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
out = null;
} catch (Exception e1) {
e1.printStackTrace();
}
}
if (in != null) {
in.close();
in = null;
}
if (this.socket != null) {
try {
this.socket.close();
} catch (Exception e1) {
e1.printStackTrace();
}
this.socket = null;
}
}
}
}
小Q:大概理解了,就是用一个线程去监听连接,然后每次有连接都开启一个线程去处理。这样做是不是会有缺陷呀!
慢慢:是的呀。如果突然有大量的请求发到服务器上,那么服务器就得创建大量的线程,这会导致创建资源的不可控,甚至是服务器宕机,这种是绝对不允许的。此外,这种模型也不支持长连接,如果一个连接久久都每释放,那么这个线程就一直被占用,导致后面能创建的线程数变少。
小Q:说到长连接,长连接和短连接的区别是什么呢?
慢慢:短连接的话,每发送完请求并接收到响应后都会自动断开连接。我们都知道,TCP 连接的建立需要经过三次握手,这也导致网络负担的增加,并且握手的数据包往返也消耗了大量的时间。长连接则是通过心跳机制来维护这个连接不会断开,并且每次发送请求都不用经过握手阶段。
|