当前位置: 首页 > news >正文

【HarmonyOS NEXT】鸿蒙Socket 连接

简介

Socket 连接主要是通过 Socket 进行数据传输,支持 TCP/UDP/Multicast/TLS 协议。

基本概念

  • Socket:套接字,就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。
  • TCP:传输控制协议(Transmission Control Protocol)。是一种面向连接的、可靠的、基于字节流的传输层通信协议。
  • UDP:用户数据报协议(User Datagram Protocol)。是一个简单的面向消息的传输层,不需要连接。
  • Multicast:多播,基于UDP的一种通信模式,用于实现组内所有设备之间广播形式的通信。
  • LocalSocket:本地套接字,IPC(Inter-Process Communication)进程间通信的一种,实现设备内进程之间相互通信,无需网络。
  • TLS:安全传输层协议(Transport Layer Security)。用于在两个通信应用程序之间提供保密性和数据完整性。

场景介绍

应用通过 Socket 进行数据传输,支持 TCP/UDP/Multicast/TLS 协议。主要场景有:

  • 应用通过 TCP/UDP Socket进行数据传输
  • 应用通过 TCP Socket Server 进行数据传输
  • 应用通过 Multicast Socket 进行数据传输
  • 应用通过 Local Socket进行数据传输
  • 应用通过 Local Socket Server 进行数据传输
  • 应用通过 TLS Socket 进行加密数据传输
  • 应用通过 TLS Socket Server 进行加密数据传输

接口说明

Socket 连接主要由 socket 模块提供。具体接口说明如下表。

接口名描述
constructUDPSocketInstance()创建一个 UDPSocket 对象。
constructTCPSocketInstance()创建一个 TCPSocket 对象。
constructTCPSocketServerInstance()创建一个 TCPSocketServer 对象。
constructMulticastSocketInstance()创建一个 MulticastSocket 对象。
constructLocalSocketInstance()创建一个 LocalSocket 对象。
constructLocalSocketServerInstance()创建一个 LocalSocketServer 对象。
listen()绑定、监听并启动服务,接收客户端的连接请求。(仅 TCP/LocalSocket 支持)。
bind()绑定 IP 地址和端口,或是绑定本地套接字路径。
send()发送数据。
close()关闭连接。
getState()获取 Socket 状态。
connect()连接到指定的 IP 地址和端口,或是连接到本地套接字(仅 TCP/LocalSocket 支持)。
getRemoteAddress()获取对端 Socket 地址(仅 TCP 支持,需要先调用 connect 方法)。
setExtraOptions()设置 Socket 连接的其他属性。
getExtraOptions()获取 Socket 连接的其他属性(仅 LocalSocket 支持)。
addMembership()加入到指定的多播组 IP 中 (仅 Multicast 支持)。
dropMembership()从指定的多播组 IP 中退出 (仅 Multicast 支持)。
setMulticastTTL()设置数据传输跳数 TTL (仅 Multicast 支持)。
getMulticastTTL()获取数据传输跳数 TTL (仅 Multicast 支持)。
setLoopbackMode()设置回环模式,允许主机在本地循环接收自己发送的多播数据包 (仅 Multicast 支持)。
getLoopbackMode()获取回环模式开启或关闭的状态 (仅 Multicast 支持)。
on(type: 'message')订阅 Socket 连接的接收消息事件。
off(type: 'message')取消订阅 Socket 连接的接收消息事件。
on(type: 'close')订阅 Socket 连接的关闭事件。
off(type: 'close')取消订阅 Socket 连接的关闭事件。
on(type: 'error')订阅 Socket 连接的 Error 事件。
off(type: 'error')取消订阅 Socket 连接的 Error 事件。
on(type: 'listening')订阅 UDPSocket 连接的数据包消息事件(仅 UDP 支持)。
off(type: 'listening')取消订阅 UDPSocket 连接的数据包消息事件(仅 UDP 支持)。
on(type: 'connect')订阅 Socket 的连接事件(仅 TCP/LocalSocket 支持)。
off(type: 'connect')取消订阅 Socket 的连接事件(仅 TCP/LocalSocket 支持)。

TLS Socket 连接主要由 tls_socket 模块提供。具体接口说明如下表。

接口名功能描述
constructTLSSocketInstance()创建一个 TLSSocket 对象。
bind()绑定 IP 地址和端口号。
close(type: 'error')关闭连接。
connect()连接到指定的 IP 地址和端口。
getCertificate()返回表示本地证书的对象。
getCipherSuite()返回包含协商的密码套件信息的列表。
getProtocol()返回包含当前连接协商的 SSL/TLS 协议版本的字符串。
getRemoteAddress()获取 TLSSocket 连接的对端地址。
getRemoteCertificate()返回表示对等证书的对象。
getSignatureAlgorithms()在服务器和客户端之间共享的签名算法列表,按优先级降序排列。
getState()获取 TLSSocket 连接的状态。
off(type: 'close')取消订阅 TLSSocket 连接的关闭事件。
off(type: 'error')取消订阅 TLSSocket 连接的 Error 事件。
off(type: 'message')取消订阅 TLSSocket 连接的接收消息事件。
on(type: 'close')订阅 TLSSocket 连接的关闭事件。
on(type: 'error')订阅 TLSSocket 连接的 Error 事件。
on(type: 'message')订阅 TLSSocket 连接的接收消息事件。
send()发送数据。
setExtraOptions()设置 TLSSocket 连接的其他属性。

应用 TCP/UDP 协议进行通信

UDP 与 TCP 流程大体类似,下面以 TCP 为例:

  1. import 需要的 socket 模块。

  2. 创建一个 TCPSocket 连接,返回一个 TCPSocket 对象。

  3. (可选)订阅 TCPSocket 相关的订阅事件。

  4. 绑定 IP 地址和端口,端口可以指定或由系统随机分配。

  5. 连接到指定的 IP 地址和端口。

  6. 发送数据。

  7. Socket 连接使用完毕后,主动关闭。

import { socket } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';class SocketInfo {message: ArrayBuffer = new ArrayBuffer(1);remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo;
}
// 创建一个TCPSocket连接,返回一个TCPSocket对象。
let tcp: socket.TCPSocket = socket.constructTCPSocketInstance();
tcp.on('message', (value: SocketInfo) => {console.log("on message");let buffer = value.message;let dataView = new DataView(buffer);let str = "";for (let i = 0; i < dataView.byteLength; ++i) {str += String.fromCharCode(dataView.getUint8(i));}console.log("on connect received:" + str);
});
tcp.on('connect', () => {console.log("on connect");
});
tcp.on('close', () => {console.log("on close");
});// 绑定本地IP地址和端口。
let ipAddress : socket.NetAddress = {} as socket.NetAddress;
ipAddress.address = "192.168.xxx.xxx";
ipAddress.port = 1234;
tcp.bind(ipAddress, (err: BusinessError) => {if (err) {console.log('bind fail');return;}console.log('bind success');// 连接到指定的IP地址和端口。ipAddress.address = "192.168.xxx.xxx";ipAddress.port = 5678;let tcpConnect : socket.TCPConnectOptions = {} as socket.TCPConnectOptions;tcpConnect.address = ipAddress;tcpConnect.timeout = 6000;tcp.connect(tcpConnect).then(() => {console.log('connect success');let tcpSendOptions: socket.TCPSendOptions = {data: 'Hello, server!'}tcp.send(tcpSendOptions).then(() => {console.log('send success');}).catch((err: BusinessError) => {console.log('send fail');});}).catch((err: BusinessError) => {console.log('connect fail');});
});// 连接使用完毕后,主动关闭。取消相关事件的订阅。
setTimeout(() => {tcp.close().then(() => {console.log('close success');}).catch((err: BusinessError) => {console.log('close fail');});tcp.off('message');tcp.off('connect');tcp.off('close');
}, 30 * 1000);

应用通过 TCP Socket Server 进行数据传输

开发步骤

服务端 TCP Socket 流程:

  1. import 需要的 socket 模块。
  2. 创建一个 TCPSocketServer 连接,返回一个 TCPSocketServer 对象。
  3. 绑定本地 IP 地址和端口,监听并接受与此套接字建立的客户端 TCPSocket 连接。
  4. 订阅 TCPSocketServer 的 connect 事件,用于监听客户端的连接状态。
  5. 客户端与服务端建立连接后,返回一个 TCPSocketConnection 对象,用于与客户端通信。
  6. 订阅 TCPSocketConnection 相关的事件,通过 TCPSocketConnection 向客户端发送数据。
  7. 主动关闭与客户端的连接。
  8. 取消 TCPSocketConnection 和 TCPSocketServer 相关事件的订阅。
import { socket } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';// 创建一个TCPSocketServer连接,返回一个TCPSocketServer对象。
let tcpServer: socket.TCPSocketServer = socket.constructTCPSocketServerInstance();
// 绑定本地IP地址和端口,进行监听let ipAddress : socket.NetAddress = {} as socket.NetAddress;
ipAddress.address = "192.168.xxx.xxx";
ipAddress.port = 4651;
tcpServer.listen(ipAddress).then(() => {console.log('listen success');
}).catch((err: BusinessError) => {console.log('listen fail');
});class SocketInfo {message: ArrayBuffer = new ArrayBuffer(1);remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo;
}
// 订阅TCPSocketServer的connect事件
tcpServer.on("connect", (client: socket.TCPSocketConnection) => {// 订阅TCPSocketConnection相关的事件client.on("close", () => {console.log("on close success");});client.on("message", (value: SocketInfo) => {let buffer = value.message;let dataView = new DataView(buffer);let str = "";for (let i = 0; i < dataView.byteLength; ++i) {str += String.fromCharCode(dataView.getUint8(i));}console.log("received message--:" + str);console.log("received address--:" + value.remoteInfo.address);console.log("received family--:" + value.remoteInfo.family);console.log("received port--:" + value.remoteInfo.port);console.log("received size--:" + value.remoteInfo.size);});// 向客户端发送数据let tcpSendOptions : socket.TCPSendOptions = {} as socket.TCPSendOptions;tcpSendOptions.data = 'Hello, client!';client.send(tcpSendOptions).then(() => {console.log('send success');}).catch((err: Object) => {console.error('send fail: ' + JSON.stringify(err));});// 关闭与客户端的连接client.close().then(() => {console.log('close success');}).catch((err: BusinessError) => {console.log('close fail');});// 取消TCPSocketConnection相关的事件订阅setTimeout(() => {client.off("message");client.off("close");}, 10 * 1000);
});// 取消TCPSocketServer相关的事件订阅
setTimeout(() => {tcpServer.off("connect");
}, 30 * 1000);

应用通过 Multicast Socket 进行数据传输

开发步骤

  1. import 需要的 socket 模块。

  2. 创建 multicastSocket 多播对象。

  3. 指定多播 IP 与端口,加入多播组。

  4. 开启消息 message 监听。

  5. 发送数据,数据以广播的形式传输,同一多播组中已经开启消息 message 监听的多播对象都会接收到数据。

  6. 关闭 message 消息的监听。

  7. 退出多播组。

import { socket } from '@kit.NetworkKit';// 创建Multicast对象
let multicast: socket.MulticastSocket = socket.constructMulticastSocketInstance();let addr : socket.NetAddress = {address: '239.255.0.1',port: 32123,family: 1
}// 加入多播组
multicast.addMembership(addr).then(() => {console.log('addMembership success');
}).catch((err: Object) => {console.log('addMembership fail');
});// 开启监听消息数据,将接收到的ArrayBuffer类型数据转换为String
class SocketInfo {message: ArrayBuffer = new ArrayBuffer(1);remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo;
}
multicast.on('message', (data: SocketInfo) => {console.info('接收的数据: ' + JSON.stringify(data))const uintArray = new Uint8Array(data.message)let str = ''for (let i = 0; i < uintArray.length; ++i) {str += String.fromCharCode(uintArray[i])}console.info(str)
})// 发送数据
multicast.send({ data:'Hello12345', address: addr }).then(() => {console.log('send success');
}).catch((err: Object) => {console.log('send fail, ' + JSON.stringify(err));
});// 关闭消息的监听
multicast.off('message')// 退出多播组
multicast.dropMembership(addr).then(() => {console.log('drop membership success');
}).catch((err: Object) => {console.log('drop membership fail');
});

应用通过 LocalSocket 进行数据传输

开发步骤:

  1. import 需要的 socket 模块。

  2. 使用 constructLocalSocketInstance 接口,创建一个 LocalSocket 客户端对象。

  3. 注册 LocalSocket 的消息(message)事件,以及一些其它事件(可选)。

  4. 连接到指定的本地套接字文件路径。

  5. 发送数据。

  6. Socket 连接使用完毕后,取消事件的注册,并关闭套接字。

import { socket } from '@kit.NetworkKit';// 创建一个LocalSocket连接,返回一个LocalSocket对象。
let client: socket.LocalSocket = socket.constructLocalSocketInstance();
client.on('message', (value: socket.LocalSocketMessageInfo) => {const uintArray = new Uint8Array(value.message)let messageView = '';for (let i = 0; i < uintArray.length; i++) {messageView += String.fromCharCode(uintArray[i]);}console.log('total receive: ' + JSON.stringify(value));console.log('message information: ' + messageView);
});
client.on('connect', () => {console.log("on connect");
});
client.on('close', () => {console.log("on close");
});// 传入指定的本地套接字路径,连接服务端。
let sandboxPath: string = getContext(this).filesDir + '/testSocket'
let localAddress : socket.LocalAddress = {address: sandboxPath
}
let connectOpt: socket.LocalConnectOptions = {address: localAddress,timeout: 6000
}
let sendOpt: socket.LocalSendOptions = {data: 'Hello world!'
}
client.connect(connectOpt).then(() => {console.log('connect success')client.send(sendOpt).then(() => {console.log('send success')}).catch((err: Object) => {console.log('send failed: ' + JSON.stringify(err))})
}).catch((err: Object) => {console.log('connect fail: ' + JSON.stringify(err));
});// 当不需要再连接服务端,需要断开且取消事件的监听时
client.off('message');
client.off('connect');
client.off('close');
client.close().then(() => {console.log('close client success')
}).catch((err: Object) => {console.log('close client err: ' + JSON.stringify(err))
})

应用通过 Local Socket Server 进行数据传输

开发步骤

服务端 LocalSocket Server 流程:

  1. import 需要的 socket 模块。

  2. 使用 constructLocalSocketServerInstance 接口,创建一个 LocalSocketServer 服务端对象。

  3. 启动服务,绑定本地套接字路径,创建出本地套接字文件,监听客户端的连接请求。

  4. 注册 LocalSocket 的客户端连接(connect)事件,以及一些其它事件(可选)。

  5. 在客户端连接上来时,通过连接事件的回调函数,获取连接会话对象。

  6. 给会话对象 LocalSocketConnection 注册消息(message)事件,以及一些其它事件(可选)。

  7. 通过会话对象主动向客户端发送消息。

  8. 结束与客户端的通信,主动断开与客户端的连接。

  9. 取消 LocalSocketConnection 和 LocalSocketServer 相关事件的订阅。

import { socket } from '@kit.NetworkKit';// 创建一个LocalSocketServer连接,返回一个LocalSocketServer对象。
let server: socket.LocalSocketServer = socket.constructLocalSocketServerInstance();
// 创建并绑定本地套接字文件testSocket,进行监听
let sandboxPath: string = getContext(this).filesDir + '/testSocket'
let listenAddr: socket.LocalAddress = {address: sandboxPath
}
server.listen(listenAddr).then(() => {console.log("listen success");
}).catch((err: Object) => {console.log("listen fail: " + JSON.stringify(err));
});// 订阅LocalSocketServer的connect事件
server.on('connect', (connection: socket.LocalSocketConnection) => {// 订阅LocalSocketConnection相关的事件connection.on('error', (err: Object) => {console.log("on error success");});connection.on('message', (value: socket.LocalSocketMessageInfo) => {const uintArray = new Uint8Array(value.message);let messageView = '';for (let i = 0; i < uintArray.length; i++) {messageView += String.fromCharCode(uintArray[i]);}console.log('total: ' + JSON.stringify(value));console.log('message information: ' + messageView);});connection.on('error', (err: Object) => {console.log("err:" + JSON.stringify(err));})// 向客户端发送数据let sendOpt : socket.LocalSendOptions = {data: 'Hello world!'};connection.send(sendOpt).then(() => {console.log('send success');}).catch((err: Object) => {console.log('send failed: ' + JSON.stringify(err));})// 关闭与客户端的连接connection.close().then(() => {console.log('close success');}).catch((err: Object) => {console.log('close failed: ' + JSON.stringify(err));});// 取消LocalSocketConnection相关的事件订阅connection.off('message');connection.off('error');
});// 取消LocalSocketServer相关的事件订阅
server.off('connect');
server.off('error');

应用通过 TLS Socket 进行加密数据传输

开发步骤

客户端 TLS Socket 流程:

  1. import 需要的 socket 模块。

  2. 绑定服务器 IP 和端口号。

  3. 双向认证上传客户端 CA 证书及数字证书;单向认证上传客户端 CA 证书。

  4. 创建一个 TLSSocket 连接,返回一个 TLSSocket 对象。

  5. (可选)订阅 TLSSocket 相关的订阅事件。

  6. 发送数据。

  7. TLSSocket 连接使用完毕后,主动关闭。

import { socket } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';class SocketInfo {message: ArrayBuffer = new ArrayBuffer(1);remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo;
}
// 创建一个(双向认证)TLS Socket连接,返回一个TLS Socket对象。
let tlsTwoWay: socket.TLSSocket = socket.constructTLSSocketInstance();
// 订阅TLS Socket相关的订阅事件
tlsTwoWay.on('message', (value: SocketInfo) => {console.log("on message");let buffer = value.message;let dataView = new DataView(buffer);let str = "";for (let i = 0; i < dataView.byteLength; ++i) {str += String.fromCharCode(dataView.getUint8(i));}console.log("on connect received:" + str);
});
tlsTwoWay.on('connect', () => {console.log("on connect");
});
tlsTwoWay.on('close', () => {console.log("on close");
});// 绑定本地IP地址和端口。
let ipAddress : socket.NetAddress = {} as socket.NetAddress;
ipAddress.address = "192.168.xxx.xxx";
ipAddress.port = 4512;
tlsTwoWay.bind(ipAddress, (err: BusinessError) => {if (err) {console.log('bind fail');return;}console.log('bind success');
});ipAddress.address = "192.168.xxx.xxx";
ipAddress.port = 1234;let tlsSecureOption : socket.TLSSecureOptions = {} as socket.TLSSecureOptions;
tlsSecureOption.key = "xxxx";
tlsSecureOption.cert = "xxxx";
tlsSecureOption.ca = ["xxxx"];
tlsSecureOption.password = "xxxx";
tlsSecureOption.protocols = [socket.Protocol.TLSv12];
tlsSecureOption.useRemoteCipherPrefer = true;
tlsSecureOption.signatureAlgorithms = "rsa_pss_rsae_sha256:ECDSA+SHA256";
tlsSecureOption.cipherSuite = "AES256-SHA256";let tlsTwoWayConnectOption : socket.TLSConnectOptions = {} as socket.TLSConnectOptions;
tlsSecureOption.key = "xxxx";
tlsTwoWayConnectOption.address = ipAddress;
tlsTwoWayConnectOption.secureOptions = tlsSecureOption;
tlsTwoWayConnectOption.ALPNProtocols = ["spdy/1", "http/1.1"];// 建立连接
tlsTwoWay.connect(tlsTwoWayConnectOption).then(() => {console.log("connect successfully");
}).catch((err: BusinessError) => {console.log("connect failed " + JSON.stringify(err));
});// 连接使用完毕后,主动关闭。取消相关事件的订阅。
tlsTwoWay.close((err: BusinessError) => {if (err) {console.log("close callback error = " + err);} else {console.log("close success");}tlsTwoWay.off('message');tlsTwoWay.off('connect');tlsTwoWay.off('close');
});// 创建一个(单向认证)TLS Socket连接,返回一个TLS Socket对象。
let tlsOneWay: socket.TLSSocket = socket.constructTLSSocketInstance(); // One way authentication// 订阅TLS Socket相关的订阅事件
tlsTwoWay.on('message', (value: SocketInfo) => {console.log("on message");let buffer = value.message;let dataView = new DataView(buffer);let str = "";for (let i = 0; i < dataView.byteLength; ++i) {str += String.fromCharCode(dataView.getUint8(i));}console.log("on connect received:" + str);
});
tlsTwoWay.on('connect', () => {console.log("on connect");
});
tlsTwoWay.on('close', () => {console.log("on close");
});// 绑定本地IP地址和端口。
ipAddress.address = "192.168.xxx.xxx";
ipAddress.port = 5445;
tlsOneWay.bind(ipAddress, (err:BusinessError) => {if (err) {console.log('bind fail');return;}console.log('bind success');
});ipAddress.address = "192.168.xxx.xxx";
ipAddress.port = 8789;
let tlsOneWaySecureOption : socket.TLSSecureOptions = {} as socket.TLSSecureOptions;
tlsOneWaySecureOption.ca = ["xxxx", "xxxx"];
tlsOneWaySecureOption.cipherSuite = "AES256-SHA256";let tlsOneWayConnectOptions: socket.TLSConnectOptions = {} as socket.TLSConnectOptions;
tlsOneWayConnectOptions.address = ipAddress;
tlsOneWayConnectOptions.secureOptions = tlsOneWaySecureOption;// 建立连接
tlsOneWay.connect(tlsOneWayConnectOptions).then(() => {console.log("connect successfully");
}).catch((err: BusinessError) => {console.log("connect failed " + JSON.stringify(err));
});// 连接使用完毕后,主动关闭。取消相关事件的订阅。
tlsTwoWay.close((err: BusinessError) => {if (err) {console.log("close callback error = " + err);} else {console.log("close success");}tlsTwoWay.off('message');tlsTwoWay.off('connect');tlsTwoWay.off('close');
});

应用通过 TLS Socket Server 进行加密数据传输

开发步骤

服务端 TLS Socket Server 流程:

  1. import 需要的 socket 模块。

  2. 启动服务,绑定 IP 和端口号,监听客户端连接,创建并初始化 TLS 会话,加载证书密钥并验证。

  3. 订阅 TLSSocketServer 的连接事件。

  4. 收到客户端连接,通过回调得到 TLSSocketConnection 对象。

  5. 订阅 TLSSocketConnection 相关的事件。

  6. 发送数据。

  7. TLSSocketConnection 连接使用完毕后,断开连接。

  8. 取消订阅 TLSSocketConnection 以及 TLSSocketServer 的相关事件。

    import { socket } from '@kit.NetworkKit';
    import { BusinessError } from '@kit.BasicServicesKit';let tlsServer: socket.TLSSocketServer = socket.constructTLSSocketServerInstance();let netAddress: socket.NetAddress = {address: '192.168.xx.xxx',port: 8080
    }let tlsSecureOptions: socket.TLSSecureOptions = {key: "xxxx",cert: "xxxx",ca: ["xxxx"],password: "xxxx",protocols: socket.Protocol.TLSv12,useRemoteCipherPrefer: true,signatureAlgorithms: "rsa_pss_rsae_sha256:ECDSA+SHA256",cipherSuite: "AES256-SHA256"
    }let tlsConnectOptions: socket.TLSConnectOptions = {address: netAddress,secureOptions: tlsSecureOptions,ALPNProtocols: ["spdy/1", "http/1.1"]
    }tlsServer.listen(tlsConnectOptions).then(() => {console.log("listen callback success");
    }).catch((err: BusinessError) => {console.log("failed" + err);
    });class SocketInfo {message: ArrayBuffer = new ArrayBuffer(1);remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo;
    }
    let callback = (value: SocketInfo) => {let messageView = '';for (let i: number = 0; i < value.message.byteLength; i++) {let uint8Array = new Uint8Array(value.message)let messages = uint8Array[i]let message = String.fromCharCode(messages);messageView += message;}console.log('on message message: ' + JSON.stringify(messageView));console.log('remoteInfo: ' + JSON.stringify(value.remoteInfo));
    }
    tlsServer.on('connect', (client: socket.TLSSocketConnection) => {client.on('message', callback);// 发送数据client.send('Hello, client!').then(() => {console.log('send success');}).catch((err: BusinessError) => {console.log('send fail');});// 断开连接client.close().then(() => {console.log('close success');}).catch((err: BusinessError) => {console.log('close fail');});// 可以指定传入on中的callback取消一个订阅,也可以不指定callback清空所有订阅。client.off('message', callback);client.off('message');
    });// 取消订阅tlsServer的相关事件
    tlsServer.off('connect');

相关文章:

【HarmonyOS NEXT】鸿蒙Socket 连接

简介 Socket 连接主要是通过 Socket 进行数据传输&#xff0c;支持 TCP/UDP/Multicast/TLS 协议。 基本概念 Socket&#xff1a;套接字&#xff0c;就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。TCP&#xff1a;传输控制协议(Transmission Control Proto…...

1978Springboot在线维修预约服务应用系统idea开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot在线维修预约服务应用系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发 &#xff09;&#xff0c;系统具有完整的源代码和…...

【vue】实现自动轮播+滚轮控制

前言 有一个无缝轮播+滚轮控制的需求,找了很多的方法发现都没办法完美的实现这种效果。 用原生的js实现不是无缝滚动 用无缝滚动插件实现,发现pc端无法实现滚轮控制 目标 1 实现表格表无缝循环滚动 2 实现滚轮控制表格表数据滚动 3 掌握vue-seamless-scroll使用方式 一些思考…...

鸿翼FEX文件安全交换系统,打造安全高效的文件摆渡“绿色通道”

随着数字经济时代的到来&#xff0c;数据已成为最有价值的生产要素&#xff0c;是企业的重要资产之一。随着数据流动性的增强&#xff0c;数据安全问题也随之突显。尤其是政务、金融、医疗和制造业等关键领域组织和中大型企业&#xff0c;面临着如何在保障数据安全的同时&#…...

苹果电脑虚拟机运行Windows Mac环境安装Win PD19虚拟机 parallels desktop19虚拟机安装教程免费密钥激活

在如今多元的数字时代&#xff0c;我们经常需要在不同的操作系统环境下进行工作和学习。而对于 Mac 用户来说&#xff0c;有时候需要在自己的电脑上安装 Windows 操作系统&#xff0c;以体验更多软件及功能&#xff0c;而在 Mac 安装 Windows 虚拟机是常用的一种操作。下面就来…...

昇思25天学习打卡营第11天|基于MindSpore通过GPT实现情感分类

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) 基于MindSpore通过GPT实现情感分类 %%capture captured_output # 实验环境已经预装了mindspore2.2.14&#xff0c;如需更换mindspore版本&#xff0c;可更改下面mindspore的版本号 !pip uninsta…...

【Python】变量与基本数据类型

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️Python】 文章目录 前言变量声明变量变量的命名规则 变量赋值多个变量赋值 标准数据类型变量的使用方式存储和访问数据&#xff1a;参与逻辑运算和数学运算在函数间传递数据构建复杂的数据结构 NameE…...

Unity按键表大全

Unity键值对应表# KeyCode是由Event.keyCode返回的。这些直接映射到键盘上的物理键&#xff0c;以下是键值对应列表&#xff1a; 常用键# Backspace 退格键 Delete Delete键 TabTab键 Clear Clear键 Return 回车键 Pause 暂停键 Escape ESC键 Space 空格键 小键盘# …...

第一周java。2

方法的作用 将重复的代码包装起来&#xff0c;写成方法&#xff0c;提高代码的复用性。 方法的语法 方法的语法格式如下 : [修饰符] 方法返回值类型 方法名(形参列表) { //由零条到多条可执行性语句组成的方法体return 返回值; } 定义方法语法格式的详细说明如下&#xf…...

Arduino - Keypad 键盘

Arduino - Keypad Arduino - Keypad The keypad is widely used in many devices such as door lock, ATM, calculator… 键盘广泛应用于门锁、ATM、计算器等多种设备中。 In this tutorial, we will learn: 在本教程中&#xff0c;我们将学习&#xff1a; How to use key…...

国产芯片方案/蓝牙咖啡电子秤方案研发

咖啡电子秤芯片方案精确值可做到分度值0.1g的精准称重,并带有过载提示、自动归零、去皮称重、压低报警等功能&#xff0c;工作电压在2.4V~3.6V之间&#xff0c;满足于咖啡电子秤的电压使用。同时咖啡电子秤PCBA设计可支持四个单位显示&#xff0c;分别为&#xff1a;g、lb、oz、…...

reactjs18 中使用@reduxjs/toolkit同步异步数据的使用

react18 中使用reduxjs/toolkit 1.安装依赖包 yarn add reduxjs/toolkit react-redux2.创建 store 根目录下面创建 store 文件夹&#xff0c;然后创建 index.js 文件。 import { configureStore } from "reduxjs/toolkit"; import { counterReducer } from "…...

剧本杀小程序:助力商家发展,提高游戏体验

近几年&#xff0c;剧本杀游戏已经成为了当下年轻人娱乐的游戏社交方式。与其他游戏相比&#xff0c;剧本杀游戏具有强大的社交性&#xff0c;玩家在游戏中既可以推理玩游戏&#xff0c;也可以与其他玩家交流互动&#xff0c;提高玩家的游戏体验感。 随着互联网的发展&#xf…...

pikachu靶场 利用Rce上传一句话木马案例(工具:中国蚁剑)

目录 一、准备靶场&#xff0c;进入RCE 二、测试写入文件 三、使用中国蚁剑 一、准备靶场&#xff0c;进入RCE 我这里用的是pikachu 打开pikachu靶场&#xff0c;选择 RCE > exec "ping" 测试是否存在 Rce 漏洞 因为我们猜测在这个 ping 功能是直接调用系统…...

CenterOS7安装java

CenterOS7安装java #进入安装目录 cd /usr/local/soft/java#wget下载java8 #直接进入官网选择相应的版本进行下载&#xff0c;然后把下载链接复制下来就可以下载了 #不时间的下载链接不一样 wget http://download.oracle.com/otn-pub/java/jdk/8u181-b13/96a7b8442fe848ef90c9…...

react 重新加载子组件

在React中&#xff0c;要重新加载某个子组件&#xff0c;你可以通过改变该组件的key属性来强制它重新渲染。这是因为React会在key变化时销毁旧的组件实例并创建一个新的实例。 多的不说直接上代码 import React, { useState } from react; import ChildComponent from ../chil…...

从零开始使用WordPress搭建个人网站并一键发布公网详细教程

文章目录 前言1. 搭建网站&#xff1a;安装WordPress2. 搭建网站&#xff1a;创建WordPress数据库3. 搭建网站&#xff1a;安装相对URL插件4. 搭建网站&#xff1a;内网穿透发布网站4.1 命令行方式&#xff1a;4.2. 配置wordpress公网地址 5. 固定WordPress公网地址5.1. 固定地…...

浅谈chrome引擎

Chrome引擎主要包括其浏览器内核Blink、JavaScript引擎V8以及其渲染、网络、安全等子系统。下面我将对这些关键部分进行简要说明分析 1. Blink浏览器内核 Blink是Google开发的浏览器排版引擎&#xff0c;自Chrome 28版本起替代了Webkit作为Chrome的渲染引擎。Blink基于Webkit…...

【常用知识点-Java】创建文件夹

Author&#xff1a;赵志乾 Date&#xff1a;2024-07-04 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 简介 java.io.File提供了mkdir()和mkdirs()方法创建文件夹&#xff0c;两者区别&#xff1a;mkdir()仅创建单层文件夹&#xff0c;如…...

【JavaScript脚本宇宙】颜色处理神器大比拼:哪款JavaScript库最适合你?

提升设计与开发效率&#xff1a;深入解析六大颜色处理库 前言 在现代前端开发中&#xff0c;颜色处理是设计和用户体验的重要组成部分。无论是网页设计、数据可视化还是图形设计&#xff0c;都需要强大的颜色处理功能来实现多样化的视觉效果。本文将探讨几种流行的JavaScript…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...