在Java中,使用socket进行网络通信,IO有如下模式:BIO、NIO、AIO。
分别代表着:同步阻塞IO、同步非阻塞IO、异步非阻塞IO。
BIO
1 概念描述
指阻塞式IO通信模式。如下图所示为BIO模式示意图:
这里写图片描述
每建立一个Socket连接时,同时创建一个新线程对该Socket进行单独通信(采用阻塞的方式通信)。这种方式具有很高的响应速度,并且控制起来也很简单,在连接数较少的时候非常有效,但是如果对每一个连接都产生一个线程的无疑是对系统资源的一种浪费,如果连接数较多将会出现资源不足的情况。
2 特点
他有以下两个特点:
(a)使用一个独立的线程维护一个socket连接,随着连接数量的增多,对虚拟机造成一定压力;
(b)使用流来读取数据,流是阻塞的,当没有可读/可写数据时,线程等待,会造成资源的浪费;
NIO
1 概念描述
指的是非阻塞式IO通信模式
针对于BIO的两个特点,其实也是两个缺点,Java提供了NIO通信模式的实现。相对于BIO来说,NIO模式即非阻塞IO。服务器端保存一个Socket连接列表,然后对这个列表进行轮询,如果发现某个Socket端口上有数据可读时(读就绪),则调用该socket连接的相应读操作;如果发现某个 Socket端口上有数据可写时(写就绪),则调用该socket连接的相应写操作;如果某个端口的Socket连接已经中断,则调用相应的析构方法关闭该端口。这样能充分利用服务器资源,效率得到了很大提高。Java中使用Selector、Channel、Buffer来实现上述效果,如下图所示:
这里写图片描述
线程中包含一个Selector对象,他相当于一个通道管理器,可以实现在一个单独线程中处理多个通道的目的,减少线程的创建数量。远程连接对应一个channel,数据的读写通过buffer均在同一个channel中完成,并且数据的读习是非阻塞的。通道创建后需要注册在selector中,同时需要为该通道注册感兴趣事件(客户端连接服务端事件、服务端接收客户端连接事件、读事件、写事件),selector线程需要采用轮训的方式调用selector的select函数,直到所有注册通道中有兴趣的事件发生,则返回,否则一直阻塞。而后循环处理所有就绪的感兴趣事件。以上步骤解决BIO的两个瓶颈:(1)不必对每个连接分别创建线程;(2)数据读写非阻塞
下面对以下三个概念做一个简单介绍,Java NIO由以下三个核心部分组成:
-(a)selector
Selector允许单线程处理多个Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。要使用Selector,得向Selector注册Channel,然后调用他的select方法,这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子入有新连接接进来,数据接收等。
-(b)channel与buffer
基本上,所有的IO在NIO中都从一个Channel开始。Channel有点像流。数据可以从channel读到buffer,也可以从budder写到channel。
channel和buffer有好几种类型。下面是Java NIO中的一些主要channel的实现:
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel
正如你所看到的,这些通道涵盖了UDP和TCP网络IO,以及文件IO。
以下是Java NIO里关键的buffer实现:
ByteBuffer
CharBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
2 特点
NIO的特点:
(a)一个线程可以处理多个通道,减少线程创建数量;
(b)读写非阻塞,节约资源:没有可读/可写数据时,不会发生阻塞导致线程资源的浪费
package com.qiz.bio;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author qiz
*/
public class BIO {
public static void main(String[] args) throws IOException {
ExecutorService executorService = Executors.newCachedThreadPool();
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("服务器启动了!");
while (true){
final Socket socket = serverSocket.accept();
System.out.println("连接到一个客户端");
executorService.execute(new Runnable() {
@Override
public void run() {
try {
handler(socket);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
public static void handler(Socket socket) throws IOException {
System.out.println("线程信息 id="+Thread.currentThread().getId()+"名字:"+Thread.currentThread().getName());
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();
while (true){
int read = inputStream.read(bytes);
if (read != -1){
System.out.println("输出客户端发送的数据");
System.out.println(new String(bytes,0,read));
}else {
break;
}
}
System.out.println("关闭和client的连接");
socket.close();
}
}
image.png
image.png
image.png
网友评论