??个人主页:水滴V2大家好,我是水滴~~
支持水滴:点赞、收藏?、留言
文章目录
- 服务端API
-
- 创建服务端套接字的异步通道
- 接收客户端连接
- 读取客户端数据
- 客户端API
-
- 创建客户端套接字的异步通道
- 连接服务端
- 向服务端写入数据
服务端API 创建服务端套接字的异步通道 Java AIO为我们提供了
AsynchronousServerSocketChannel
类,它是一个面向流监听套接字的异步通道,可以通过该类的open
方法创建一个实例。新创建的异步服务器套接字通道还没有绑定本地地址(IP地址+端口),通过
bind
方法来绑定一个本地地址,并且启动了监听。此时客户端已经可以连接了。该通道是线程安全的,但同时只能有一个ACCEPT操作是进行中的。如果前一个线程还未完成ACCEPT操作,后一个线程发起ACCEPT操作,将会抛出
AcceptPendingException
异常。// 打开一个异步的 ServerSocket 通道
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
// 绑定本地地址
serverSocketChannel.bind(new InetSocketAddress(8080));
接收客户端连接
AsynchronousServerSocketChannel
类提供了两个accept
方法,它们都是异步的,不会阻塞。下面我们使用带参数的方法来完成ACCEPT操作。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cjc62Af8-1649771737856)(https://secure2.wostatic.cn/static/gBmK7M8b6tGTcXMUaRJb8L/image.png)]
第一个参数是要附加到I/O操作的对象,可以为
null
。第二个参数是一个连接的结果处理器,即当有连接完成后,会自动调用该处理器
CompletionHandler
,来执行后面的内容。该处理器是一个泛型接口,第一个泛型类型是AsynchronousSocketChannel
类,第二个泛型类型是前面提到的附加对象。下面的示例代码,我们将
AsynchronousServerSocketChannel
对象做为附加对象,并使用函数式编程来创建CompletionHandler
接口的实现。CompletionHandler
接口有两个方法需要我们来实现,completed
方法用于客户端连接完成时调用;failed
用于客户端连接失败时调用。不管连接是成功还是失败,都要继续接收下一个连接,以保证服务端的工作。
// 接收客户端连接
serverSocketChannel.accept(serverSocketChannel, new CompletionHandler() {
@Override
public void completed(AsynchronousSocketChannel result, AsynchronousServerSocketChannel attachment) {
try {
// 一个客户端连接后,继续接收下一个连接
attachment.accept(attachment, this);
// 申请一个1024个字节的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 读取客户端数据
result.read(byteBuffer, byteBuffer, new ReadCompletionHandler(result));
} catch (IOException e) {
e.printStackTrace();
}
}@Override
public void failed(Throwable exc, AsynchronousServerSocketChannel attachment) {
// 一个客户端连失败,继续接收下一个连接
attachment.accept(attachment, this);
}
});
读取客户端数据
AsynchronousServerSocketChannel
通道只负责监听客户端连接。当连接建立成功后,会为该连接创建一个AsynchronousSocketChannel
通道,后续服务端与该客户端的I/O操作,都是通过该通道完成的。该类的
read
方法用来读取客户端数据,此方法的第一个参数为ByteBuffer
缓冲区,第二个参数为附加对象,第三个参数是读取数据的结果处理器。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BgcC9Tef-1649771737857)(https://secure2.wostatic.cn/static/eRpdrux1ummfcYxTw6hzja/image.png)]
该方法启动了一个异步读取操作,会将一个字节序列从该通道中读入给定的缓冲区中。处理程序是一个在读取操作完成(或失败)时调用的结果处理程序。当读取操作完成后,会传递一个读取的字节数,如果通道中流结束(另一端关闭通道),则字节数为-1。
在下面的代码示例中,为读取结果的处理程序创建了一个实现类
ReadCompletionHandler
,该类的构造方法传入AsynchronousSocketChannel
实例,用来读取该通道中数据。该类在读取完成缓冲区中数据后,会继续调用
read
方法,来继续读取下一报文。static class ReadCompletionHandler implements CompletionHandler {
private final AsynchronousSocketChannel asynchronousSocketChannel;
ReadCompletionHandler(AsynchronousSocketChannel asynchronousSocketChannel) {
this.asynchronousSocketChannel = asynchronousSocketChannel;
}@Override
public void completed(Integer result, ByteBuffer attachment) {
// 客户端关闭通道,字节数为-1
if(result == -1) {
System.out.printf("[%s] - 客户端断开连接!\n", Thread.currentThread().getName());
try {
// 关闭当前 Socket 通道
asynchronousSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
// 将缓冲区进行反转(刚才是写入,反转后变为读取)
attachment.flip();
// 读取缓冲区中的内容,并转为字符串
String content = new String(attachment.array(), 0, result);
System.out.printf("[%s] - 接收客户端发来的内容:%s\n", Thread.currentThread().getName(), content);
// 清除缓冲区
attachment.clear();
// 继续读取下一报文
asynchronousSocketChannel.read(attachment, attachment, new ReadCompletionHandler(asynchronousSocketChannel));
}@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.printf("[%s] - 客户端断开连接!\n", Thread.currentThread().getName());
try {
// 关闭当前 Socket 通道
asynchronousSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端API 创建客户端套接字的异步通道 客户端可以通过
AsynchronousSocketChannel
的open
静态方法,来创建一个异步通道。// 打开一个异步的 Socket 通道
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
连接服务端 该异步通道通过
connect
方法来连接指定的服务端地址(IP地址+端口),该connect
方法也是一个异步方法,同样可以指定结果处理器,但这次我们使用Future
方式来实现。Future
是非阻塞的,可以通过isDone
方法来检测是否执行完毕。在这里,我们使用get
方法来阻塞获取结果,直到连接成功。// 连接指定的服务端,并同步获取连接结果。
Future connectFuture = socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
// get()操作会阻塞,直到连接成功。
connectFuture .get();
向服务端写入数据 【Netty|Java I/O 模型之 AIO】连接成功后,可以向该通道写入数据了。同样
write
方法也是一个异步的,这里我们也使用Future
方式,并通过get
方法阻塞获取结果。// 申请一个1024字节的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 将控制台输入的内容写入缓冲区
byteBuffer.put("你好".getBytes());
// 反转缓冲区(从写入变为读取)
byteBuffer.flip();
// 将缓冲区中的内容写入到 Socket 通道中。get()操作会阻塞,直到写入成功。
socketChannel.write(byteBuffer).get();
// 清除缓冲区
byteBuffer.clear();
推荐阅读
- Netty|Java I/O 模型之 BIO
- Dubbo|Dubbo Admin 安装与部署
- Netty|Java I/O 模型之 NIO
- Java|idea控制台显示services窗口
- 二叉树的基础练习(Java)
- java/Android 接口调用的几种写法
- 6 款 Java 8 自带工具,轻松分析定位 JVM 问题!
- 安全|华为员工利用系统Bug越权获取机密数据,还透露给了第三方!
- java|“为报复公司解雇,我更改了项目的所有代码注释!”