BIO
什么叫BIO , BIO 是 block input output 的缩写 , 意思就是阻塞式的IO。这个是为了区别后面的NIO , 有关NIO ,后面再做介绍。
举个简单的例子:我在使用bio的socket网络通信中,会有下面的代码:
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
OutputStream outputStream = accept.getOutputStream();
byte[] readBytes = new byte[100];
inputStream.read(readBytes);
System.out.println("===== " + new String(readBytes));
outputStream.write((System.currentTimeMillis() + "").getBytes());
accept.close();
}
在 inputStream.read(readBytes);这一行,如果socket链接没有断,他会一直等待,直到读完socket的一次写入的所有的数据,或者是byte指定大小的数据。在链接没有断开的情况下,直到数据读完,才会执行下面的代码。(这个是bio的关键部分)。
所以,如果在服务端,如果是单线程的,上面的代码,如果一个客户端链接进来,没有关闭,则第二个用户链接,发送数据不会有相应的。
——————————
所以针对,bio 的socket,出现了下面三种模型:
socket bio -- 单线程模式:
这个比较简单,直接写demo :
public class BioSimpleServer {
private static int port = 8888;
private static void init() throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
OutputStream outputStream = accept.getOutputStream();
byte[] readBytes = new byte[100];
inputStream.read(readBytes);
String readString = new String(readBytes);
System.out.println("===== " + readString);
outputStream.write((System.currentTimeMillis() + "").getBytes());
if (readString != null && readString.trim().equalsIgnoreCase("quit")){
accept.close();
}
}
}
public static void main(String[] args) throws IOException {
init();
}
}
这段代码,只是一个简单的demo , 没有考虑发送数据的长度,直接通过100个byte接收了。如果客户端,有数据过来,直接返回一个当前时间的毫秒数,如果请求的字符串是quit,则关闭连接。如果你采用telnet测试 , 只有第一个连接发送quit以后,第二个连接才会有相应。
socket bio -- 多线程模式:
上面可以看到,单线程模式的弊端是,一次只能服务一个客户端,如果有多个客户端过来链接,那就只能傻等,只有这个用户关闭链接以后,第二个客户端才可以链接的上,为了解决这个问题,出现了bio socket的多线程模式。
public class BioSimpleThread {
private static int port = 8888;
private static void init() throws IOException {
ServerSocket serverSocket = new ServerSocket(port , 10);
while (true) {
final Socket accept = serverSocket.accept();
new Thread() {
@Override
public void run() {
while (true){
try {
InputStream inputStream = accept.getInputStream();
OutputStream outputStream = accept.getOutputStream();
byte[] readBytes = new byte[100];
inputStream.read(readBytes);
String readString = new String(readBytes);
System.out.println("===== " + readString);
outputStream.write((System.currentTimeMillis() + "").getBytes());
if (readString != null && readString.trim().equalsIgnoreCase("quit")){
accept.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
}
}
public static void main(String[] args) throws IOException {
init();
}
}
代码比较简单,一看就懂,这里添加了一个线程,只要有socket accept过来,就起一个线程,处理客户端的请求,这样可以保证,可以同时处理多个用户请求的情况。
socket bio -- 线程池模式:
在看了多线程的bio 以后,我们发现,只要有请求过来,就启动一个线程,如果有10万的客户端轻轻,就会启动10万个线程 ,这样就会出现cpu由于多个线程切换,线程相互竞争,服务端会越来越慢,直到服务器被托跨的问题,
为了避免这种问题,我们就需要规划自己的用户规模,考虑服务器的性能,单台机器上面,最多可以支持多少的在线用户。采用线程池的模式。
public class BioThreadPool {
private static int port = 8888;
static ExecutorService cachedThreadPool = Executors.newFixedThreadPool(100);
private static void init() throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
final Socket accept = serverSocket.accept();
cachedThreadPool.execute(new Thread() {
@Override
public void run() {
try {
InputStream inputStream = accept.getInputStream();
OutputStream outputStream = accept.getOutputStream();
byte[] readBytes = new byte[100];
inputStream.read(readBytes);
String readString = new String(readBytes);
System.out.println("===== " + readString);
outputStream.write((System.currentTimeMillis() + "").getBytes());
if (readString != null && readString.trim().equalsIgnoreCase("quit")){
accept.close();
}
} catch (Exception e) {
if (accept != null){
try {
accept.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
}
}
});
}
}
public static void main(String[] args) throws IOException {
init();
}
}
这里采用了 static ExecutorService cachedThreadPool = Executors.newFixedThreadPool(100); , 避免单台服务器上面,启动过多的线程 , 将服务器托跨。
来源:oschina
链接:https://my.oschina.net/u/725800/blog/808036