Files
hutool/hutool-socket/src/main/java/cn/hutool/socket/nio/NioClient.java
gonggy c2bb34fd7e 1. 日志优化
2. 方法javadoc优化,增加throws
3. NioServer创建失败时自动关闭资源
2022-07-09 17:53:52 +08:00

167 lines
3.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cn.hutool.socket.nio;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.log.Log;
import cn.hutool.socket.SocketRuntimeException;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
/**
* NIO客户端
*
* @author looly
* @since 4.4.5
*/
public class NioClient implements Closeable {
private static final Log log = Log.get();
private Selector selector;
private SocketChannel channel;
private ChannelHandler handler;
/**
* 构造
*
* @param host 服务器地址
* @param port 端口
*/
public NioClient(String host, int port) {
init(new InetSocketAddress(host, port));
}
/**
* 构造
*
* @param address 服务器地址
*/
public NioClient(InetSocketAddress address) {
init(address);
}
/**
* 初始化
*
* @param address 地址和端口
* @return this
*/
public NioClient init(InetSocketAddress address) {
try {
//创建一个SocketChannel对象配置成非阻塞模式
this.channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(address);
//创建一个选择器并把SocketChannel交给selector对象
this.selector = Selector.open();
channel.register(this.selector, SelectionKey.OP_READ);
// 等待建立连接
//noinspection StatementWithEmptyBody
while (false == channel.finishConnect()){}
} catch (IOException e) {
close();
throw new IORuntimeException(e);
}
return this;
}
/**
* 设置NIO数据处理器
*
* @param handler {@link ChannelHandler}
* @return this
*/
public NioClient setChannelHandler(ChannelHandler handler){
this.handler = handler;
return this;
}
/**
* 开始监听
*/
public void listen() {
ThreadUtil.execute(() -> {
try {
doListen();
} catch (IOException e) {
log.error("Listen failed", e);
}
});
}
/**
* 开始监听
*
* @throws IOException IO异常
*/
private void doListen() throws IOException {
while (this.selector.isOpen() && 0 != this.selector.select()) {
// 返回已选择键的集合
final Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
while (keyIter.hasNext()) {
handle(keyIter.next());
keyIter.remove();
}
}
}
/**
* 处理SelectionKey
*
* @param key SelectionKey
*/
private void handle(SelectionKey key) {
// 读事件就绪
if (key.isReadable()) {
final SocketChannel socketChannel = (SocketChannel) key.channel();
try{
handler.handle(socketChannel);
} catch (Exception e){
throw new SocketRuntimeException(e);
}
}
}
/**
* 实现写逻辑<br>
* 当收到写出准备就绪的信号后,回调此方法,用户可向客户端发送消息
*
* @param datas 发送的数据
* @return this
*/
public NioClient write(ByteBuffer... datas) {
try {
this.channel.write(datas);
} catch (IOException e) {
throw new IORuntimeException(e);
}
return this;
}
/**
* 获取SocketChannel
*
* @return SocketChannel
* @since 5.3.10
*/
public SocketChannel getChannel() {
return this.channel;
}
@Override
public void close() {
IoUtil.close(this.selector);
IoUtil.close(this.channel);
}
}