HarmonyOS鸿蒙Next网络编程系列53-仓颉版TCP连接超时分析示例
HarmonyOS鸿蒙Next网络编程系列53-仓颉版TCP连接超时分析示例
1. TCP连接超时简介
在本系列的第5篇文章《鸿蒙网络编程系列5-TCP连接超时分析》中,介绍了基于ArkTS语言使用TCPSocket对象连接TCP服务端超时的示例。不过,在基于仓颉语言的鸿蒙应用开发中,并没有提供类似的鸿蒙API,幸运的是,仓颉语言自己内置了更强大的TcpSocket对象,可以使用该对象连接TCP服务端。在本系列的第49篇文章《鸿蒙网络编程系列49-仓颉版TCP客户端》中,介绍了TcpSocket对象的connect函数的用法,本文就不再赘述了,不过,我们同样要回答如下的三个问题:
- 连接的默认超时时间是多少?
- 如果超时时间设置为0会怎么样?
- 如果超时时间设置的非常大,比如5分钟,套接字会一直尝试连接吗?
这些问题不太容易回答,官方目前也没有相应的文档,接下来我们将通过一个示例来寻找答案。
2. TCP连接超时示例演示
本示例运行后的页面如图所示:
输入服务端地址和端口后(需确保服务器是真实的,但是端口是不存在的),单击下面的“连接测试”按钮,会发起连接,如图所示:
等选择的测试完成后,再一次单击下面的按钮,最终测试的输入如下所示:
下面是滚动条隐藏的内容:
3. TCP连接超时示例编写
下面详细介绍创建该示例的步骤(确保DevEco Studio已安装仓颉插件)。
步骤1:创建[Cangjie]Empty Ability项目。
步骤2:在module.json5配置文件加上对权限的声明:
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
这里添加了访问互联网的权限。
步骤3:在build-profile.json5配置文件加上仓颉编译架构:
"cangjieOptions": {
"path": "./src/main/cangjie/cjpm.toml",
"abiFilters": ["arm64-v8a", "x86_64"]
}
步骤4:在index.cj文件里添加如下的代码:
package ohos_app_cangjie_entry
import ohos.base.*;
import ohos.component.*;
import ohos.state_manage.*;
import ohos.state_macro_manage.*;
import std.collection.HashMap
import std.convert.*;
import std.net.*;
import std.socket.*;
import std.time.Duration
import std.time.DateTime
@Entry
@Component
class EntryView {
@State
var title: String = '仓颉版TCP连接超时示例';
//连接、通讯历史记录
@State
var msgHistory: String = '';
//服务端端口号
@State
var serverPort: UInt16 = 9998;
//服务端地址
@State
var serverAddress: String = "*.*.*.*";
//测试连接按钮是否可用
@State
var testEnable: Bool = true
let scroller: Scroller = Scroller()
func build() {
Row {
Column {
Text(title)
.fontSize(14)
.fontWeight(FontWeight.Bold)
.width(100.percent)
.textAlign(TextAlign.Center)
.padding(10)
Flex(FlexParams(justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center)) {
Text("服务端地址:").fontSize(14)
TextInput(text: serverAddress)
.onChange({
value => serverAddress = value
})
.width(100)
.fontSize(11)
.flexGrow(1)
Text(":").fontSize(14)
TextInput(text: serverPort.toString())
.onChange({
value => serverPort = UInt16.parse(value)
})
.setType(InputType.Number)
.width(80)
.fontSize(11)
}.width(100.percent).padding(10)
TimeoutTestItem(isDefaultTimeout: true, timeout: 0, connectTest: this.connectTest,
buttonEnabled: testEnable)
TimeoutTestItem(isDefaultTimeout: false, timeout: 0, connectTest: this.connectTest,
buttonEnabled: testEnable)
TimeoutTestItem(isDefaultTimeout: false, timeout: 3000, connectTest: this.connectTest,
buttonEnabled: testEnable)
TimeoutTestItem(isDefaultTimeout: false, timeout: 30000, connectTest: this.connectTest,
buttonEnabled: testEnable)
TimeoutTestItem(isDefaultTimeout: false, timeout: 300000, connectTest: this.connectTest,
buttonEnabled: testEnable)
Scroll(scroller) {
Text(msgHistory).textAlign(TextAlign.Start).width(100.percent).backgroundColor(0xeeeeee)
}
.align(Alignment.Top)
.backgroundColor(0xeeeeee)
.height(300)
.flexGrow(1)
.scrollable(ScrollDirection.Vertical)
.scrollBar(BarState.On)
.scrollBarWidth(20)
}.width(100.percent).height(100.percent)
}.height(100.percent)
}
//连接测试函数
func connectTest(isDefaultTimeout: Bool, timeout: Int64) {
let tcpClient = TcpSocket(serverAddress, serverPort)
if (isDefaultTimeout) {
this.msgHistory += "连接超时时间设置:默认\r\n"
} else {
this.msgHistory += "连接超时时间设置:${timeout}毫秒\r\n"
}
let start = DateTime.now()
this.msgHistory += "连接开始时间:${start.toString('yyyyMMdd HH:mm:ss')}\r\n"
this.msgHistory += "正在连接...\r\n"
//测试期间禁止点击按钮
testEnable = false
//启动一个线程进行测试
spawn {
try {
//如果使用默认超时配置,就不配置超时时间
if (isDefaultTimeout) {
tcpClient.connect()
} else { //配置超时时间
tcpClient.connect(timeout: Duration.millisecond * timeout)
}
} catch (err: Exception) {
//抛出异常的时间
let end = DateTime.now()
let period = end - start
this.msgHistory += "连接异常:${err.toString()}\r\n"
this.msgHistory += "连接结束时间:${end.toString('yyyyMMdd HH:mm:ss')}\r\n"
this.msgHistory += "连接耗时:${period.toMilliseconds()}毫秒\r\n\r\n"
}
//测试完成允许点击按钮
testEnable = true
}
}
}
步骤5:本示例使用了自定义组件TimeoutTestItem,需要创建TimeoutTestItem.cj文件,然后输入如下的代码:
package ohos_app_cangjie_entry
import ohos.base.*;
import ohos.component.*;
import ohos.state_manage.*;
import ohos.state_macro_manage.*;
@Component
public class TimeoutTestItem {
var isDefaultTimeout: Bool = true
var timeout: Int64 = 0
//连接测试按钮是否可用
@Link
var buttonEnabled: Bool
//测试按钮点击调用的函数
@Prop
var connectTest: (isDefaultTimeout: Bool, timeout: Int64) -> Future<Unit>
func build() {
Row {
Flex(FlexParams(justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center)) {
Text(if (this.isDefaultTimeout) {
"默认超时时间"
} else {
"超时时间为${this.timeout / 1000}秒"
}).width(150)
Text(if (this.isDefaultTimeout) {
"default"
} else {
this.timeout.toString()
}).width(150).flexGrow(1)
Button("连接测试")
.onClick {
evt => connectTest(this.isDefaultTimeout, this.timeout)
}
.enabled(buttonEnabled)
}.width(100.percent).padding(5)
}
}
}
步骤6:编译运行,可以使用模拟器或者真机。
步骤7:按照本文第2部分“TCP连接超时示例演示”操作即可。
4. 代码分析
本示例为简化代码,使用自定义组件TimeoutTestItem处理连接测试按钮的点击和状态显示问题,这里面有点复杂的是测试按钮的回调函数,该函数在index.cj中定义,名称为connectTest,接收两个参数,分别表示是否使用默认超时配置以及超时时间。在TimeoutTestItem中,通过变量connectTest指向该函数,并在按钮点击后触发回调。
另外一点需要注意的是,在模拟器和真机环境里,执行的效果是不同的,模拟器可以用来测试,但是要以真机环境运行的为准。在测试的时候,需要确保目标服务器是真实存在的,网络也是通畅的,同时要确保端口不存在,这样就可以实现连接超时的测试了。
根据输出的日志可以分析如下:
- 默认的超时时间为65秒钟
- 超时时间设置为0时,如果连接不上就马上返回,也就是说超时时间就是0
- 设置3秒钟或者30秒钟,实际超时时间就是3秒钟或者30秒钟
- 设置大于65秒的超时时间,实际超时时间为65秒左右
那么,为什么最长超时时间是65秒钟呢?这里分析一下,实际上,TCP连接重试时执行等待时间翻倍的规则,也就是连接失败后等待1秒钟重试,再失败等待2秒钟,然后依次是4秒钟、8秒钟、16秒钟、32秒钟,默认重试5次,也就是1+2+4+8+16+32=63秒钟,再加上其他环节耗费的时间,所以表现出来最大超时时间是65秒左右。
(本文作者原创,除非明确授权禁止转载)
本文源码地址: https://gitee.com/zl3624/harmonyos_network_samples/tree/master/code/tcp/TCPTimeout4Cj
本系列源码地址: https://gitee.com/zl3624/harmonyos_network_samples
更多关于HarmonyOS鸿蒙Next网络编程系列53-仓颉版TCP连接超时分析示例的实战教程也可以访问 https://www.itying.com/category-93-b0.html
鸿蒙Next仓颉版TCP连接超时可能涉及以下技术点:
- 网络权限未配置,需检查config.json中的ohos.permission.INTERNET权限;
- 使用@ohos.net.socket接口时connectTimeout参数设置不当;
- 网络策略限制,需确认设备网络状态正常;
- TLS握手超时,需检查安全协议版本兼容性;
- 本地DNS解析失败,建议改用IP直连测试;
- 系统底层socket非阻塞模式设置异常。
典型错误码包括ENETUNREACH(网络不可达)、ETIMEDOUT(连接超时)。超时阈值默认20秒,可通过socket.setOptions()调整。
更多关于HarmonyOS鸿蒙Next网络编程系列53-仓颉版TCP连接超时分析示例的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
这是一个关于HarmonyOS仓颉版TCP连接超时分析的详细示例,我来总结几个关键点:
- 超时机制分析:
- 默认超时时间约为65秒
- 设置为0时会立即返回连接失败
- 设置3秒或30秒会按设定值超时
- 超过65秒的设置仍会在大约65秒后超时
-
65秒超时原因: TCP连接采用指数退避重试机制: 1 + 2 + 4 + 8 + 16 + 32 = 63秒 加上其他处理时间,总计约65秒
-
代码实现要点:
- 使用TcpSocket.connect()方法
- 通过Duration.millisecond设置超时
- 采用异步spawn处理连接避免阻塞UI
- 自定义TimeoutTestItem组件封装测试逻辑
- 测试注意事项:
- 确保目标服务器可达但端口不可用
- 真机测试结果更准确
- 连接测试期间需禁用按钮防止重复操作
这个示例很好地验证了仓颉语言中TCP连接的超时行为,对网络编程有重要参考价值。