Socket通信指南(TCP/UDP)
数据由AI生成。
1. Socket通信概述
在鸿蒙系统中,Socket通信分为TCP和UDP两种协议:
TCP:面向连接、可靠传输、基于字节流的协议
UDP:无连接、不可靠传输、基于数据报的协议
2. 鸿蒙Socket API (API12+)
鸿蒙提供了@ohos.net.socket
模块来实现Socket通信。
2.1 引入模块
import socket from '@ohos.net.socket';
3. TCP Socket实现
3.1 TCP客户端实现
// 创建TCP客户端Socket
let tcpClient: socket.TCPSocket = socket.constructTCPSocketInstance();
// 设置Socket选项
tcpClient.bind({
address: '0.0.0.0', // 绑定本地地址
port: 0, // 0表示系统自动分配端口
family: 1 // 1表示IPv4
}, (err) => {
if (err) {
console.error('bind failed: ' + JSON.stringify(err));
return;
}
console.log('bind success');
});
// 连接服务器
tcpClient.connect({
address: '192.168.1.100', // 服务器IP
port: 8080, // 服务器端口
timeout: 5000 // 超时时间(ms)
}, (err) => {
if (err) {
console.error('connect failed: ' + JSON.stringify(err));
return;
}
console.log('connect success');
// 发送byte数据
let byteData = new Uint8Array([0x48, 0x65, 0x6C, 0x6C, 0x6F]); // "Hello"的字节表示
tcpClient.send({
data: byteData.buffer
}, (err) => {
if (err) {
console.error('send failed: ' + JSON.stringify(err));
return;
}
console.log('send success');
});
});
// 接收数据
tcpClient.on('message', (data: ArrayBuffer) => {
console.log('received data length: ' + data.byteLength);
// 将ArrayBuffer转换为Uint8Array
let byteArray = new Uint8Array(data);
// 处理接收到的byte数据
processReceivedData(byteArray);
});
// 错误处理
tcpClient.on('error', (err) => {
console.error('socket error: ' + JSON.stringify(err));
});
// 关闭连接
function closeTCPConnection() {
tcpClient.close((err) => {
if (err) {
console.error('close failed: ' + JSON.stringify(err));
return;
}
console.log('close success');
});
}
3.2 TCP服务器实现
// 创建TCP服务器Socket
let tcpServer: socket.TCPSocketServer = socket.constructTCPSocketServerInstance();
// 监听端口
tcpServer.listen({
address: '0.0.0.0',
port: 8080,
backlog: 10
}, (err) => {
if (err) {
console.error('listen failed: ' + JSON.stringify(err));
return;
}
console.log('listen success');
});
// 接受客户端连接
tcpServer.on('connect', (client: socket.TCPSocketConnection) => {
console.log('client connected');
// 接收客户端数据
client.on('message', (data: ArrayBuffer) => {
console.log('received data length: ' + data.byteLength);
// 将ArrayBuffer转换为Uint8Array
let byteArray = new Uint8Array(data);
// 处理接收到的byte数据
processReceivedData(byteArray);
// 发送响应
let response = new Uint8Array([0x57, 0x6F, 0x72, 0x6C, 0x64]); // "World"的字节表示
client.send({
data: response.buffer
}, (err) => {
if (err) {
console.error('send response failed: ' + JSON.stringify(err));
}
});
});
// 错误处理
client.on('error', (err) => {
console.error('client error: ' + JSON.stringify(err));
client.close();
});
// 连接关闭
client.on('close', () => {
console.log('client disconnected');
});
});
// 关闭服务器
function closeTCPServer() {
tcpServer.close((err) => {
if (err) {
console.error('server close failed: ' + JSON.stringify(err));
return;
}
console.log('server closed');
});
}
4. UDP Socket实现
4.1 UDP发送和接收
// 创建UDP Socket
let udpSocket: socket.UDPSocket = socket.constructUDPSocketInstance();
// 绑定本地端口
udpSocket.bind({
address: '0.0.0.0',
port: 12345,
family: 1 // IPv4
}, (err) => {
if (err) {
console.error('bind failed: ' + JSON.stringify(err));
return;
}
console.log('bind success');
});
// 发送UDP数据报
function sendUDPMessage() {
let byteData = new Uint8Array([0x48, 0x65, 0x6C, 0x6C, 0x6F]); // "Hello"的字节表示
udpSocket.send({
address: '192.168.1.100',
port: 8080,
data: byteData.buffer
}, (err) => {
if (err) {
console.error('send failed: ' + JSON.stringify(err));
return;
}
console.log('send success');
});
}
// 接收UDP数据报
udpSocket.on('message', (data: ArrayBuffer, remoteInfo: socket.SocketRemoteInfo) => {
console.log(`received from ${remoteInfo.address}:${remoteInfo.port}`);
console.log('data length: ' + data.byteLength);
// 将ArrayBuffer转换为Uint8Array
let byteArray = new Uint8Array(data);
// 处理接收到的byte数据
processReceivedData(byteArray);
});
// 错误处理
udpSocket.on('error', (err) => {
console.error('udp error: ' + JSON.stringify(err));
});
// 关闭UDP Socket
function closeUDPSocket() {
udpSocket.close((err) => {
if (err) {
console.error('close failed: ' + JSON.stringify(err));
return;
}
console.log('udp closed');
});
}
5. 数据缓存与处理
5.1 数据缓存实现
// 定义数据缓存区
let dataBuffer: Uint8Array = new Uint8Array(0);
// 处理接收到的数据
function processReceivedData(newData: Uint8Array) {
// 合并新数据到缓存区
let mergedBuffer = new Uint8Array(dataBuffer.length + newData.length);
mergedBuffer.set(dataBuffer);
mergedBuffer.set(newData, dataBuffer.length);
dataBuffer = mergedBuffer;
// 尝试解析数据
tryParseData();
}
// 尝试解析缓存中的数据
function tryParseData() {
// 示例:假设我们的协议是前4字节表示数据长度
if (dataBuffer.length >= 4) {
// 读取数据长度 (大端序)
let dataLength = (dataBuffer[0] << 24) |
(dataBuffer[1] << 16) |
(dataBuffer[2] << 8) |
dataBuffer[3];
// 检查是否收到完整数据包
if (dataBuffer.length >= 4 + dataLength) {
// 提取有效数据
let packetData = dataBuffer.slice(4, 4 + dataLength);
// 处理数据包
handlePacket(packetData);
// 移除已处理的数据
dataBuffer = dataBuffer.slice(4 + dataLength);
// 递归处理剩余数据
if (dataBuffer.length > 0) {
tryParseData();
}
}
}
}
// 处理完整数据包
function handlePacket(packet: Uint8Array) {
console.log('received packet length: ' + packet.length);
// 这里可以根据实际协议进行解析
// 例如转换为字符串:
let str = String.fromCharCode.apply(null, Array.from(packet));
console.log('packet content: ' + str);
// 或者处理为其他格式...
}
5.2 高级缓存管理
对于更复杂的场景,可以实现环形缓冲区:
class CircularBuffer {
private buffer: Uint8Array;
private head: number = 0;
private tail: number = 0;
private capacity: number;
constructor(size: number) {
this.capacity = size;
this.buffer = new Uint8Array(size);
}
// 写入数据
write(data: Uint8Array): boolean {
if (this.availableSpace() < data.length) {
return false; // 空间不足
}
// 写入数据
for (let i = 0; i < data.length; i++) {
this.buffer[this.tail] = data[i];
this.tail = (this.tail + 1) % this.capacity;
}
return true;
}
// 读取数据
read(size: number): Uint8Array | null {
if (this.availableData() < size) {
return null; // 数据不足
}
let result = new Uint8Array(size);
for (let i = 0; i < size; i++) {
result[i] = this.buffer[this.head];
this.head = (this.head + 1) % this.capacity;
}
return result;
}
// 查看数据但不移动指针
peek(size: number): Uint8Array | null {
if (this.availableData() < size) {
return null;
}
let result = new Uint8Array(size);
let tempHead = this.head;
for (let i = 0; i < size; i++) {
result[i] = this.buffer[tempHead];
tempHead = (tempHead + 1) % this.capacity;
}
return result;
}
// 获取可用空间
availableSpace(): number {
if (this.tail >= this.head) {
return this.capacity - this.tail + this.head - 1;
} else {
return this.head - this.tail - 1;
}
}
// 获取可用数据量
availableData(): number {
if (this.tail >= this.head) {
return this.tail - this.head;
} else {
return this.capacity - this.head + this.tail;
}
}
// 清空缓冲区
clear(): void {
this.head = 0;
this.tail = 0;
}
}
// 使用环形缓冲区
const buffer = new CircularBuffer(1024 * 1024); // 1MB缓冲区
// 接收数据时写入缓冲区
socket.on('message', (data: ArrayBuffer) => {
const byteData = new Uint8Array(data);
if (!buffer.write(byteData)) {
console.error('Buffer overflow!');
// 处理缓冲区溢出情况
}
// 尝试解析数据
processBufferData();
});
// 处理缓冲区数据
function processBufferData() {
// 假设协议头是4字节长度
const header = buffer.peek(4);
if (!header) return; // 数据不足
const packetLength = (header[0] << 24) |
(header[1] << 16) |
(header[2] << 8) |
header[3];
// 检查完整数据包是否到达
if (buffer.availableData() >= 4 + packetLength) {
// 跳过头部
buffer.read(4);
// 读取数据包
const packet = buffer.read(packetLength);
if (packet) {
handlePacket(packet);
// 继续处理可能存在的下一个数据包
processBufferData();
}
}
}
6. 性能优化建议
6.1 Socket参数优化
// TCP参数优化
tcpClient.setExtraOptions({
keepAlive: true, // 启用KeepAlive
TCPNoDelay: true, // 禁用Nagle算法
receiveBufferSize: 8192, // 接收缓冲区大小
sendBufferSize: 8192 // 发送缓冲区大小
});
// UDP参数优化
udpSocket.setExtraOptions({
receiveBufferSize: 65536, // 增大UDP接收缓冲区
sendBufferSize: 65536 // 增大UDP发送缓冲区
});
6.2 多线程处理
对于高负载场景,可以使用Worker线程处理数据:
// 主线程
socket.on('message', (data: ArrayBuffer) => {
// 将数据传递给Worker线程处理
workerPort.postMessage(data, [data]);
});
// Worker线程中处理数据
workerPort.onmessage = (event: MessageEvent<ArrayBuffer>) => {
const byteData = new Uint8Array(event.data);
// 复杂的数据处理逻辑...
};
7. 错误处理与重连机制
7.1 TCP自动重连实现
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
const reconnectDelay = 2000; // 2秒
function connectToServer() {
tcpClient.connect({
address: '192.168.1.100',
port: 8080,
timeout: 5000
}, (err) => {
if (err) {
console.error('Connect failed:', err);
if (reconnectAttempts < maxReconnectAttempts) {
reconnectAttempts++;
console.log(`Retrying in ${reconnectDelay/1000} seconds... (Attempt ${reconnectAttempts}/${maxReconnectAttempts})`);
setTimeout(() => {
connectToServer();
}, reconnectDelay);
} else {
console.error('Max reconnect attempts reached');
}
return;
}
console.log('Connected successfully');
reconnectAttempts = 0; // 重置重连计数器
});
}
tcpClient.on('close', () => {
console.log('Connection closed, attempting to reconnect...');
connectToServer();
});
8. 实际应用示例
8.1 实现一个简单的网络协议
假设我们需要实现一个简单的应用层协议:
协议头:4字节长度字段(大端序)
协议体:实际数据
协议尾:2字节CRC校验
// 发送数据
function sendPacket(data: Uint8Array) {
// 计算CRC16校验
const crc = calculateCRC16(data);
// 构造完整数据包
const length = data.length;
const packet = new Uint8Array(4 + length + 2);
// 写入长度 (大端序)
packet[0] = (length >> 24) & 0xFF;
packet[1] = (length >> 16) & 0xFF;
packet[2] = (length >> 8) & 0xFF;
packet[3] = length & 0xFF;
// 写入数据
packet.set(data, 4);
// 写入CRC (大端序)
packet[4 + length] = (crc >> 8) & 0xFF;
packet[4 + length + 1] = crc & 0xFF;
// 发送数据
tcpClient.send({
data: packet.buffer
}, (err) => {
if (err) {
console.error('Send packet failed:', err);
}
});
}
// 接收数据处理
function processReceivedData(data: Uint8Array) {
// 检查最小长度 (4字节长度 + 2字节CRC)
if (data.length < 6) {
console.error('Invalid packet: too short');
return;
}
// 读取长度 (大端序)
const length = (data[0] << 24) |
(data[1] << 16) |
(data[2] << 8) |
data[3];
// 检查数据完整性
if (data.length < 4 + length + 2) {
console.error('Incomplete packet');
return;
}
// 提取数据部分
const payload = data.slice(4, 4 + length);
// 提取CRC
const receivedCRC = (data[4 + length] << 8) | data[4 + length + 1];
// 计算校验
const calculatedCRC = calculateCRC16(payload);
if (receivedCRC !== calculatedCRC) {
console.error('CRC check failed');
return;
}
// 处理有效数据
handleApplicationData(payload);
}
// 简单的CRC16计算
function calculateCRC16(data: Uint8Array): number {
let crc = 0xFFFF;
for (let i = 0; i < data.length; i++) {
crc ^= data[i] << 8;
for (let j = 0; j < 8; j++) {
crc = crc & 0x8000 ? (crc << 1) ^ 0x1021 : crc << 1;
}
}
return crc & 0xFFFF;
}
9. 安全注意事项
数据验证:始终验证接收数据的长度和格式
缓冲区限制:设置合理的缓冲区大小限制,防止内存耗尽
超时处理:为所有网络操作设置合理的超时
错误处理:妥善处理所有可能的错误情况
敏感数据:避免在日志中打印原始二进制数据
// 安全发送示例
function safeSend(socket: socket.TCPSocket, data: Uint8Array) {
try {
if (data.length > 1024 * 1024) { // 限制1MB
throw new Error('Data too large');
}
socket.send({
data: data.buffer
}, (err) => {
if (err) {
console.error('Send error:', err.message); // 不打印详细错误
// 执行重试或恢复逻辑
}
});
} catch (err) {
console.error('Send failed:', err.message);
}
}
10. 测试与调试建议
单元测试:为数据处理逻辑编写单元测试
模拟网络环境:测试在不同网络条件下的表现
压力测试:测试高负载情况下的性能
日志记录:记录关键操作和错误
// 调试日志示例
const debugLog: Array<string> = [];
function logDebug(message: string) {
const timestamp = new Date().toISOString();
debugLog.push(`${timestamp} - ${message}`);
// 控制日志大小
if (debugLog.length > 100) {
debugLog.shift();
}
}
// 网络状态监控
function monitorNetwork() {
setInterval(() => {
const stats = tcpClient.getStats();
logDebug(`Sent: ${stats.bytesSent}, Received: ${stats.bytesReceived}`);
}, 5000);
}
以上内容提供了鸿蒙ArkUI开发中使用Socket进行TCP/UDP通信的完整实现方案,包括数据缓存处理、协议解析、性能优化和安全考虑等方面。开发者可以根据实际需求调整和扩展这些基础实现。