Netty 框架学习 —— 传输
概述
流经网络的数据总是具有相同的类型:字节,这些字节如何传输主要取决于我们所说的网络传输。用户并不关心传输的细节,只在乎字节是否被可靠地发送和接收
如果使用 Java 网络编程,你会发现,某些时候当你需要支持高并发连接,随后你尝试将阻塞传输切换为非阻塞传输,那么你会因为这两种 API 的截然不同而遇到问题。Netty 提供了一个通用的 API,这使得转换更加简单。
传统的传输方式
这里介绍仅使用 JDK API 来实现应用程序的阻塞(OIO)和非阻塞版本(NIO)
阻塞网络编程如下:
public class PlainOioServer {
public void server(int port) throws IOException {
// 将服务器绑定到指定端口
final ServerSocket socket = new ServerSocket(port);
try {
while (true) {
// 接收连接
final Socket clientSocket = socket.accept();
System.out.println("Accepted connection from " + clientSocket);
// 创建一个新的线程来处理连接
new Thread(() -> {
OutputStream out;
try {
out = clientSocket.getOutputStream();
// 将消息写给已连接的客户端
out.write("Hi\r\n".getBytes(StandardCharsets.UTF_8));
out.flush();
// 关闭连接x
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
这段代码可以处理中等数量的并发客户端,但随着并发连接的增多,你决定改用异步网络编程,但异步的 API 是完全不同的
非阻塞版本如下:
public class PlainNioServer {
public void server(int port) throws IOException {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
ServerSocket ssocket = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
// 将服务器绑定到选定的端口
ssocket.bind(address);
// 打开 Selector 来处理 Channel
Selector selector = Selector.open();
// 将 ServerSocket 注册到 Selector 以接受连接
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
final ByteBuffer msg = ByteBuffer.wrap("Hi\r\n".getBytes());
while (true) {
try {
// 等待需要处理的新事件,阻塞将一直持续到下一个传入事件
selector.select();
} catch (IOException e) {
e.printStackTrace();
break;
}
Set<SelectionKey> readKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
// 检查事件是否是一个新的已经就绪可以被接受的连接
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
// 接受客户端,并将它注册到选择器
client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, msg.duplicate());
System.out.println("Accepted connection from " + client);
}
// 检查套接字是否已经准备好写数据
if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
while (buffer.hasRemaining()) {
// 将数据写到已连接的客户端
if (client.write(buffer) == 0) {
break;
}
}
client.close();
}
} catch (IOException exception) {
key.cancel();
try {
key.channel().close();
} catch (IOException cex) {
cex.printStackTrace();
}
}
}
}
}
}
可以看到,阻塞和非阻塞的代码是截然不同的。如果为了实现非阻塞而完全重写程序,无疑十分困难
基于 Netty 的传输
使用 Netty 的阻塞网络处理如下:
public class NettyOioServer {
public void server(int port) throws Exception {
final ByteBuf buf = Unpooled.unreleasableBuffer(
Unpooled.copiedBuffer("Hi\n\r", StandardCharsets.UTF_8));
EventLoopGroup group = new OioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(group)
// 使用阻塞模式
.channel(OioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new SimpleChannelInboundHandler<>() {
@Override
protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.writeAndFlush(buf.duplicate())
.addListener(ChannelFutureListener.CLOSE);
}
});
}
});
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
}
而非阻塞版本和阻塞版本几乎一模一样,只需要改动两处地方
EventLoopGroup group = new NioEventLoopGroup();
b.group(group).channel(NioServerSocketChannel.class);
传输 API
传输 API 的核心是 interface Channel,它被用于所有的 IO 操作。每个 Channel 都将被分配一个 ChannelPipeline 和 ChannelConfig,ChannelConfig 包含了该 Channel 的所有配置设置,ChannelPipeline 持有所有将应用于入站和出站数据以及事件的 ChannelHandler 实例
除了访问所分配的 ChannelPipeline 和 ChannelConfig 之外,也可以利用 Channel 的其他方法
方法名 | 描述 |
---|---|
eventLoop | 返回分配给 Channel 的 EventLoop |
pipeline | 返回分配给 Channel 的 ChannelPipeline |
isActive | 如果 Channel 活动的,返回 true |
localAddress | 返回本地的 SocketAddress |
remoteAddress | 返回远程的 SocketAddress |
write | 将数据写到远程节点 |
flush | 将之前已写的数据冲刷到底层传输 |
writeAndFlush | 等同于调用 write() 并接着调用 flush() |
内置的传输
Netty 内置了一些可开箱即用的传输,但它们所支持的协议不尽相同,因此你必须选择一个和你的应用程序所使用协议相容的传输
名称 | 包 | 描述 |
---|---|---|
NIO | io.netty.channel.socket.nio | 使用 java.nio.channels 包作为基础 |
Epoll | io.netty.channel.epoll | 由 JNI 驱动的 epoll() 和非阻塞 IO,可支持只有在 Linux 上可用的多种特性,比 NIO 传输更快,且完全非阻塞 |
OIO | io.netty.channel.socket.oio | 使用 java.net 包作为基础 |
Local | io.netty.channel.local | 可以在 VM 内部通过管道进行通信的本地传输 |
Embedded | io.netty.channel.embedded | Embedded 传输,允许使用 ChannelHandler 而不需要一个真正的基于网络的传输,主要用于测试 |
原文地址:https://www.cnblogs.com/Yee-Q/p/14878616.html
- Go语言单链表实现方法
- Go语言实现AzDG可逆加密算法实例
- 剖析Go编写的Socket服务器模块解耦及基础模块的设计
- Golang中的sync.WaitGroup用法实例
- Go 语言实现 MapReduce 框架
- Performance Schema使用简介(一)
- golang 垃圾回收 gc
- Go语言服务器开发之简易TCP客户端与服务端实现方法
- 移动搜索SEO分享:PHP自动生成百度开放适配及360移动适配专用的Sitemap文件
- 分享两种外链跳转方法,可避免权重流失。
- go语言十大排序算法总结
- 网站安全检测提示“页面异常导致本地路径泄漏”的解决办法
- Go语言归并排序算法实现
- 超強统计插件:My Visitors在知更鸟主题下的修改教程
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python实现十大经典排序算法
- 140行Python代码实现Flippy Bird
- 200行Python代码实现贪吃蛇
- 简单说维特比算法 - python实现
- Android绘图及Bitmap几个知识点整理
- Spark Pipeline官方文档
- Python-时间及日期-07-获取本周或上周几具体日期
- Spark Job-Stage-Task实例理解
- 想查看微信好友撤回的消息?Python帮你搞定
- MySQL 案例:同步中断与SQL线程类型转换
- Android端Charles抓包
- Spring boot 启动提示数据源错误
- leetcode树之从上到下打印二叉树
- 整理C/C++的可变参数
- ThingJS结合Web地图API开发,让数据展示更加出色!