鸿蒙新特性实战:@ohos.net.socket — TCP/UDP 套接字编程与网络调试器

📅 2026/7/12 18:16:15
鸿蒙新特性实战:@ohos.net.socket — TCP/UDP 套接字编程与网络调试器
引言在移动应用开发中网络通信通常由 HTTP 客户端如ohos.net.http封装完成。但有些场景需要绕过 HTTP 层直接操控传输层协议——比如与 IoT 设备通信的自定义二进制协议、实现简单的端口扫描器、或开发局域网文件传输工具。这些场景下TCP 和 UDP 套接字是无可替代的底层基础设施。HarmonyOS NEXT 通过ohos.net.socket模块提供了完整的 Berkeley 套接字抽象。开发者可以像在 Node.js 或 POSIX 系统中一样创建 TCP 客户端连接、绑定 UDP 端口、收发二进制数据并通过事件监听器异步处理网络 I/O。本文将构建一个Socket 调试器Demo展示 TCP 客户端生命周期创建 → 连接 → 发送 → 接收 → 关闭和 UDP 套接字的绑定/发送流程配以实时操作日志监控每一步的状态变化。读完本文你将掌握TCP 客户端constructTCPSocketInstance()→connect()→send()→on(message)→close()UDP 套接字constructUDPSocketInstance()→bind()→send()事件驱动模型on(message)/off(message)注册/注销数据接收回调二进制数据处理ArrayBuffer↔Uint8Array↔ 字符串互转权限与安全ohos.permission.INTERNET的作用范围和声明方式环境与权限ohos.net.socket属于NetworkKit工具包自 API 7 开始提供。使用前需要导入import{socket}fromkit.NetworkKit;重要所有 Socket 操作都需要ohos.permission.INTERNET权限。这是一个normal 级别的权限在 HarmonyOS 中声明后即可自动获得无需用户手动授权// entry/src/main/module.json5 { module: { requestPermissions: [ { name: ohos.permission.INTERNET } ] } }如果未声明此权限所有 bind/connect/send/close 操作都会抛出201 - Permission denied错误。一、核心数据类型1.1 NetAddress — 网络地址interfaceNetAddress{address:string;// IP 地址或域名如 192.168.1.1 或 www.example.comport?:number;// 端口号如 80family?:number;// 地址族1IPv4, 2IPv6默认 1}NetAddress是贯穿整个 socket 模块的基础类型用于 bind、connect、send 等操作中指定目标地址。address可以是 IP 地址或域名——Socket 模块内部会自动解析域名。1.2 SocketStateBase — 连接状态interfaceSocketStateBase{isBound:boolean;// 是否已绑定本地地址isClose:boolean;// 是否已关闭isConnected:boolean;// 是否已连接仅 TCP}通过TCPSocket.getState()可以查询当前状态。在 Demo 中我们通过追踪connect()和close()的调用结果来维护连接状态避免不必要的getState()异步调用。1.3 SocketRemoteInfo — 远程信息数据接收事件on(message)的回调中会附带remoteInfo包含发送方的地址和端口信息。对于 TCP 连接来说这通常是已连接的对端对于 UDP 来说这标识了数据包的来源。二、TCP 套接字完整生命周期TCP 是面向连接的可靠传输协议。一个 TCP 客户端的典型生命周期包括五个阶段2.1 创建实例import{socket}fromkit.NetworkKit;lettcp:socket.TCPSocketsocket.constructTCPSocketInstance();constructTCPSocketInstance()是一个同步工厂方法直接返回 TCPSocket 实例。这个实例在未连接时仅占用极少资源可以安全地持有多实例。2.2 连接服务器connectletaddr:socket.NetAddress{address:www.example.com,port:80,family:1// IPv4};awaittcp.connect({address:addr,timeout:5000// 连接超时毫秒});connect()接受一个TCPConnectOptions参数包含目标地址和超时时间单位毫秒可选。方法返回Promisevoid——成功表示三次握手完成失败会抛出 BusinessError。connect 内部发生了什么DNS 解析如果address是域名系统会自动执行 DNS 查询TCP 三次握手SYN → SYN-ACK → ACK任一阶段失败都会抛出异常2.3 注册数据监听on messageTCP 连接建立后需要注册message事件监听器来接收服务器发来的数据tcp.on(message,(data:Object){letbuf:ArrayBuffer(dataasRecordstring,Object)[message]asArrayBuffer;letviewnewUint8Array(buf);// 处理接收到的二进制数据...});on(message, callback)注册一个持久的事件监听器。每当服务器发送数据callback 就会被触发。注意data.message是ArrayBuffer类型需要用Uint8Array视图来逐字节读取。2.4 发送数据sendawaittcp.send({data:GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n,encoding:UTF-8});send()接受TCPSendOptions参数data要发送的字符串数据encoding字符编码默认 UTF-8注意send()发送的是字符串内部会自动编码为字节数组。如果需要发送原始二进制数据可以先转为 Base64 字符串再发送。2.5 关闭连接closetcp.off(message);// 先注销事件监听awaittcp.close();// 再关闭连接tcpnull;// 释放引用关闭连接时必须先注销事件监听否则可能导致内存泄漏或回调在已销毁的对象上执行。close()会发送 FIN 包完成四次挥手。2.6 完整示例发送 HTTP 请求asyncfunctiontcpHttpDemo():Promisevoid{lettcpsocket.constructTCPSocketInstance();// 注册数据监听tcp.on(message,(data:Object){letbuf(dataasRecordstring,Object)[message]asArrayBuffer;letresponse;letviewnewUint8Array(buf);for(leti0;iview.lengthi1000;i){responseString.fromCharCode(view[i]);}console.log(响应: response);});try{// 连接awaittcp.connect({address:{address:www.example.com,port:80,family:1},timeout:5000});console.log(连接成功);// 发送 HTTP GET 请求awaittcp.send({data:GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: close\r\n\r\n});console.log(请求已发送);}catch(e){console.error(操作失败: JSON.stringify(e));}finally{// 延迟关闭等待响应setTimeout(async(){tcp.off(message);awaittcp.close();console.log(连接已关闭);},3000);}}注意这是一个手动构造 HTTP 请求的示例演示了 Socket 的原始能力。在生产环境中发送 HTTP 请求应使用ohos.net.http模块它自动处理头部解析、chunked 编码、重定向等复杂逻辑。三、UDP 套接字UDP 是无连接的、非可靠的数据报协议。与 TCP 不同UDP 不需要 connect只需要 bind send3.1 绑定本地端口bindletudp:socket.UDPSocketsocket.constructUDPSocketInstance();letlocalAddr:socket.NetAddress{address:0.0.0.0,// 监听所有网卡port:0,// 0 系统自动分配端口family:1};awaitudp.bind(localAddr);bind()将 UDP 套接字绑定到本地地址和端口。端口设为0表示让系统自动分配一个空闲端口——这是客户端的典型做法。服务端则需要指定一个固定端口。3.2 发送数据报lettarget:socket.NetAddress{address:192.168.1.100,port:8888,family:1};awaitudp.send({data:Hello UDP!,address:target});与 TCP 的send()不同UDP 的send()需要附带目标地址——因为 UDP 是无连接的每个数据报都可以发往不同的目标。3.3 TCP vs UDP 对比维度TCPUDP连接方式connect() 建立连接bind() 绑定端口即可可靠性可靠有序无丢失不可靠可能丢包/乱序发送方法send({data}) 无需指定地址send({data, address}) 每次携带目标地址接收模型on(‘message’) 持续接收on(‘message’) 逐包接收状态检查getState() → SocketStateBasegetState() → SocketStateBase使用场景HTTP、文件传输、聊天DNS、视频流、IoT传感器四、实战 DemoSocket 调试器页面结构Socket 调试器 ├── 状态栏 — 实时操作反馈 ├── 目标配置 │ ├── Host 输入框 │ ├── Port 输入框 │ └── TCP / UDP 状态指示器 ├── TCP 客户端面板 │ ├── 连接 / 断开 按钮 │ ├── 发送数据输入框含 {host} 占位符 │ └── TCP 发送 按钮 ├── UDP 套接字面板 │ ├── 绑定 / 关闭 按钮 │ └── UDP 发送 按钮 ├── 操作日志 — 最近30条事件含时间戳 └── 核心 API 参考 — 10 个关键 API4 个交互点TCP 连接/断开— 输入目标 Host Port点击连接建立 TCP 连接点击断开完成四次挥手TCP 发送与接收— 编辑发送数据默认 HTTP GET 请求点击发送后自动监听响应UDP 绑定/发送— 绑定本地端口后向目标地址发送 UDP 数据报实时日志— 所有操作连接/发送/接收/异常/关闭均以带时间戳的彩色日志呈现核心代码位置完整代码在dev/entry/src/main/ets/pages/SocketLabPage.ets约 310 行路由已注册为pages/SocketLabPage。五、二进制数据处理模式Socket 通信的核心是ArrayBuffer↔Uint8Array↔ 字符串的三向转换。接收ArrayBuffer → 字符串letviewnewUint8Array(arrayBuffer);letstr;for(leti0;iview.length;i){strString.fromCharCode(view[i]);}对于纯 ASCII/UTF-8 文本这种逐字节转换方式简单且高效。对于混合编码或多字节字符如中文应使用util.TextDecoder。发送字符串 → 发送Socket 的send()方法直接接受字符串内部自动完成编码awaittcp.send({data:GET / HTTP/1.1\r\nHost: example.com\r\n\r\n});如果需要发送原始二进制数据如 Protobuf可以先转为 Base64 字符串。六、API 总结表API返回值说明socket.constructTCPSocketInstance()TCPSocket创建 TCP 客户端实例socket.constructUDPSocketInstance()UDPSocket创建 UDP 实例tcp.bind(address)Promisevoid绑定本地地址需 INTERNETtcp.connect(options)Promisevoid连接远程服务器需 INTERNETtcp.send(options)Promisevoid发送数据需 INTERNETtcp.on(message, cb)void注册数据接收监听tcp.off(message, cb)void注销数据接收监听tcp.close()Promisevoid关闭连接需 INTERNETtcp.getRemoteAddress()PromiseNetAddress获取远程地址udp.send(options)PromisevoidUDP 发送数据报需 INTERNET七、与 net.http 的对比第 25 篇文章介绍了ohos.net.http用于 HTTP 请求。它与ohos.net.socket的关系如下维度net.httpnet.socket抽象层级应用层HTTP传输层TCP/UDP连接管理自动连接池手动connect/close数据格式自动解析 JSON/响应头原始 ArrayBuffer协议限制仅 HTTP/HTTPSTCP/UDP 任意协议使用复杂度低高适用场景REST API、文件下载IoT、自定义协议、P2P两者互补net.http覆盖 90% 的日常网络需求net.socket处理剩下 10% 需要底层控制的场景。八、总结ohos.net.socket为 HarmonyOS NEXT 应用提供了底层的 TCP/UDP 套接字编程能力TCP 客户端完整生命周期创建 → connect → send → on(‘message’) → closeUDP 数据报模式bind → send → on(‘message’) → close事件驱动 I/Oon(message)/off(message)的注册/注销模式二进制数据ArrayBuffer / Uint8Array 的灵活转换权限要求所有操作需声明ohos.permission.INTERNET这个模块是构建 IoT 通信、局域网文件传输、自定义协议客户端等高级网络应用的基础。结合ohos.net.connection的网络状态感知和ohos.net.http的 HTTP 封装开发者已经掌握了 HarmonyOS 网络编程的完整工具箱。