1、**配置网络权限:**根据网页2和6,需要在module.json5中添加INTERNET权限。这是必要的前提条件。
2、**导入模块:**使用来自@kit.NetworkKit的webSocket模块,所以正确的导入应该是import webSocket from ‘@ohos.net.webSocket’或者类似,需要确认具体路径。网页6的代码示例显示导入的是’@ohos.net.webSocket’,或者@kit.NetworkKit,可能版本不同,需要统一说明。
3、**创建WebSocket实例:**使用createWebSocket方法,如let ws = webSocket.createWebSocket();
4、**连接服务器:**调用connect方法,传入服务器地址,可能有回调处理错误。
5、**事件处理:**订阅open、message、close、error事件,处理各自的回调函数。例如,在open事件中发送初始消息,在message中处理接收的数据,close时进行清理,error时处理异常。
6、**发送消息:**通过send方法发送文本或二进制数据,注意异步回调处理可能的错误。
7、**关闭连接:**调用close方法,确保资源释放。
在ArkTS中创建WebSocket连接主要涉及以下步骤和关键接口,结合HarmonyOS官方文档及开发者实践经验,具体流程如下:
ArkTs中的wbSocket
一、配置网络权限
在module.json5文件中添加网络权限声明:
"requestPermissions": [
{"name": "ohos.permission.INTERNET"}
二、导入WebSocket模块
使用ArkTS提供的原生API,需从@ohos.net.webSocket模块导入相关功能:
import webSocket from '@ohos.net.webSocket';
三、创建WebSocket实例
通过createWebSocket()方法初始化连接对象:
let ws = webSocket.createWebSocket();
四、建立连接
调用connect()方法并指定服务器地址(支持ws://或wss://协议):
const serverUrl = 'ws://your-server-address:port';
ws.connect(serverUrl, (err: BusinessError, value: boolean) => {
if (!err) {
console.log('连接成功');
else {
console.error('连接失败:', JSON.stringify(err));
});
五、事件监听与处理
需订阅以下核心事件实现双向通信:
open事件
连接成功时触发,通常用于发送初始消息:
ws.on('open', (err: BusinessError, value: Object) => {
if (!err) {
ws.send('Hello Server', (sendErr) => { / 处理发送结果 / });
});
message事件
接收服务器消息,支持文本和二进制数据:
ws.on('message', (err: BusinessError, value: string | ArrayBuffer) => {
if (!err) {
console.log('收到消息:', value);
});
close事件
连接关闭时触发,可获取关闭状态码和原因:
ws.on(‘close’, (err: BusinessError, value: webSocket.CloseResult) => {
console.log(连接关闭,代码: {value.code}, 原因: {value.reason}
);
});
error事件
处理连接或通信中的异常:
ws.on('error', (err: BusinessError) => {
console.error('发生错误:', JSON.stringify(err));
});
六、发送消息
通过send()方法发送数据(支持文本或ArrayBuffer):
ws.send('Hello World', (err: BusinessError) => {
if (!err) {
console.log('消息发送成功');
}
});
七、关闭连接
调用close()主动断开连接:
ws.close((err: BusinessError) => {
if (!err) {
console.log('连接已关闭');
}
});
注意事项
协议安全性:敏感数据传输应使用wss://协议加密。
错误恢复:需处理网络中断等异常,可加入自动重连机制。
性能优化:高频通信场景建议采用消息队列或异步处理防止阻塞。
资源释放:页面销毁时需取消事件监听并关闭连接。
完整示例代码
import webSocket from '@ohos.net.webSocket';
let ws = webSocket.createWebSocket();
ws.on('open', (err, value) => {
if (!err) {
ws.send('Hello Server', (sendErr) => { / 处理发送 / });
});
ws.on('message', (err, value) => {
if (value === 'bye') ws.close(); // 服务器发送bye时关闭连接
});
ws.connect('ws://your-server:8080', (err) => { / 处理连接结果 / });
以上步骤结合了HarmonyOS官方接口规范及开发者实践经验,适用于HarmonyOS NEXT环境下的实时通信场景(如聊天、实时数据监控等)。具体实现需根据服务器协议调整参数。
每一个ws.on是否都是一个独立线程,用于监听服务器信息
在 ArkTS(HarmonyOS NEXT)的 WebSocket 实现中,**所有 ws.on 事件监听器均运行在主线程(ArkTS 引擎主线程)中,不会创建独立线程。**以下是具体分析:
1、ArkTS 的单线程模型
**主线程特性:**ArkTS 的 JavaScript 运行环境基于单线程事件循环机制,所有同步代码、异步回调(如 Promise、setTimeout)及 WebSocket 事件监听均在主线程中执行。
**事件队列机制:**通过事件队列(宏任务队列和微任务队列)处理异步操作,如 onopen、onmessage 等回调函数会被推入队列,由主线程按顺序执行。
2、WebSocket 事件监听机制
**非阻塞监听:**ws.on 方法仅注册回调函数,不会阻塞主线程。当服务器消息到达时,底层网络库(如 libuv)会将消息封装为事件,插入主线程的事件队列,最终由主线程触发对应的回调。
事件类型示例:
// 以下所有回调均在同一线程(主线程)执行
ws.on('open', () => { / 连接成功回调 / });
ws.on('message', (data) => { / 接收消息回调 / });
ws.on('close', () => { / 连接关闭回调 / });
3、多线程与并发的实现方式
虽然事件监听本身不创建线程,但 ArkTS 提供了两种多线程方案处理耗时操作,避免阻塞主线程:
(1)Worker 线程
独立于主线程运行,需手动创建和管理。
通过消息传递与主线程通信(序列化数据,最大 16MB)。
适用场景:长时间 CPU 密集型任务(如复杂计算)。
(2)TaskPool 任务池
系统管理的线程池,支持任务优先级和负载均衡。
适用场景:短期耗时操作(如批量数据处理)。
4、性能与注意事项
**避免主线程阻塞:**若在 onmessage 回调中执行耗时操作(如循环计算),会导致 UI 卡顿甚至应用崩溃(超过 6 秒触发 APP_INPUT_BLOCK 错误)。
**正确做法:**将耗时逻辑移至 Worker 或 TaskPool:
// 示例:在 Worker 中处理消息
const worker = new worker.ThreadWorker('workers/MessageHandler.ets');
ws.on('message', (data) => {
worker.postMessage(data); // 将消息转发给 Worker
});
总结
**ws.on 监听器:**均在主线程执行,依赖事件队列实现异步非阻塞。
**多线程需求:**需通过 Worker 或 TaskPool 显式创建,与 WebSocket 事件监听无关。
**设计建议:**根据业务场景选择并发模型,避免主线程阻塞导致体验问题。
linux中C语言版本的socket连接步骤
以下是 Linux 环境下 C 语言版本的 Socket 连接步骤详解,结合多篇技术文档和实践案例整理:
一、服务端实现步骤
1、创建 Socket
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
参数说明:
AF_INET 表示 IPv4 协议;
SOCK_STREAM 表示 TCP 协议(若使用 UDP 则为 SOCK_DGRAM)。
2、配置地址结构体
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有网卡
server_addr.sin_port = htons(8080); // 设置端口号
关键函数:
htons() 将主机字节序转换为网络字节序(16 位);
htonl() 用于 32 位数据转换。
3、绑定地址
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("bind failed");
exit(EXIT_FAILURE);
}
**作用:**将 Socket 与 IP 地址和端口号关联。
4、监听连接
if (listen(server_fd, 5) == -1) { // 最大连接队列长度设为5
perror("listen failed");
exit(EXIT_FAILURE);
}
5、接受客户端连接
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd == -1) {
perror("accept failed");
exit(EXIT_FAILURE);
}
**阻塞特性:**accept() 会阻塞直到有客户端连接。
6、数据收发
char buffer[1024];
ssize_t bytes_received = recv(client_fd, buffer, sizeof(buffer), 0);
if (bytes_received == -1) {
perror("recv failed");
}
const char* response = "Message received";
send(client_fd, response, strlen(response), 0);
7、关闭连接
close(client_fd);
close(server_fd);
二、客户端实现步骤
1、创建 Socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
2、配置服务器地址
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); // 转换IP地址
3、连接服务器
if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("connect failed");
exit(EXIT_FAILURE);
}
4、数据收发
const char* message = "Hello Server";
send(sock, message, strlen(message), 0);
char buffer[1024];
recv(sock, buffer, sizeof(buffer), 0);
printf("Server response: %s\n", buffer);
5、关闭连接
close(sock);
三、关键注意事项
错误处理
每个系统调用后需检查返回值(如 socket() 返回 -1 表示失败),使用 perror() 或 strerror(errno) 输出错误信息。
字节序转换
必须使用 htons() 和 htonl() 转换端口和 IP 地址,避免因主机与网络字节序不同导致通信失败。
多线程优化
服务端可通过 pthread_create() 为每个客户端创建独立线程(示例代码见网页1),避免阻塞主线程。
协议选择
TCP:可靠传输,需三次握手(SOCK_STREAM)。
UDP:无连接传输(SOCK_DGRAM),需改用 sendto() 和 recvfrom()。
四、完整示例代码
服务端代码(简化版)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t addr_len = sizeof(client_addr);
char buffer[BUFFER_SIZE] = {0};
// 1. 创建Socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// 2. 配置地址结构体
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // 绑定所有网卡[3](@ref)
server_addr.sin_port = htons(PORT); // 端口转换字节序[1](@ref)
// 3. 绑定地址
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("Bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 4. 开始监听
if (listen(server_fd, 5) == -1) { // 最大队列长度5[6](@ref)
perror("Listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
// 5. 接受客户端连接
if ((client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &addr_len)) == -1) {
perror("Accept failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Client connected from %s:%d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// 6. 数据收发
ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE);
printf("Received: %s\n", buffer);
const char* response = "Message received";
send(client_fd, response, strlen(response), 0);
// 7. 关闭连接
close(client_fd);
close(server_fd);
return 0;
}
客户端代码(简化版)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8080
#define SERVER_IP "127.0.0.1"
#define BUFFER_SIZE 1024
int main() {
int sock;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE] = {0};
// 1. 创建Socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// 2. 配置服务器地址
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) { // IP转换[7](@ref)
perror("Invalid address");
close(sock);
exit(EXIT_FAILURE);
}
// 3. 连接服务器
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
perror("Connection failed");
close(sock);
exit(EXIT_FAILURE);
}
printf("Connected to server\n");
// 4. 发送数据
const char* message = "Hello from client";
send(sock, message, strlen(message), 0);
printf("Message sent\n");
// 5. 接收响应
read(sock, buffer, BUFFER_SIZE);
printf("Server response: %s\n", buffer);
// 6. 关闭连接
close(sock);
return 0;
}