在这里使用 java
代码,记录 BIO
的理解。要使用 jdk1.4 版本的才能在追踪的时候看出本质。高版本的jdk 内部已经有了优化,会使用poll
的方式来执行。Block IO
。
在linux
中使用 strace
命令,可以追踪程序的系统调用。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketBIO {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(9090, 20);
System.out.println("服务已开启");
while (true) {
Socket client = server.accept();//阻塞1,等待接收连接
System.out.println("客户端已连上,端口号:" + client.getPort());
//一旦获取到了一个client,分一个线程出去处理
new Thread(() -> {
InputStream in = null;
try {
in = client.getInputStream();//获取输入流,字节
//将字节转换为字符并读取
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
while (true) {
String s = bufferedReader.readLine();//阻塞2,获取该线程连接的输入
if (null != s) {
System.out.println("输入的内容是:" + s);
} else {
client.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}
以上代码执行后,会有两个阻塞,线程一直被挂起,等待连接和输入
用strace
追踪后可以发现下图是主线程等待连接的阻塞:
开启一个socket,返回一个
变量
3,为3绑定端口号,然后监听,做write
系统调用,3这个线程开始等待连接。上图中的
2531
行中的write
函数就是系统调用,参数和上面代码的输出不一致。当使用
nc
命令连接上这个服务后,将会返回一个客户端,进入线程中,主线程继续 accept
。从追踪命令当中可以看到,
java
当中的新启动一个线程,在linux
中,实际上是开启了一个进程。新进程是从主进程中clone
出来的。返回来个1590进程号。
继续追踪1590进程号可以发现还有一个recv
阻塞了。对应的是代码的这一行bufferedReader.readLine()
。
这里的变量
5是新线程的client。
- 因为
BIO
模式有blocking
,所以只能以抛出线城的方式去处理连接,不然在主线程只能接收一个连接。程序员的代码是无法解决这个问题的,因为想要连接多个,必然只能抛出多个线程,因为有阻塞。 - 每轮一下,还会涉及到系统调用,
accept,clone,recv
都是系统调用函数。所以要解决这个问题,只能从内核下手。所以有了后来的NIO
,noblock
。
以上两点就是BIO
的弊端