mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-08-18 20:38:02 +08:00
nio enhancement
This commit is contained in:
13
CHANGELOG.md
13
CHANGELOG.md
@@ -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)
|
* 【captcha】 AbstractCaptcha增加getImageBase64Data方法(pr#985@Github)
|
||||||
@@ -13,7 +22,7 @@
|
|||||||
* 【core 】 MapUtil增加getXXX的默认值重载(issue#I1PTGI@Gitee)
|
* 【core 】 MapUtil增加getXXX的默认值重载(issue#I1PTGI@Gitee)
|
||||||
* 【core 】 CalendarUtil增加parseByPatterns方法(issue#993@Github)
|
* 【core 】 CalendarUtil增加parseByPatterns方法(issue#993@Github)
|
||||||
|
|
||||||
### Bug修复
|
### Bug修复#
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@@ -116,21 +116,21 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-all</artifactId>
|
||||||
<version>5.3.11</version>
|
<version>5.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Gradle
|
### Gradle
|
||||||
```
|
```
|
||||||
compile 'cn.hutool:hutool-all:5.3.11'
|
compile 'cn.hutool:hutool-all:5.4.0'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 非Maven项目
|
### 非Maven项目
|
||||||
|
|
||||||
点击以下任一链接,下载`hutool-all-X.X.X.jar`即可:
|
点击以下任一链接,下载`hutool-all-X.X.X.jar`即可:
|
||||||
|
|
||||||
- [Maven中央库1](https://repo1.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.3.11/)
|
- [Maven中央库2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.4.0/)
|
||||||
|
|
||||||
> 注意
|
> 注意
|
||||||
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
|
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
|
||||||
|
@@ -1 +1 @@
|
|||||||
5.3.11
|
5.4.0
|
||||||
|
@@ -1 +1 @@
|
|||||||
var version = '5.3.11'
|
var version = '5.4.0'
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-all</artifactId>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-aop</artifactId>
|
<artifactId>hutool-aop</artifactId>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-bloomFilter</artifactId>
|
<artifactId>hutool-bloomFilter</artifactId>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-bom</artifactId>
|
<artifactId>hutool-bom</artifactId>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-cache</artifactId>
|
<artifactId>hutool-cache</artifactId>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-captcha</artifactId>
|
<artifactId>hutool-captcha</artifactId>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-core</artifactId>
|
<artifactId>hutool-core</artifactId>
|
||||||
|
@@ -2419,7 +2419,11 @@ public class StrUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将对象转为字符串<br>
|
* 将对象转为字符串<br>
|
||||||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组
|
||||||
|
* 2、对象数组会调用Arrays.toString方法
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param obj 对象
|
* @param obj 对象
|
||||||
* @return 字符串
|
* @return 字符串
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-cron</artifactId>
|
<artifactId>hutool-cron</artifactId>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-crypto</artifactId>
|
<artifactId>hutool-crypto</artifactId>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-db</artifactId>
|
<artifactId>hutool-db</artifactId>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-dfa</artifactId>
|
<artifactId>hutool-dfa</artifactId>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-extra</artifactId>
|
<artifactId>hutool-extra</artifactId>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-http</artifactId>
|
<artifactId>hutool-http</artifactId>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-json</artifactId>
|
<artifactId>hutool-json</artifactId>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-log</artifactId>
|
<artifactId>hutool-log</artifactId>
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-poi</artifactId>
|
<artifactId>hutool-poi</artifactId>
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-script</artifactId>
|
<artifactId>hutool-script</artifactId>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-setting</artifactId>
|
<artifactId>hutool-setting</artifactId>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-socket</artifactId>
|
<artifactId>hutool-socket</artifactId>
|
||||||
|
@@ -1,13 +1,5 @@
|
|||||||
package cn.hutool.socket.aio;
|
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.IORuntimeException;
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.thread.ThreadFactoryBuilder;
|
import cn.hutool.core.thread.ThreadFactoryBuilder;
|
||||||
@@ -16,6 +8,14 @@ import cn.hutool.log.Log;
|
|||||||
import cn.hutool.log.LogFactory;
|
import cn.hutool.log.LogFactory;
|
||||||
import cn.hutool.socket.SocketConfig;
|
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服务端实现
|
* 基于AIO的Socket服务端实现
|
||||||
*
|
*
|
||||||
@@ -76,11 +76,7 @@ public class AioServer implements Closeable {
|
|||||||
* @param sync 是否阻塞
|
* @param sync 是否阻塞
|
||||||
*/
|
*/
|
||||||
public void start(boolean sync) {
|
public void start(boolean sync) {
|
||||||
try {
|
|
||||||
doStart(sync);
|
doStart(sync);
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IORuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -173,9 +169,8 @@ public class AioServer implements Closeable {
|
|||||||
* 开始监听
|
* 开始监听
|
||||||
*
|
*
|
||||||
* @param sync 是否阻塞
|
* @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.");
|
log.debug("Aio Server started, waiting for accept.");
|
||||||
|
|
||||||
// 接收客户端连接
|
// 接收客户端连接
|
||||||
|
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
@@ -2,7 +2,7 @@ package cn.hutool.socket.nio;
|
|||||||
|
|
||||||
import cn.hutool.core.io.IORuntimeException;
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
import cn.hutool.core.io.IoUtil;
|
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.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -10,12 +10,8 @@ import java.net.InetSocketAddress;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.SelectionKey;
|
import java.nio.channels.SelectionKey;
|
||||||
import java.nio.channels.Selector;
|
import java.nio.channels.Selector;
|
||||||
import java.nio.channels.ServerSocketChannel;
|
|
||||||
import java.nio.channels.SocketChannel;
|
import java.nio.channels.SocketChannel;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ThreadFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NIO客户端
|
* NIO客户端
|
||||||
@@ -24,10 +20,8 @@ import java.util.concurrent.ThreadFactory;
|
|||||||
* @since 4.4.5
|
* @since 4.4.5
|
||||||
*/
|
*/
|
||||||
public abstract class NioClient implements Closeable {
|
public abstract class NioClient implements Closeable {
|
||||||
|
|
||||||
private Selector selector;
|
private Selector selector;
|
||||||
private SocketChannel channel;
|
private SocketChannel channel;
|
||||||
private ExecutorService executorService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
@@ -59,55 +53,26 @@ public abstract class NioClient implements Closeable {
|
|||||||
//创建一个SocketChannel对象,配置成非阻塞模式
|
//创建一个SocketChannel对象,配置成非阻塞模式
|
||||||
this.channel = SocketChannel.open();
|
this.channel = SocketChannel.open();
|
||||||
channel.configureBlocking(false);
|
channel.configureBlocking(false);
|
||||||
|
channel.connect(address);
|
||||||
|
|
||||||
//创建一个选择器,并把SocketChannel交给selector对象
|
//创建一个选择器,并把SocketChannel交给selector对象
|
||||||
this.selector = Selector.open();
|
this.selector = Selector.open();
|
||||||
channel.register(selector, SelectionKey.OP_CONNECT);
|
channel.register(this.selector, SelectionKey.OP_READ);
|
||||||
|
|
||||||
//发起建立连接的请求,这里会立即返回,当连接建立完成后,SocketChannel就会被选取出来
|
// 等待建立连接
|
||||||
channel.connect(address);
|
//noinspection StatementWithEmptyBody
|
||||||
|
while (false == channel.finishConnect()){}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查连接是否建立完成
|
|
||||||
*/
|
|
||||||
public boolean waitConnect() throws IOException {
|
|
||||||
boolean isConnect = false;
|
|
||||||
while (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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyIter.remove();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (isConnect) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isConnect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始监听
|
* 开始监听
|
||||||
*/
|
*/
|
||||||
public void listen() {
|
public void listen() {
|
||||||
this.executorService = Executors.newSingleThreadExecutor(r -> {
|
ThreadUtil.execute(() -> {
|
||||||
final Thread thread = Executors.defaultThreadFactory().newThread(r);
|
|
||||||
thread.setName("nio-client-listen");
|
|
||||||
return thread;
|
|
||||||
});
|
|
||||||
this.executorService.execute(() -> {
|
|
||||||
try {
|
try {
|
||||||
doListen();
|
doListen();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -122,7 +87,7 @@ public abstract class NioClient implements Closeable {
|
|||||||
* @throws IOException IO异常
|
* @throws IOException IO异常
|
||||||
*/
|
*/
|
||||||
private void doListen() throws IOException {
|
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();
|
final Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
|
||||||
while (keyIter.hasNext()) {
|
while (keyIter.hasNext()) {
|
||||||
@@ -137,7 +102,7 @@ public abstract class NioClient implements Closeable {
|
|||||||
*
|
*
|
||||||
* @param key SelectionKey
|
* @param key SelectionKey
|
||||||
*/
|
*/
|
||||||
private void handle(SelectionKey key) throws IOException {
|
private void handle(SelectionKey key) {
|
||||||
// 读事件就绪
|
// 读事件就绪
|
||||||
if (key.isReadable()) {
|
if (key.isReadable()) {
|
||||||
final SocketChannel socketChannel = (SocketChannel) key.channel();
|
final SocketChannel socketChannel = (SocketChannel) key.channel();
|
||||||
@@ -169,14 +134,19 @@ public abstract class NioClient implements Closeable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void closeListen() {
|
/**
|
||||||
this.executorService.shutdown();
|
* 获取SocketChannel
|
||||||
|
*
|
||||||
|
* @return SocketChannel
|
||||||
|
* @since 5.3.10
|
||||||
|
*/
|
||||||
|
public SocketChannel getChannel() {
|
||||||
|
return this.channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
IoUtil.close(this.selector);
|
IoUtil.close(this.selector);
|
||||||
IoUtil.close(this.channel);
|
IoUtil.close(this.channel);
|
||||||
closeListen();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,12 +2,11 @@ package cn.hutool.socket.nio;
|
|||||||
|
|
||||||
import cn.hutool.core.io.IORuntimeException;
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.log.Log;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.nio.channels.SelectableChannel;
|
|
||||||
import java.nio.channels.SelectionKey;
|
import java.nio.channels.SelectionKey;
|
||||||
import java.nio.channels.Selector;
|
import java.nio.channels.Selector;
|
||||||
import java.nio.channels.ServerSocketChannel;
|
import java.nio.channels.ServerSocketChannel;
|
||||||
@@ -20,10 +19,14 @@ import java.util.Iterator;
|
|||||||
* @author looly
|
* @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 Selector selector;
|
||||||
private ServerSocketChannel serverSocketChannel;
|
private ServerSocketChannel serverSocketChannel;
|
||||||
|
private ChannelHandler handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
@@ -45,23 +48,52 @@ public abstract class NioServer implements Closeable {
|
|||||||
// 打开服务器套接字通道
|
// 打开服务器套接字通道
|
||||||
this.serverSocketChannel = ServerSocketChannel.open();
|
this.serverSocketChannel = ServerSocketChannel.open();
|
||||||
// 设置为非阻塞状态
|
// 设置为非阻塞状态
|
||||||
serverSocketChannel.configureBlocking(false);
|
this.serverSocketChannel.configureBlocking(false);
|
||||||
// 获取通道相关联的套接字
|
|
||||||
final ServerSocket serverSocket = serverSocketChannel.socket();
|
|
||||||
// 绑定端口号
|
// 绑定端口号
|
||||||
serverSocket.bind(address);
|
this.serverSocketChannel.bind(address);
|
||||||
|
|
||||||
// 打开一个选择器
|
// 打开一个选择器
|
||||||
selector = Selector.open();
|
this.selector = Selector.open();
|
||||||
// 服务器套接字注册到Selector中 并指定Selector监控连接事件
|
// 服务器套接字注册到Selector中 并指定Selector监控连接事件
|
||||||
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
|
this.serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.debug("Server listen on: [{}]...", address);
|
||||||
|
|
||||||
return this;
|
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异常
|
* @throws IOException IO异常
|
||||||
*/
|
*/
|
||||||
private void doListen() throws IOException {
|
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();
|
final Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
|
||||||
while (keyIter.hasNext()) {
|
while (keyIter.hasNext()) {
|
||||||
@@ -97,35 +129,13 @@ public abstract class NioServer implements Closeable {
|
|||||||
private void handle(SelectionKey key) {
|
private void handle(SelectionKey key) {
|
||||||
// 有客户端接入此服务端
|
// 有客户端接入此服务端
|
||||||
if (key.isAcceptable()) {
|
if (key.isAcceptable()) {
|
||||||
// 获取通道 转化为要处理的类型
|
ACCEPT_HANDLER.completed((ServerSocketChannel) key.channel(), this);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读事件就绪
|
// 读事件就绪
|
||||||
if (key.isReadable()) {
|
if (key.isReadable()) {
|
||||||
final SocketChannel socketChannel = (SocketChannel) key.channel();
|
final SocketChannel socketChannel = (SocketChannel) key.channel();
|
||||||
read(socketChannel);
|
handler.handle(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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,42 +144,4 @@ public abstract class NioServer implements Closeable {
|
|||||||
IoUtil.close(this.selector);
|
IoUtil.close(this.selector);
|
||||||
IoUtil.close(this.serverSocketChannel);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,13 +1,10 @@
|
|||||||
package cn.hutool.socket;
|
package cn.hutool.socket.aio;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.socket.aio.AioClient;
|
|
||||||
import cn.hutool.socket.aio.AioSession;
|
import java.net.InetSocketAddress;
|
||||||
import cn.hutool.socket.aio.SimpleIoAction;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
public class AioClientTest {
|
public class AioClientTest {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
@@ -1,15 +1,12 @@
|
|||||||
package cn.hutool.socket;
|
package cn.hutool.socket.aio;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.io.BufferUtil;
|
import cn.hutool.core.io.BufferUtil;
|
||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.log.StaticLog;
|
import cn.hutool.log.StaticLog;
|
||||||
import cn.hutool.socket.aio.AioServer;
|
|
||||||
import cn.hutool.socket.aio.AioSession;
|
import java.nio.ByteBuffer;
|
||||||
import cn.hutool.socket.aio.SimpleIoAction;
|
|
||||||
|
|
||||||
public class AioServerTest {
|
public class AioServerTest {
|
||||||
|
|
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-system</artifactId>
|
<artifactId>hutool-system</artifactId>
|
||||||
|
2
pom.xml
2
pom.xml
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.3.11-SNAPSHOT</version>
|
<version>5.4.0-SNAPSHOT</version>
|
||||||
<name>hutool</name>
|
<name>hutool</name>
|
||||||
<description>Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。</description>
|
<description>Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。</description>
|
||||||
<url>https://github.com/looly/hutool</url>
|
<url>https://github.com/looly/hutool</url>
|
||||||
|
Reference in New Issue
Block a user