JAVA NIO之SocketChannel
1. 简述
NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接、I/O处理问题的有效方式。
Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道
2. 特点
1. 非阻塞
2. 单线程
3. 轮循
3. 解决问题
当我们使用socket进行通信时, 建立后的链接需保持通信状态则不可关闭, 会启动分线程进行并发通信, 其中大部分时间会是阻塞状态,等待接收消息, 会造成大量的线程阻塞, 对于cpu需要维护线程之间的切换和调用, 在高并发是性能非常低, 因此使用nio可有效的提高效率
4. demo功能
客户端每隔2秒发送一次数据到服务器端, 服务器在其消息前加hello并回复客户端信息
- server端使用非阻塞模式单线程轮循的方式进行与client端通信处理
- client端使用2个线程进行运作, 一个线程用于定时发送消息到server端, 另一个线程用于读取server端消息
5. 工作原理
1. client注册SocketChannel到server中
第一步: 创建ServerSocketChannel服务器端并绑定通信端口8000, 存入selector挑选器keys中, 并注册accept事件
第二步: 客户端链接服务器的8000端口, 与服务器端进行通信,触发服务器端的accept事件
第三步: 挑选器挑选出本次需要执行的keys (ServerSocketChannel)存入挑选结果及selectedKeys中, 等待执行
第四步: 执行ServerSockerChannel.accept处理方式, 即获取与客户端client1通信的SocketChannel对象, 并未其注册read事件, 存储到挑选器中
第五步: 执行完本次挑选的结果集后, 清空selectedKeys集合即可
后续客户端连接服务器便重复第二,三,四步即可
2. client与server端的通信
第一步: client1和client3发送消息到server, 触发服务器端的read事件
第二步: 挑选器挑选出本次需要执行的keys (ServerSocketChannel)存入挑选结果及selectedKeys中, 等待执行
第三步: 遍历筛选出来的client1和client2的链接, 分别读取数据, 并返回响应消息写入到SocketChannel中
第四步: client通过SocketChannel接收server端发送的消息, 进行处理
重复以上步骤即可完成server和client端的通信
3. 特点
在server端, 使用selector的select方法进行轮循挑选触发事件的链接, 使用单线程即可完成与客户端的通信过程, 极大的提高了服务器端的性能, 在高并发时非常实用
6. 代码示例
1. ServerChannel
package pro.nio.socket;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
/**
* nio之socket通信服务器端
*/
public class ServerChannel {
public static void main(String[] args) throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 开启挑选器
Selector selector = Selector.open();
// 开启ServerSocketChannel通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress("localhost", 8000));
// 设置非阻塞模式
serverSocketChannel.configureBlocking(false);
// 在挑选器中注册通道(服务器通道)
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT)