项目背景:
数传服务器项目,需对 与外界交互的 报文进行加/解密,提高数传服务器的报文处理能力;
因为数传服务器需随 便携设备 一起工作,所以并不能简单的依靠增加设备来提高的报文处理能力,因此项目改进点只能是提高单个设备的报文处理能力
目前数传服务器的 IO 已通过 NIO 实现,现自研以 AIO 的方式实现 Demo
相关说明:
数传服务器 属于 服务端功能,对接收到的报文进行 加/解密 后,再转发
目前数传服务器的 IO 已通过 NIO 实现,现自研以 AIO 的方式实现 Demo
demo说明:
- AioServer :封装了 AIO 的 Server 实现,支持不间断的处理 接收到的报文,予以处理后再发送出去;(回调方式实现)
- AioClient :封装了 AIO 的 Client 实现,支持从控制台接受输入作为报文,发送给客户端
NIO 的原理与基本概念,参考:
- https://www.cnblogs.com/Theshy/p/7696313.html
- https://blog.youkuaiyun.com/zmx729618/article/details/53171553
Demo实现
主要有两个类:
- AioServer:封装了 AIO 的 Server 实现,支持不间断的处理 接收到的报文,予以处理后再发送出去;(回调方式实现)
- AioClient :封装了 AIO 的 Client 实现,支持从控制台接受输入作为报文,发送给客户端
AioServer
封装了 AIO 的 Server 实现,支持不间断的处理 接收到的报文,予以处理后再发送出去;(回调方式实现)
package com.wj.io.aio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.*;
import java.util.function.Function;
/**
* AIO 服务端,实现 不间断的 报文 收-处理-发 的过程
*/
public class AioServer implements Runnable {
/**
* 收发消息的编码格式
*/
private final Charset utf8 = Charset.forName("UTF-8");
/**
* 服务端的处理逻辑(Function Interface)
*/
private Function<String, String> service;
/**
* 服务端地址
*/
private InetSocketAddress localAddress;
/**
* 异步 Channel 的分组管理器,它可以实现资源共享
*/
private AsynchronousChannelGroup asynchronousChannelGroup = null;
/**
* AIO 服务端 Channel
*/
private AsynchronousServerSocketChannel serverChannel = null;
public AioServer(Function<String, String> service, int port) {
this.service = service;
this.localAddress = new InetSocketAddress(port);
}
public AioServer(Function<String, String> service, String hostname, int port) {
this.service = service;
this.localAddress = new InetSocketAddress(hostname, port);
}
@Override
public void run() {
try {
init();
serverChannel.accept(this, new CompletionHandler<AsynchronousSocketChannel, AioServer>() {
@Override
public void completed(AsynchronousSocketChannel socketChannel, AioServer aioServer) {
System.out.println("==============================================================");
System.out.println("server process begin...");
try {
System.out.println("client host: " + socketChannel.getRemoteAddress());
ByteBuffer buffer = ByteBuffer.allocate(1024);
socketChannel.read(buffer, buffer, new ReadCompletionHandler(socketChannel));
} catch (Exception e) {
/* TODO 结合具体业务逻辑处理... */
} finally {
// 监听新的connect,递归调用
aioServer.serverChannel.accept(aioServer, this);
}
}
@Override
public void failed(Throwable e, AioServer attachment) {
/* TODO 结合具体业务逻辑处理... */
}
});
} catch (Exception e) {
/* TODO 结合具体业务逻辑处理... */
}
}
private class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {
final AsynchronousSocketChannel socketChannel;
public ReadCompletionHandler(AsynchronousSocketChannel socketChannel) {
this.socketChannel = socketChannel;
}
@Override
public void completed(Integer result, ByteBuffer buffer) {
buffer.flip();
System.out.println("received : " + utf8.decode(buffer));
buffer.rewind();
/* TODO service 服务处理 收到的 报文,并加以处理 */
String rspMsg = service.apply(utf8.decode(buffer).toString());
socketChannel.write(ByteBuffer.wrap(rspMsg.getBytes(StandardCharsets.UTF_8)));
ByteBuffer buffer_ = ByteBuffer.allocate(1024);
socketChannel.read(buffer_, buffer_, this);
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
/* TODO 结合具体业务逻辑处理... */
}
}
private void init() throws IOException {
this.serverChannel = AsynchronousServerSocketChannel.open(asynchronousChannelGroup);
serverChannel.bind(localAddress);
System.out.println("listening on : " + localAddress.getHostString());
}
// -----------------------------------------------------------
// setter / getter
// -----------------------------------------------------------
public void setAsynchronousChannelGroup(AsynchronousChannelGroup asynchronousChannelGroup) {
this.asynchronousChannelGroup = asynchronousChannelGroup;
}
public static void main(String[] args) throws IOException {
/* AsynchronousChannelGroup 可以理解为一个 JVM 中对于Socket相关操作的一些公共资源的代表 */
AsynchronousChannelGroup asynchronousChannelGroup = AsynchronousChannelGroup.withCachedThreadPool(Executors.newCachedThreadPool(), 10);
AioServer aioServer = new AioServer(msg -> "Msg Server send : " + msg, 8989);
/* 系统中如果只有 一个 AioServer 服务端,此处可 set null,即不通过 AsynchronousChannelGroup 来新建 AsynchronousServerSocketChannel */
aioServer.setAsynchronousChannelGroup(asynchronousChannelGroup);
new Thread(aioServer).start();
/* AIO 异步非阻塞,工作过程中不需要任何线程 监控,所以需要其他线程 保持工作状态,防止JVM关闭 */
while (true) {
}
}
}
AioClient
封装了 AIO 的 Client 实现,支持从控制台接受输入作为报文,发送给客户端
package com.wj.io.aio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* AIO Client,可以从控制台输入报文,然后发送给 AIO Server
*/
public class AioClient implements Runnable {
/**
* AIO 客务端 Channel
*/
private AsynchronousSocketChannel client;
/**
* 服务端地址
*/
private InetSocketAddress remoteAddress;
/**
* 待发送数据的消息队列
*/
private BlockingQueue<String> queue = new LinkedBlockingQueue<>();
public AioClient(String host, int port) throws IOException {
this.client = AsynchronousSocketChannel.open();
this.remoteAddress = new InetSocketAddress(host, port);
}
public static void main(String[] args) throws IOException {
AioClient client = new AioClient("127.0.0.1", 8989);
new Thread(client).start();
while (true) {
// 模拟业务数据不断放入消息队列
Scanner scanner = new Scanner(System.in);
client.queue.add(scanner.nextLine());
}
}
@Override
public void run() {
client.connect(remoteAddress, null, new CompletionHandler<Void, Object>() {
@Override
public void completed(Void result, Object attachment) {
System.out.println("============== connect to server successfully. ================");
try {
/* 当消息队列中有元素,立即取出发送给 AIO Server */
while (true) {
String msg = queue.take();
client.write(ByteBuffer.wrap(msg.getBytes(StandardCharsets.UTF_8)));
}
} catch (Exception e) {
/* TODO 结合具体业务逻辑处理... */
}
}
@Override
public void failed(Throwable exc, Object attachment) {
/* TODO 结合具体业务逻辑处理... */
}
});
final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
client.read(byteBuffer, byteBuffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer buffer) {
System.out.println(result);
System.out.println("client read data: " + new String(byteBuffer.array()));
/* 监听新的消息,递归调用 */
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
client.read(byteBuffer, byteBuffer, this);
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
/* TODO 结合具体业务逻辑处理... */
}
});
}
}
测试时先起 Server 端,再起 Client 即可。
测试过程与结果
- 启动 Server 和 Client
- 在 Client 端控制台输入 报文
3. Server 端查看收到的消息