This action will force synchronization from 陌溪/LearningNotes, which will overwrite any changes that you have made since you forked the repository, and can not be recovered!!!
Synchronous operation will process in the background and will refresh the page when finishing processing. Please be patient.
概念
在提到NIO之前,我们说先看看BIO,也就是Blocking IO,阻塞IO,我们首先实现一个最基本的网络通信
/**
* QQ客户端
*
* @author: 陌溪
* @create: 2020-03-28-11:09
*/
public class QQClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8080);
socket.getOutputStream().write("我来发送".getBytes());
}
}
/**
* QQ客户端
*
* @author: 陌溪
* @create: 2020-03-28-11:09
*/
public class QQServer {
static byte[] bytes = new byte[1024];
public static void main(String[] args) throws IOException {
while(true) {
ServerSocket serverSocket = new ServerSocket();
// 绑定IP地址
serverSocket.bind(new InetSocketAddress(8080));
System.out.println("服务器等待连接....");
// 阻塞
Socket socket = serverSocket.accept();
System.out.println("服务器连接....");
// 阻塞
System.out.println("等待发送客户端数据");
socket.getInputStream().read(bytes);
System.out.println("数据接收成功:" + new String(bytes));
}
}
}
首先运行Server端,然后在运行Client端
服务器等待连接....
服务器连接....
等待发送客户端数据
数据接收成功:我来发送
通过运行结果我们能发现,服务端线程会进行两次阻塞,首先第一次就是在等待连接的时候会阻塞,然后是等待数据的时候又会阻塞。
因此在服务器端,不活跃的线程,比较多,所以我们考虑单线程
我们从上面的阻塞开始说起
while(true) {
ServerSocket serverSocket = new ServerSocket();
// 绑定IP地址
serverSocket.bind(new InetSocketAddress(8080));
// 阻塞
Socket socket = serverSocket.accept();
// 阻塞
socket.getInputStream().read(bytes);
}
假设我们现在访问淘宝网页,就相当于我们本机与淘宝的服务器建立了连接,然后淘宝服务器就会等待我的请求,但是假设我只是打开了网页,什么事情都不做,如果我的线程被一直阻塞的话,那就不能为服务进行接收了,这样就会卡在这里,但是我也不能为了这个不活跃的用户单独开启线程,因为他非常消耗我们的CPU资源,这样是会,非阻塞IO的用处就来了
在提到NIO之前,我们在将刚刚的QQServer改成非阻塞版本的伪代码,也就是我们通过一个关键字,设置他不阻塞,但是因为BIO里面没有这个方法,因此他就会一直阻塞着,所以把它称为阻塞的
// 设置非阻塞
serverSocket.setConfig();
伪代码如下:
/**
* 单线程版服务器,NIO的伪代码
*
* @author: 陌溪
* @create: 2020-03-28-12:04
*/
public class OneThreadServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080));
// 套接字列表,用于存储
List<Socket> socketList = new ArrayList<>();
// 缓冲区
byte[] bytes = new byte[1024];
// 设置非阻塞
// serverSocket.setConfig();
while(true) {
// 获取连接
Socket socket = serverSocket.accept();
// 如果没人连接
if(socket == null) {
System.out.println("没人连接");
// 遍历循环socketList,套接字list
for(Socket item : socketList) {
int read = socket.getInputStream().read(bytes);
// 表示有人发送东西
if(read != 0) {
// 打印出内容
System.out.println(new String(bytes));
}
}
} else {
// 如果有人连接,把套接字放入到列表中
socketList.add(socket);
// 设置非阻塞
// serverSocket.setConfig();
// 遍历循环socketList,套接字list
// 遍历循环socketList,套接字list
for(Socket item : socketList) {
int read = socket.getInputStream().read(bytes);
// 表示有人发送东西
if(read != 0) {
// 打印出内容
System.out.println(new String(bytes));
}
}
}
}
}
}
上述也提到了,因为BIO里面不提供不阻塞的方法,因此无法将其改成非阻塞的
但是在NIO里面,就提供了让其不阻塞的方法
在之前我们需要创建通信,BIO的方法如下所示:
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080));
在NIO里面提出了通道的概念,其实代码和上面类似,只不过上面是创建了一个Socker连接,而下面是创建了一个通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));
我们创建的通道,默认是阻塞的,但是我们可以通过下面的方式,将其设置成非阻塞的
// 设置非阻塞
serverSocketChannel.configureBlocking(false);
下面我们进入while(true)的方法里面,因为原来是通过Socket获取到一个连接
// 获取连接
Socket socket = serverSocket.accept();
但是我们都知道,上述的连接是阻塞的,也就是说如果没有连接过来,它会一直阻塞的,因此Java提出了一个新的类,SockerChannel,它里面 提供了非阻塞的方法
// 设置非阻塞
serverSocketChannel.configureBlocking(false);
完整代码:
/**
* NIO版QQ服务器
*
* @author: 陌溪
* @create: 2020-03-28-12:16
*/
public class QQServerByNIO {
public static void main(String[] args) throws IOException {
// 创建一个通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));
// 定义list用于存储SocketChannel,也就是非阻塞的连接
List<SocketChannel> socketChannelList = new ArrayList<>();
byte [] bytes = new byte[1024];
// 缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 设置非阻塞
serverSocketChannel.configureBlocking(false);
while(true) {
SocketChannel socketChannel = serverSocketChannel.accept();
// 但无人连接的时候
if(socketChannel == null) {
// 睡眠一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("无人连接");
for(SocketChannel item: socketChannelList) {
int len = item.read(byteBuffer);
if(len > 0) {
// 切换成读模式
byteBuffer.flip();
// 打印出结果
System.out.println("读取到的数据" + new String(byteBuffer.array(), 0, len));
}
byteBuffer.clear();
}
} else {
// 但有人连接的时候
// 设置成非阻塞
socketChannel.configureBlocking(false);
// 将该通道存入到List中
socketChannelList.add(socketChannel);
for(SocketChannel item: socketChannelList) {
int len = item.read(byteBuffer);
if(len > 0) {
// 切换成读模式
byteBuffer.flip();
// 打印出结果
System.out.println("读取到的数据" + new String(byteBuffer.array(), 0, len));
}
byteBuffer.clear();
}
}
}
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。