当前位置: 首页 > 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…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的&#xff0c;根据Excel列的需求预估的工时直接打骨折&#xff0c;不要问我为什么&#xff0c;主要…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...