同步阻塞IO
单线程BIO
最原始的网络编程思路就是服务器用一个while循环,不断监听端口是否有新的套接字连接,如果有,那么就调用一个处理函数处理。
while(true){
socket = accept();
handle(socket)
}
这种方法的最大问题是无法并发,效率太低。如果当前的请求没有处理完,那么后面的请求只能被阻塞,服务器的吞吐量太低。
多线程BIO
针对上面的问题,很自然想到了使用多线程处理IO,也就是很经典的connection per thread,每一个连接用一个线程处理。
while(true){
socket = accept();
new thread(socket);
}
tomcat服务器的早期版本确实是这样实现的。多线程的方式确实一定程度上极大地提高了服务器的吞吐量,因为之前的请求在read阻塞以后,不会影响到后续的请求,因为他们在不同的线程中。这也是为什么通常会讲“一个线程只能对应一个socket”的原因。
那么,线程中创建多个socket不行吗?语法上确实可以,但是实际上没有用,每一个socket都是阻塞的,这就遇到同单线程IO一样的问题。所以在一个线程里只能处理一个socket,就算accept了多个也没用,前一个socket被阻塞了,后面的是无法被执行到的。
Java BIO示例
下面基于同步阻塞式IO创建一个时间服务TimeServer。
TimeServer服务端
public class TimeServer {
private static int port = 8080;
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
System.out.println("server starts in port: " + port);
Socket socket = null;
while (true) {
// 监听来自客户端的连接,主线程阻塞在accept操作上
socket = serverSocket.accept();
// 创建一个新的线程处理socket链路
new Thread(new TimeServerHandler(socket)).start();
}
} finally {
if (serverSocket != null) {
serverSocket.close();
}
}
}
}
TimeServer Handler代码
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 currTime = null;
String body = null;
while (true) {
body = in.readLine();
if (body == null) {
break;
}
System.out.println("time server receive: " + body);
currTime = new Date(System.currentTimeMillis()).toString();
out.println(currTime);
}
} catch (Exception e) {
// ignore
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (out != null) {
out.close();
}
if (this.socket != null) {
try {
this.socket.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
this.socket = null;
}
}
}
TimeClient客户端
public