Hyperion: 一个支持自定义编解码器的TCP通信框架
特性
- 支持自定义编解码器
- 高效的ByteBuffer实现,降低请求处理过程中数据拷贝
- 自带连接池支持,支持连接重建、连接空闲超时
- 易于扩展,可以积木式添加IoFilter处理入栈、出栈消息
组件
- hyperion.buffer: 支持扩容的ByteBuffer实现
- hyperion.logadapter:支持打印异常堆栈的日志实现,可以适配第三方日志系统
- hyperion.objectpool:对象池实现
- hyperion.threadpool:线程池实现
- hyperion.transport:支持自定义编解码器的TCP通信框架
Redis仓颉语言客户端SDK: redis-sdk 项目使用了该TCP框架,并实现了RESP2、RESP3协议的编解码。
ActiveMQ仓颉语言客户端SDK:acitvemq4cj项目使用了该TCP框架。
1. 编译和测试
工程目录结构
|---samples 使用Hyperion TCP框架的示例目录
|---src Hyperion TCP框架的源码目录
|---module.json
|---README.md
1.1 编译步骤
清理工程,在工程根目录下运行:
$> cjpm clean
编译工程,在工程根目录下运行:
$> cjpm build
编译的静态库位于:
build/release/hyperion/hyperion.buffer.cjo
build/release/hyperion/hyperion.logadapter.cjo
build/release/hyperion/hyperion.objectpool.cjo
build/release/hyperion/hyperion.threadpool.cjo
build/release/hyperion/hyperion.transport.cjo
1.2 单元测试
在工程根目录下运行:
$> cjpm test
1.3 运行示例程序
编译服务端示例程序,在samples/echo_server目录下运行:
$> cjpm build
启动服务端,在samples/echo_server目录下运行:
$>./build/release/bin/main
编译客户端示例程序,在samples/echo_client目录下运行:
$> cjpm build
启动服务端,在samples/echo_client目录下运行:
$>./build/release/bin/main
2. 架构
2.1 Hyperion TCP框架的架构
Hyperion TCP框架的架构图如下:
MessageCompletedHandler接口
用于判断消息的报文是否读取完整,提供如下方法:
func messageCompleted(buffer: ByteBuffer, status: MessageCompletedStatus): Unit
Session接口
单向会话接口,可以向对端发送消息
IoSession接口
双向会话接口,可以从对端收取消息,也可以向对端发送消息
IoFilter接口
对TCP框架客户端或者服务端的入栈消息、出栈消息进行加工,提供如下方法:
func inboundMessage(context: IoFilterContext, session: Session, inMessages: ArrayList): Unit
func outboundMessage(context: IoFilterContext, session: Session, outMessages: ArrayList): Unit
SingularMessageIoFilter类
只处理单个入栈消息和单个出栈消息的IoFilter实现
IoFilterChain类
由IoFilter串联而成的链表
Connection接口
TCP框架客户端和服务端之间建立的连接
ConnectionInitializer接口
用于客户端和服务端初始化TCP连接,提供如下方法:
func initialize(session: Session): Unit
EventLoopHandler类
TCP框架客户端或者服务端的事件处理器,循环处理入栈消息,并按需要将消息出栈
TcpEndpoint类
TCP框架服务端实现
ClientTcpEndpoint类
TCP框架客户端实现,服务端支持非Hyperion TCP框架的服务端
2.1 Hyperion TCP框架编解码模块的架构图
Hyperion TCP框架的的编解码模块的架构图如下:
ProtocolEncoder接口
编码器接口
ProtocolDecoder接口
解码器接口
ProtocolCodecFilter类
一对编码器、解码器的组合,实现了IoFilter接口
StringToByteMessageEncoder
字符串编码器
ByteToStringMessageDecoder
字符串解码器
LengthBasedFrameEncoder
带长度的报文编码器
LengthBasedFrameDecoder
带长度的报文解码器
3. 在工程中使用Hyperion TCP框架
3.1 导入Hyperion TCP框架的静态库
在工程的module.json中引入Hyperion TCP框架的静态库:
"package_requires": {
"package_option": {
"hyperion_hyperion.buffer": "${path_to_hyperion_project}/build/release/hyperion/hyperion.buffer.cjo",
"hyperion_hyperion.logadapter": "${path_to_hyperion_project}/build/release/hyperion/hyperion.logadapter.cjo",
"hyperion_hyperion.objectpool": "${path_to_hyperion_project}/build/release/hyperion/hyperion.objectpool.cjo",
"hyperion_hyperion.threadpool": "${path_to_hyperion_project}/build/release/hyperion/hyperion.threadpool.cjo",
"hyperion_hyperion.transport": "${path_to_hyperion_project}/build/release/hyperion/hyperion.transport.cjo"
},
"path_option": []
},
3.2 TCP框架客户端和服务端支持的配置
1. 客户端和服务端都支持的配置
Socket配置
TcpSocketOptions类的属性 | 作用 |
---|---|
prop port: UInt16 | 设置TCP服务端的监听端口 |
prop receiveBufferSize: ?Int64 | 接收缓冲区大小 |
prop sendBufferSizeVal: ?Int64 | 发送缓冲区大小 |
prop noDelay: ?Bool | TCP_NODELAY选项 |
prop linger: ?Duration | SO_LINGER选项 |
prop readTimeout: ?Duration | Socket读超时时间 |
prop writeTimeout: ?Duration | Socket写超时时间 |
prop idleTimeout: Duration | 连接空闲超时时间 |
Hyperion TCP框架配置
TcpSocketOptions类的属性 | 作用 | 默认值 |
---|---|---|
prop asyncWrite: Bool | 是否开启每个连接一个写线程 | true |
prop sliceExceedBuffer: Bool | 是否通过切片方式减少数组拷贝 | true |
prop maxMessageSizeInBytes: Int64 | 支持处理的消息的最大长度 | 64M |
prop bufferAllocateSize: Int64 | 分配的ByteBuffer大小 | 8192 |
* prop usePooledBufferAllocator: Bool | 是否将ByteBuffer池化重用 | false |
* prop maxPooledBuffers: Int64 | 缓存的ByteBuffer的最大数量 | 2048 |
标*的为实验性质配置,目前不建议在生产环境中修改这些配置的默认值
2. 服务端特有的配置
EndpointConfig类的属性 | 作用 |
---|---|
prop address: String | 设置TCP服务端的监听地址 |
prop backlogSize: ?Int64 | backlog队列大小 |
prop reuseAddress: ?Bool | SO_REUSE_ADDR选项 |
prop sendBufferSizeVal: ?Int64 | 发送缓冲区大小 |
prop reusePort: ?Bool | SO_REUSE_PORT选项 |
prop acceptTimeout: ?Duration | Accept超时时间 |
3. 客户端特有的配置
ClientEndpointConfig类的属性 | 作用 |
---|---|
prop host: String | 设置TCP服务端的监听地址 |
prop minConnections: Int64 | 连接池的最小连接数 |
prop maxConnections: Int64 | 连接池的最大连接数 |
prop waitConnectionTimeout: Duration | 等待连接超时时间 |
3.3 实现一个回写消息的服务端
3.3.1 需求
需求: 回写客户端发送过来的字符串消息
传输报文:4字节的字符串二进制数组长度+字符串的二进制数组
长度 | 说明 |
---|---|
4字节 | 使用utf8编码的字符串的Byte数组的长度 |
n字节 | 使用utf8编码的字符串的Byte数组 |
3.3.2 服务定义
编写一个回写字符串消息的服务:
public class EchoService {
public func processMessage(message: String) {
return message
}
}
3.3.3 实现编解码和调用服务的IoFilter
编写一个IoFilter,将入栈消息交给EchoService处理:
public class EchoHanlder <: SingularMessageIoFilter {
private let service = EchoService()
/**
* 处理入栈消息
*/
public func processInboundMessage(context: IoFilterContext, session: Session, inMessage: Any): Unit {
if (let Some(text) <- inMessage as String) {
println("Received Message: ${text}")
// 调用业务处理逻辑
let result = service.processMessage(text)
context.offerMessage(result)
} else {
let exception = Exception("Only accept string message")
context.exceptionCaught(exception)
}
}
public func processInboundException(context: IoFilterContext, session: Session, ex: Exception) {
context.exceptionCaught(ex)
}
/**
* 处理出栈消息
*/
public func processOutboundMessage(context: IoFilterContext, session: Session, outMessage: Any): Unit {
// 转交给下一个IoFilter处理
context.offerMessage(outMessage)
}
public func processOutboundException(context: IoFilterContext, session: Session, ex: Exception): Unit {
context.exceptionCaught(ex)
}
public func toString() {
return "EchoHanlder"
}
}
编写一个将字符串转换为二进制数组的IoFilter,由于hyperion.transport.ByteAndStringCodec已经实现该功能,可以直接选用ByteAndStringCodec
编写一个给二进制数组添加报文长度的IoFilter,由于hyperion.transport.LengthBasedFrameCodec已经实现该功能,可以直接选用LengthBasedFrameCodec
3.3.4 编写TCP服务端
编写服务端,并添加编解码和调用服务使用的IoFilter:
let config = EndpointConfig()
config.address = "127.0.0.1"
config.port = 8090
// 服务端使用的线程池
let threadPool = ThreadPoolFactory.createThreadPool(3, 128, 4096, Duration.minute * 2)
// 创建服务端Endpoint
let tcpEndpoint = TcpEndpoint(config, threadPool)
// 使用4字节记录报文长度的编解码器
let lengthFrameEncoder = LengthBasedFrameEncoder(4)
let lengthFrameDecoder = LengthBasedFrameDecoder(4)
// 判断报文是否包含完整消息的MessageCompletedHandler
tcpEndpoint.setMessageCompletedHandler(lengthFrameDecoder)
// 解析报文长度的IoFilter
tcpEndpoint.addFilter(LengthBasedFrameCodec(lengthFrameEncoder, lengthFrameDecoder))
// 字符串和二进制数组转换的IoFilter
tcpEndpoint.addFilter(ByteAndStringCodec())
// 调用服务的IoFilter
tcpEndpoint.addFilter(EchoHanlder())
// 启动服务端Endpoint
tcpEndpoint.start()
3.4 实现一个收发字符串消息的客户端
3.4.1 需求
需求: 向服务端发送字符串,并收取字符串响应
传输报文:4字节的字符串二进制数组长度+字符串的二进制数组
长度 | 说明 |
---|---|
4字节 | 使用utf8编码的字符串的Byte数组的长度 |
n字节 | 使用utf8编码的字符串的Byte数组 |
3.4.2 实现接收响应消息的类
public class EchoResponse {
private var exception: ?Exception = None
private var message: ?String = None
public func setException(exception: Exception) {
this.exception = exception
}
public func getException(): ?Exception {
return this.exception
}
public func setMessage(message: String) {
this.message = message
}
public func getMessage(): String {
if (let Some(ex) <- exception) {
throw ex
}
if (let Some(message) <- message) {
return message
}
throw Exception("No response message")
}
}
3.4.3 实现编解码和调用服务的IoFilter
编写一个IoFilter,将收取到的消息放入队列中,以便后续获取使用:
public class ClientHandler <: SingularMessageIoFilter {
private let messages = BlockingQueue<EchoResponse>()
public func takeMessage(): String {
let echoResponse = messages.dequeue()
return echoResponse.getMessage()
}
public func takeMessage(timeout: Duration): ?String {
if (let Some(echoResponse) <- messages.dequeue(timeout)) {
return echoResponse.getMessage()
}
return None
}
/**
* 处理入栈消息
*/
public func processInboundMessage(context: IoFilterContext, session: Session, inMessage: Any): Unit {
let echoResponse = EchoResponse()
if (let Some(text) <- inMessage as String) {
echoResponse.setMessage(text)
} else {
let exception = Exception("Only accept string message")
echoResponse.setException(exception)
}
// 添加到messages队列中,不再交给下一个IoFilter处理
messages.enqueue(echoResponse)
}
public func processInboundException(context: IoFilterContext, session: Session, ex: Exception) {
let echoResponse = EchoResponse()
echoResponse.setException(ex)
// 添加到messages队列中,不再交给下一个IoFilter处理
messages.enqueue(echoResponse)
}
/**
* 处理出栈消息
*/
public func processOutboundMessage(context: IoFilterContext, session: Session, outMessage: Any): Unit {
if (let Some(text) <- outMessage as String) {
// 直接发给下个IoFilter处理
context.offerMessage(text)
} else {
let exception = Exception("Only accept string message")
context.exceptionCaught(exception)
}
}
public func processOutboundException(context: IoFilterContext, session: Session, ex: Exception): Unit {
context.exceptionCaught(ex)
}
public func toString() {
return "ClientHanlder"
}
}
编写一个将字符串转换为二进制数组的IoFilter,由于hyperion.transport.ByteAndStringCodec已经实现该功能,可以直接选用ByteAndStringCodec
编写一个给二进制数组添加报文长度的IoFilter,由于hyperion.transport.LengthBasedFrameCodec已经实现该功能,可以直接选用LengthBasedFrameCodec
3.4.4 编写TCP客户端
编写服务端,并添加编解码和调用服务使用的IoFilter:
let config = ClientEndpointConfig()
config.host = "127.0.0.1"
config.port = 8090
// 客户端使用的线程池
let threadPool = ThreadPoolFactory.createThreadPool(3, 128, 4096, Duration.minute * 2)
// 创建客户端Endpoint
let tcpEndpoint = ClientTcpEndpoint(config, threadPool)
// 使用4字节记录报文长度的编解码器
let lengthFrameEncoder = LengthBasedFrameEncoder(4)
let lengthFrameDecoder = LengthBasedFrameDecoder(4)
// 判断报文是否包含完整消息的MessageCompletedHandler
tcpEndpoint.setMessageCompletedHandler(lengthFrameDecoder)
// 解析报文长度的IoFilter
tcpEndpoint.addFilter(LengthBasedFrameCodec(lengthFrameEncoder, lengthFrameDecoder))
// 字符串和二进制数组转换的IoFilter
tcpEndpoint.addFilter(ByteAndStringCodec())
// 接收消息并缓存消息到队列中的IoFilter
let clientHandler = ClientHandler()
tcpEndpoint.addFilter(clientHandler)
// 启动客户端Endpoint
tcpEndpoint.start()
// 创建会话,并使用会话发送消息
try (session = tcpEndpoint.createSession()) {
// 发送消息,并收取对应的响应
for (i in 1..=100) {
let message = "Message${i}"
println("Send message: ${message}")
session.writeAndFlushMessage(message)
let receiveMsg = clientHandler.takeMessage()
println("Client receive message: ${receiveMsg}")
}
}
但总有很多小伙伴不知道学习哪些鸿蒙开发技术? 不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以要有一份实用的鸿蒙(HarmonyOS NEXT)学习路线与学习文档用来跟着学习是非常有必要的。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。
本路线共分为四个阶段:
第一阶段:鸿蒙初中级开发必备技能
第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH
第三阶段:应用开发中高级就业技术
第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/MNxiaona/733GH
《鸿蒙 (Harmony OS)开发学习手册》(共计892页)
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:gitee.com/MNxiaona/733GH
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH
美团APP实战开发教学:gitee.com/MNxiaona/733GH
OpenHarmony系统内核开发
写在最后
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:
gitee.com/MNxiaona/733GH