首先安装netcat(window安装教程)来模拟客户端
BIO(Blocking IO)顾名思义阻塞式IO
在服务端的主要实现代码是
ServerSocket.accept();
InputStream.read(), OutputStream.write()
这些方法都是阻塞的方法,没有请求就会一直阻塞直到有新连接进来,或数据输入输出
单线程BIO
public class ServerBio {
public static void main(String[] args) throws IOException {
//监听9000端口
ServerSocket serverSocket = new ServerSocket(9000);
while (true) {
//未有连接,阻塞
Socket socket = serverSocket.accept();
System.out.println("一个新的连接进来");
handler(socket);
socket.close();
}
}
private static void handler(Socket socket) throws IOException {
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();
//循环读出一次传过来的所有字节
while (true) {
int read = inputStream.read(bytes);
if (read != -1) {
System.out.println(new String(bytes, 0, read));
} else {
break;
}
}
}
}
打上断点,调试运行程序
下一步,可以看到程序阻塞在socket的accept方法,等待连接进来
accept方法也详细说明了阻塞知道一个连接建立
用先前安装好的netcat作为客户端发起连接,可以看到程序取消阻塞,执行下一步
一直点击下一步,来到下图,因为客户端没有发数据过来,所以read方法又阻塞
InputStream的read方法注释
在nc命令端输入数据,就会传送到服务端
当服务器接收完数据之后,只要客户端还保持连接,read就不会等于-1,又阻塞在read方法等待客户端的数据
还是read注释:如果socket流关闭了read方法就会返回-1
现在我们来新开一个客户端连接同样的端口,可以发现由于程序阻塞在read方法,并没有接收到新客户端的连接
在新客户端发的消息服务器理所当然也没有接收,因为服务器当是单线程的,正在阻塞等待旧客户端的数据过来
那为什么第二个连接还在等待连接呢,现在关闭第一个客户端连接,发现服务器出错了,原因是客户端连接断开->stream退出,服务器的socket并没有拿到输入流,所以抛出异常
下面将代码完善一下,在客户端输入结束字符让服务器结束本次socket的连接,这样就不会抛出异常了
private static void handler(Socket socket) throws IOException {
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();
while (true) {
int read = inputStream.read(bytes);
String msg = new String(bytes, 0, read);
if("end\n".equals(msg)) break;
if (read != -1) {
System.out.println(new String(bytes, 0, read));
} else {
break;
}
}
}
可以看到客户端自己结束后,刚阻塞的新连接也连接成功了
线程池实现的BIO
每有一次客户端的连接进来,就会在
线
程
池
\color{red}{线程池}
线程池分配一个线程来执行这个连接,从下图可以看到红色的虚线是请求服务器并且分配线程,建立连接后双方就可以互相通信(黑色实线)
代码实现
public class ServerThreadBio {
public static void main(String[] args) throws IOException {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
//监听9000端口
ServerSocket serverSocket = new ServerSocket(8000);
while (true) {
//未有连接,阻塞
final Socket socket = serverSocket.accept();
System.out.println("一个新的连接进来");
newCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
handler(socket);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
private static void handler(Socket socket) throws IOException {
try{
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();
while (true) {
int read = inputStream.read(bytes);
if (read != -1) {
System.out.println(new String(bytes, 0, read));
} else {
break;
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
socket.close();
}
}
}
可以看到服务器能同时处理两个客户端的连接请求的数据了
可以看到线程池有两个线程 Running