nio enhancement

This commit is contained in:
Looly 2020-08-01 18:58:44 +08:00
parent f527c7af39
commit 04917da6e2
38 changed files with 429 additions and 427 deletions

View File

@ -3,7 +3,16 @@
-------------------------------------------------------------------------------------------------------------
## 5.3.11 (2020-08-01)
# 5.4.0 (2020-08-01)
### 新特性
* 【socket】 对NioServer和NioClient改造pr#992@Github
### Bug修复#
-------------------------------------------------------------------------------------------------------------
# 5.3.11 (2020-08-01)
### 新特性
* 【captcha】 AbstractCaptcha增加getImageBase64Data方法pr#985@Github
@ -13,7 +22,7 @@
* 【core 】 MapUtil增加getXXX的默认值重载issue#I1PTGI@Gitee
* 【core 】 CalendarUtil增加parseByPatterns方法issue#993@Github
### Bug修复
### Bug修复#
-------------------------------------------------------------------------------------------------------------

View File

@ -116,21 +116,21 @@ Hutool的存在就是为了减少代码搜索成本避免网络上参差不
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.11</version>
<version>5.4.0</version>
</dependency>
```
### Gradle
```
compile 'cn.hutool:hutool-all:5.3.11'
compile 'cn.hutool:hutool-all:5.4.0'
```
### 非Maven项目
点击以下任一链接,下载`hutool-all-X.X.X.jar`即可:
- [Maven中央库1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.3.11/)
- [Maven中央库2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.3.11/)
- [Maven中央库1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/)
- [Maven中央库2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.4.0/)
> 注意
> Hutool 5.x支持JDK8+对Android平台没有测试不能保证所有工具类或工具方法可用。

View File

@ -1 +1 @@
5.3.11
5.4.0

View File

@ -1 +1 @@
var version = '5.3.11'
var version = '5.4.0'

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-all</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-aop</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-bloomFilter</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-bom</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-cache</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-captcha</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-core</artifactId>

View File

@ -2419,7 +2419,11 @@ public class StrUtil {
/**
* 将对象转为字符串<br>
* 1Byte数组和ByteBuffer会被转换为对应字符串的数组 2对象数组会调用Arrays.toString方法
*
* <pre>
* 1Byte数组和ByteBuffer会被转换为对应字符串的数组
* 2对象数组会调用Arrays.toString方法
* </pre>
*
* @param obj 对象
* @return 字符串

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-cron</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-crypto</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-db</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-dfa</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-extra</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-http</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-json</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-log</artifactId>

View File

@ -8,7 +8,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-poi</artifactId>

View File

@ -8,7 +8,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-script</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-setting</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-socket</artifactId>

View File

@ -1,13 +1,5 @@
package cn.hutool.socket.aio;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketOption;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.thread.ThreadFactoryBuilder;
@ -16,6 +8,14 @@ import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import cn.hutool.socket.SocketConfig;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketOption;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
/**
* 基于AIO的Socket服务端实现
*
@ -76,11 +76,7 @@ public class AioServer implements Closeable {
* @param sync 是否阻塞
*/
public void start(boolean sync) {
try {
doStart(sync);
} catch (IOException e) {
throw new IORuntimeException(e);
}
doStart(sync);
}
/**
@ -173,9 +169,8 @@ public class AioServer implements Closeable {
* 开始监听
*
* @param sync 是否阻塞
* @throws IOException IO异常
*/
private void doStart(boolean sync) throws IOException {
private void doStart(boolean sync) {
log.debug("Aio Server started, waiting for accept.");
// 接收客户端连接

View File

@ -0,0 +1,38 @@
package cn.hutool.socket.nio;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.log.StaticLog;
import java.io.IOException;
import java.nio.channels.CompletionHandler;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
/**
* 接入完成回调单例使用
*
* @author looly
*/
public class AcceptHandler implements CompletionHandler<ServerSocketChannel, NioServer> {
@Override
public void completed(ServerSocketChannel serverSocketChannel, NioServer nioServer) {
SocketChannel socketChannel;
try {
// 获取连接到此服务器的客户端通道
socketChannel = serverSocketChannel.accept();
StaticLog.debug("Client [{}] accepted.", socketChannel.getRemoteAddress());
} catch (IOException e) {
throw new IORuntimeException(e);
}
// SocketChannel通道的可读事件注册到Selector中
NioUtil.registerChannel(nioServer.getSelector(), socketChannel, Operation.READ);
}
@Override
public void failed(Throwable exc, NioServer nioServer) {
StaticLog.error(exc);
}
}

View File

@ -0,0 +1,17 @@
package cn.hutool.socket.nio;
import java.nio.channels.SocketChannel;
/**
* NIO数据处理接口通过实现此接口可以从{@link SocketChannel}中读写数据
*
*/
public interface ChannelHandler {
/**
* 处理NIO数据
*
* @param socketChannel {@link SocketChannel}
*/
void handle(SocketChannel socketChannel);
}

View File

@ -2,7 +2,7 @@ package cn.hutool.socket.nio;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import cn.hutool.core.thread.ThreadUtil;
import java.io.Closeable;
import java.io.IOException;
@ -10,12 +10,8 @@ import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
/**
* NIO客户端
@ -24,159 +20,133 @@ import java.util.concurrent.ThreadFactory;
* @since 4.4.5
*/
public abstract class NioClient implements Closeable {
private Selector selector;
private SocketChannel channel;
private ExecutorService executorService;
/**
* 构造
*
* @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);
//创建一个选择器并把SocketChannel交给selector对象
this.selector = Selector.open();
channel.register(selector, SelectionKey.OP_CONNECT);
//发起建立连接的请求这里会立即返回当连接建立完成后SocketChannel就会被选取出来
channel.connect(address);
} catch (IOException e) {
throw new IORuntimeException(e);
}
return this;
}
private Selector selector;
private SocketChannel channel;
/**
* 检查连接是否建立完成
* 构造
*
* @param host 服务器地址
* @param port 端口
*/
public boolean waitConnect() throws IOException {
boolean isConnect = false;
while (0 != this.selector.select()) {
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) {
throw new IORuntimeException(e);
}
return this;
}
/**
* 开始监听
*/
public void listen() {
ThreadUtil.execute(() -> {
try {
doListen();
} catch (IOException e) {
e.printStackTrace();
}
});
}
/**
* 开始监听
*
* @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()) {
//连接建立完成
SelectionKey key = keyIter.next();
if (key.isConnectable()) {
if (this.channel.finishConnect()) {
this.channel.register(selector, SelectionKey.OP_READ);
isConnect = true;
}
}
handle(keyIter.next());
keyIter.remove();
break;
}
if (isConnect) {
break;
}
}
return isConnect;
}
/**
* 开始监听
*/
public void listen() {
this.executorService = Executors.newSingleThreadExecutor(r -> {
final Thread thread = Executors.defaultThreadFactory().newThread(r);
thread.setName("nio-client-listen");
return thread;
});
this.executorService.execute(() -> {
try {
doListen();
} catch (IOException e) {
e.printStackTrace();
}
});
}
/**
* 开始监听
*
* @throws IOException IO异常
*/
private void doListen() throws IOException {
while (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) throws IOException {
// 读事件就绪
if (key.isReadable()) {
final SocketChannel socketChannel = (SocketChannel) key.channel();
read(socketChannel);
}
}
/**
* 处理读事件<br>
* 当收到读取准备就绪的信号后回调此方法用户可读取从客户端传出来的消息
*
* @param socketChannel SocketChannel
*/
protected abstract void read(SocketChannel socketChannel);
/**
* 实现写逻辑<br>
* 当收到写出准备就绪的信号后回调此方法用户可向客户端发送消息
*
* @param datas 发送的数据
* @return this
*/
public NioClient write(ByteBuffer... datas) {
try {
this.channel.write(datas);
} catch (IOException e) {
throw new IORuntimeException(e);
}
return this;
}
public void closeListen() {
this.executorService.shutdown();
/**
* 处理SelectionKey
*
* @param key SelectionKey
*/
private void handle(SelectionKey key) {
// 读事件就绪
if (key.isReadable()) {
final SocketChannel socketChannel = (SocketChannel) key.channel();
read(socketChannel);
}
}
@Override
public void close() {
IoUtil.close(this.selector);
IoUtil.close(this.channel);
closeListen();
}
/**
* 处理读事件<br>
* 当收到读取准备就绪的信号后回调此方法用户可读取从客户端传出来的消息
*
* @param socketChannel SocketChannel
*/
protected abstract void read(SocketChannel socketChannel);
/**
* 实现写逻辑<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);
}
}

View File

@ -2,12 +2,11 @@ package cn.hutool.socket.nio;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.log.Log;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
@ -20,10 +19,14 @@ import java.util.Iterator;
* @author looly
*
*/
public abstract class NioServer implements Closeable {
public class NioServer implements Closeable {
private static final Log log = Log.get();
private static final AcceptHandler ACCEPT_HANDLER = new AcceptHandler();
private Selector selector;
private ServerSocketChannel serverSocketChannel;
private ChannelHandler handler;
/**
* 构造
@ -45,23 +48,52 @@ public abstract class NioServer implements Closeable {
// 打开服务器套接字通道
this.serverSocketChannel = ServerSocketChannel.open();
// 设置为非阻塞状态
serverSocketChannel.configureBlocking(false);
// 获取通道相关联的套接字
final ServerSocket serverSocket = serverSocketChannel.socket();
this.serverSocketChannel.configureBlocking(false);
// 绑定端口号
serverSocket.bind(address);
this.serverSocketChannel.bind(address);
// 打开一个选择器
selector = Selector.open();
this.selector = Selector.open();
// 服务器套接字注册到Selector中 并指定Selector监控连接事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
this.serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
throw new IORuntimeException(e);
}
log.debug("Server listen on: [{}]...", address);
return this;
}
/**
* 设置NIO数据处理器
*
* @param handler {@link ChannelHandler}
* @return this
*/
public NioServer setChannelHandler(ChannelHandler handler){
this.handler = handler;
return this;
}
/**
* 获取{@link Selector}
*
* @return {@link Selector}
*/
public Selector getSelector(){
return this.selector;
}
/**
* 启动NIO服务端即开始监听
*
* @see #listen()
*/
public void start(){
listen();
}
/**
* 开始监听
*/
@ -79,7 +111,7 @@ public abstract class NioServer implements Closeable {
* @throws IOException IO异常
*/
private void doListen() throws IOException {
while (0 != this.selector.select()) {
while (this.selector.isOpen() && 0 != this.selector.select()) {
// 返回已选择键的集合
final Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
while (keyIter.hasNext()) {
@ -97,35 +129,13 @@ public abstract class NioServer implements Closeable {
private void handle(SelectionKey key) {
// 有客户端接入此服务端
if (key.isAcceptable()) {
// 获取通道 转化为要处理的类型
final ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel;
try {
// 获取连接到此服务器的客户端通道
socketChannel = server.accept();
} catch (IOException e) {
throw new IORuntimeException(e);
}
// SocketChannel通道的可读事件注册到Selector中
registerChannel(selector, socketChannel, Operation.READ);
ACCEPT_HANDLER.completed((ServerSocketChannel) key.channel(), this);
}
// 读事件就绪
if (key.isReadable()) {
final SocketChannel socketChannel = (SocketChannel) key.channel();
read(socketChannel);
// SocketChannel通道的可写事件注册到Selector中
registerChannel(selector, socketChannel, Operation.WRITE);
}
// 写事件就绪
if (key.isWritable()) {
final SocketChannel socketChannel = (SocketChannel) key.channel();
write(socketChannel);
// SocketChannel通道的可读事件注册到Selector中
registerChannel(selector, socketChannel, Operation.READ);
handler.handle(socketChannel);
}
}
@ -134,42 +144,4 @@ public abstract class NioServer implements Closeable {
IoUtil.close(this.selector);
IoUtil.close(this.serverSocketChannel);
}
/**
* 处理读事件<br>
* 当收到读取准备就绪的信号后回调此方法用户可读取从客户端传出来的消息
*
* @param socketChannel SocketChannel
*/
protected abstract void read(SocketChannel socketChannel);
/**
* 实现写逻辑<br>
* 当收到写出准备就绪的信号后回调此方法用户可向客户端发送消息
*
* @param socketChannel SocketChannel
*/
protected abstract void write(SocketChannel socketChannel);
/**
* 注册通道到指定Selector上
*
* @param selector Selector
* @param channel 通道
* @param ops 注册的通道监听类型
*/
private void registerChannel(Selector selector, SelectableChannel channel, Operation ops) {
if (channel == null) {
return;
}
try {
channel.configureBlocking(false);
// 注册通道
//noinspection MagicConstant
channel.register(selector, ops.getValue());
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
}

View File

@ -0,0 +1,37 @@
package cn.hutool.socket.nio;
import cn.hutool.core.io.IORuntimeException;
import java.io.IOException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.Selector;
/**
* NIO工具类
*
* @since 5.4.0
*/
public class NioUtil {
/**
* 注册通道的指定操作到指定Selector上
*
* @param selector Selector
* @param channel 通道
* @param ops 注册的通道监听操作类型
*/
public static void registerChannel(Selector selector, SelectableChannel channel, Operation ops) {
if (channel == null) {
return;
}
try {
channel.configureBlocking(false);
// 注册通道
//noinspection MagicConstant
channel.register(selector, ops.getValue());
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
}

View File

@ -1,67 +0,0 @@
package cn.hutool.socket;
import cn.hutool.core.util.StrUtil;
import cn.hutool.socket.nio.NioClient;
import lombok.SneakyThrows;
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.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
public class NioClientTest {
@SneakyThrows
public static void main(String[] args) {
NioClient client = new NioClient("127.0.0.1", 8080) {
@SneakyThrows
@Override
protected void read(SocketChannel sc) {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
//从channel读数据到缓冲区
int readBytes = sc.read(readBuffer);
if (readBytes > 0){
//Flips this buffer. The limit is set to the current position and then
// the position is set to zero就是表示要从起始位置开始读取数据
readBuffer.flip();
//eturns the number of elements between the current position and the limit.
// 要读取的字节长度
byte[] bytes = new byte[readBuffer.remaining()];
//将缓冲区的数据读到bytes数组
readBuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("the read client receive message: " + body);
}else if(readBytes < 0){
sc.close();
}
}
};
if (client.waitConnect()) {
client.listen();
}
ByteBuffer buffer = ByteBuffer.wrap("client 发生到 server".getBytes());
client.write(buffer);
buffer = ByteBuffer.wrap("client 再次发生到 server".getBytes());
client.write(buffer);
/**
* 在控制台向服务器端发送数据
*/
System.out.println("请在下方畅所欲言");
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String request = scanner.nextLine();
if (request != null && request.trim().length() > 0) {
client.write(
Charset.forName("UTF-8")
.encode("测试client" + ": " + request));
}
}
}
}

View File

@ -1,82 +0,0 @@
package cn.hutool.socket;
import cn.hutool.core.util.StrUtil;
import cn.hutool.socket.nio.NioServer;
import lombok.SneakyThrows;
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;
public class NioServerTest {
public static void main(String[] args) {
NioServer server = new NioServer(8080) {
@SneakyThrows
@Override
protected void read(SocketChannel sc) {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
//从channel读数据到缓冲区
int readBytes = sc.read(readBuffer);
if (readBytes > 0){
//Flips this buffer. The limit is set to the current position and then
// the position is set to zero就是表示要从起始位置开始读取数据
readBuffer.flip();
//eturns the number of elements between the current position and the limit.
// 要读取的字节长度
byte[] bytes = new byte[readBuffer.remaining()];
//将缓冲区的数据读到bytes数组
readBuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("the read server receive message: " + body);
doWrite(sc, body);
}else if(readBytes < 0){
sc.close();
}
}
@SneakyThrows
@Override
protected void write(SocketChannel sc) {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
//从channel读数据到缓冲区
int readBytes = sc.read(readBuffer);
if (readBytes > 0){
//Flips this buffer. The limit is set to the current position and then
// the position is set to zero就是表示要从起始位置开始读取数据
readBuffer.flip();
//eturns the number of elements between the current position and the limit.
// 要读取的字节长度
byte[] bytes = new byte[readBuffer.remaining()];
//将缓冲区的数据读到bytes数组
readBuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("the write server receive message: " + body);
doWrite(sc, body);
}else if(readBytes < 0){
sc.close();
}
}
};
server.listen();
}
public static void doWrite(SocketChannel channel, String response) throws IOException {
response = "我们已收到消息:"+response;
if(!StrUtil.isBlank(response)){
byte [] bytes = response.getBytes();
//分配一个bytes的length长度的ByteBuffer
ByteBuffer write = ByteBuffer.allocate(bytes.length);
//将返回数据写入缓冲区
write.put(bytes);
write.flip();
//将缓冲数据写入渠道返回给客户端
channel.write(write);
}
}
}

View File

@ -1,13 +1,10 @@
package cn.hutool.socket;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
package cn.hutool.socket.aio;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.StrUtil;
import cn.hutool.socket.aio.AioClient;
import cn.hutool.socket.aio.AioSession;
import cn.hutool.socket.aio.SimpleIoAction;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
public class AioClientTest {
public static void main(String[] args) {

View File

@ -1,15 +1,12 @@
package cn.hutool.socket;
import java.nio.ByteBuffer;
package cn.hutool.socket.aio;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.BufferUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.StaticLog;
import cn.hutool.socket.aio.AioServer;
import cn.hutool.socket.aio.AioSession;
import cn.hutool.socket.aio.SimpleIoAction;
import java.nio.ByteBuffer;
public class AioServerTest {

View File

@ -0,0 +1,59 @@
package cn.hutool.socket.nio;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
public class NioClientTest {
@SneakyThrows
public static void main(String[] args) {
NioClient client = new NioClient("127.0.0.1", 8080) {
@SneakyThrows
@Override
protected void read(SocketChannel sc) {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
//从channel读数据到缓冲区
int readBytes = sc.read(readBuffer);
if (readBytes > 0) {
//Flips this buffer. The limit is set to the current position and then
// the position is set to zero就是表示要从起始位置开始读取数据
readBuffer.flip();
//returns the number of elements between the current position and the limit.
// 要读取的字节长度
byte[] bytes = new byte[readBuffer.remaining()];
//将缓冲区的数据读到bytes数组
readBuffer.get(bytes);
String body = StrUtil.utf8Str(bytes);
Console.log("the read client receive message: " + body);
} else if (readBytes < 0) {
sc.close();
}
}
};
client.listen();
ByteBuffer buffer = ByteBuffer.wrap("client 发生到 server".getBytes());
client.write(buffer);
buffer = ByteBuffer.wrap("client 再次发生到 server".getBytes());
client.write(buffer);
// 在控制台向服务器端发送数据
Console.log("请在下方畅所欲言");
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String request = scanner.nextLine();
if (request != null && request.trim().length() > 0) {
client.write(
CharsetUtil.CHARSET_UTF_8
.encode("测试client" + ": " + request));
}
}
}
}

View File

@ -0,0 +1,56 @@
package cn.hutool.socket.nio;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.StrUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NioServerTest {
public static void main(String[] args) {
NioServer server = new NioServer(8080);
server.setChannelHandler((sc)->{
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
try{
//从channel读数据到缓冲区
int readBytes = sc.read(readBuffer);
if (readBytes > 0) {
//Flips this buffer. The limit is set to the current position and then
// the position is set to zero就是表示要从起始位置开始读取数据
readBuffer.flip();
//eturns the number of elements between the current position and the limit.
// 要读取的字节长度
byte[] bytes = new byte[readBuffer.remaining()];
//将缓冲区的数据读到bytes数组
readBuffer.get(bytes);
String body = StrUtil.utf8Str(bytes);
Console.log("the read server receive message: " + body);
doWrite(sc, body);
} else if (readBytes < 0) {
IoUtil.close(sc);
}
} catch (IOException e){
throw new IORuntimeException(e);
}
});
server.listen();
}
public static void doWrite(SocketChannel channel, String response) throws IOException {
response = "我们已收到消息:" + response;
if (!StrUtil.isBlank(response)) {
byte[] bytes = response.getBytes();
//分配一个bytes的length长度的ByteBuffer
ByteBuffer write = ByteBuffer.allocate(bytes.length);
//将返回数据写入缓冲区
write.put(bytes);
write.flip();
//将缓冲数据写入渠道返回给客户端
channel.write(write);
}
}
}

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
</parent>
<artifactId>hutool-system</artifactId>

View File

@ -8,7 +8,7 @@
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.11-SNAPSHOT</version>
<version>5.4.0-SNAPSHOT</version>
<name>hutool</name>
<description>Hutool是一个小而全的Java工具类库通过静态方法封装降低相关API的学习成本提高工作效率使Java拥有函数式语言般的优雅让Java语言也可以“甜甜的”。</description>
<url>https://github.com/looly/hutool</url>