diff --git a/uni_modules/uni-websocket/changelog.md b/uni_modules/uni-websocket/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/uni_modules/uni-websocket/package.json b/uni_modules/uni-websocket/package.json new file mode 100644 index 0000000000000000000000000000000000000000..0462385377c32c9092a0a65347d2debcc65e7cd3 --- /dev/null +++ b/uni_modules/uni-websocket/package.json @@ -0,0 +1,141 @@ +{ + "id": "uni-websocket", + "displayName": "uni-websocket", + "version": "1.0.0", + "description": "uni-websocket", + "keywords": [ + "uni-websocket" + ], + "repository": "", + "engines": { + "HBuilderX": "^3.6.8" + }, + "dcloudext": { + "type": "uts", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "", + "data": "", + "permissions": "" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "uni-ext-api": { + "uni": { + "connectSocket": { + "name": "connectSocket", + "app": { + "js": false, + "kotlin": true, + "swift": false + } + }, + "sendSocketMessage": { + "name": "sendSocketMessage", + "app": { + "js": false, + "kotlin": true, + "swift": false + } + }, + "closeSocket": { + "name": "closeSocket", + "app": { + "js": false, + "kotlin": true, + "swift": false + } + }, + "onSocketOpen": { + "name": "onSocketOpen", + "app": { + "js": false, + "kotlin": true, + "swift": false + } + }, + "onSocketMessage": { + "name": "onSocketMessage", + "app": { + "js": false, + "kotlin": true, + "swift": false + } + }, + "onSocketClose": { + "name": "onSocketClose", + "app": { + "js": false, + "kotlin": true, + "swift": false + } + }, + "onSocketError": { + "name": "onSocketError", + "app": { + "js": false, + "kotlin": true, + "swift": false + } + } + } + }, + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "u", + "aliyun": "u" + }, + "client": { + "Vue": { + "vue2": "u", + "vue3": "u" + }, + "App": { + "app-android": "u", + "app-ios": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "钉钉": "u", + "快手": "u", + "飞书": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-websocket/readme.md b/uni_modules/uni-websocket/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..7ea24816f2024904b21ec347d3847272958b7bd7 --- /dev/null +++ b/uni_modules/uni-websocket/readme.md @@ -0,0 +1,6 @@ +# uni-websocket +### 开发文档 +[UTS 语法](https://uniapp.dcloud.net.cn/tutorial/syntax-uts.html) +[UTS API插件](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html) +[UTS 组件插件](https://uniapp.dcloud.net.cn/plugin/uts-component.html) +[Hello UTS](https://gitcode.net/dcloud/hello-uts) \ No newline at end of file diff --git a/uni_modules/uni-websocket/utssdk/app-android/config.json b/uni_modules/uni-websocket/utssdk/app-android/config.json new file mode 100644 index 0000000000000000000000000000000000000000..fcfc9c4a8d0fcd5e82bf401d8892c0089879a5ee --- /dev/null +++ b/uni_modules/uni-websocket/utssdk/app-android/config.json @@ -0,0 +1,6 @@ +{ + "dependencies": [ + "com.squareup.okhttp3:okhttp:3.12.12" + ], + "minSdkVersion": "19" +} diff --git a/uni_modules/uni-websocket/utssdk/app-android/index.uts b/uni_modules/uni-websocket/utssdk/app-android/index.uts new file mode 100644 index 0000000000000000000000000000000000000000..82c2a9450a449aadf253d4b22b79e8da8077524f --- /dev/null +++ b/uni_modules/uni-websocket/utssdk/app-android/index.uts @@ -0,0 +1,31 @@ +import { ConnectSocket, ConnectSocketOptions, SocketTask, SendSocketMessage, SendSocketMessageOptions, CloseSocket, CloseSocketOptions, OnSocketOpen, OnSocketOpenCallback, OnSocketError, OnSocketErrorCallback, OnSocketMessage, OnSocketMessageCallback, OnSocketClose, OnSocketCloseCallback } from "../interface"; +import { WebSocketManager } from "./websocket/WebSocketManager" + + +export const connectSocket : ConnectSocket = (options : ConnectSocketOptions) : SocketTask => { + return WebSocketManager.getInstance().connectSocket(options); +} + +export const sendSocketMessage : SendSocketMessage = (options : SendSocketMessageOptions) : void => { + return WebSocketManager.getInstance().sendSocketMessage(options); +} + +export const closeSocket : CloseSocket = (options : CloseSocketOptions) : void => { + return WebSocketManager.getInstance().closeSocket(options); +} + +export const onSocketOpen : OnSocketOpen = (callback : OnSocketOpenCallback) : void => { + return WebSocketManager.getInstance().onSocketOpen(callback); +} + +export const onSocketMessage : OnSocketMessage = (callback : OnSocketMessageCallback) : void => { + return WebSocketManager.getInstance().onSocketMessage(callback); +} + +export const onSocketClose : OnSocketClose = (callback : OnSocketCloseCallback) : void => { + return WebSocketManager.getInstance().onSocketClose(callback); +} + +export const onSocketError : OnSocketError = (callback : OnSocketErrorCallback) : void => { + return WebSocketManager.getInstance().onSocketError(callback); +} \ No newline at end of file diff --git a/uni_modules/uni-websocket/utssdk/app-android/websocket/WebSocketManager.uts b/uni_modules/uni-websocket/utssdk/app-android/websocket/WebSocketManager.uts new file mode 100644 index 0000000000000000000000000000000000000000..6d3982a17b8e7baf4d5f880cf4432ac2b8a3f96c --- /dev/null +++ b/uni_modules/uni-websocket/utssdk/app-android/websocket/WebSocketManager.uts @@ -0,0 +1,292 @@ +import { ConnectSocket, ConnectSocketOptions, SocketTask, SendSocketMessageOptions, CloseSocketOptions, OnSocketOpenCallbackResult, GeneralCallbackResult, OnSocketMessageCallbackResult, OnSocketOpenCallback, OnSocketMessageCallback, OnSocketCloseCallback, OnSocketErrorCallback, OnSocketCloseCallbackResult, OnSocketErrorCallbackResult, ConnectSocketSuccess } from "../../interface"; +import { WebsockerClient } from "./WebsockerClient" +import UTSAndroid from 'io.dcloud.uts.UTSAndroid'; +class SimpleSocketTask implements SocketTask { + + private client : WebsockerClient | null = null; + + private openCallbacks : Array<(result : OnSocketOpenCallbackResult) => void> = []; + private closeCallbacks : Array<(result : any) => void> = []; + private errorCallbacks : Array<(result : GeneralCallbackResult) => void> = []; + private messageCallbacks : Array<(result : OnSocketMessageCallbackResult) => void> = []; + constructor(client : WebsockerClient) { + this.client = client; + } + + public dispatchOpen(options : OnSocketOpenCallbackResult) { + for (let i = 0; i < this.openCallbacks.length; i++) { + const callback = this.openCallbacks[i]; + callback(options); + } + } + + public dispatchClose(options : any) { + for (let i = 0; i < this.closeCallbacks.length; i++) { + const callback = this.closeCallbacks[i]; + callback(options); + } + } + + public dispatchError(options : GeneralCallbackResult) { + for (let i = 0; i < this.errorCallbacks.length; i++) { + const callback = this.errorCallbacks[i]; + callback(options); + } + } + + public dispatchMessage(options : OnSocketMessageCallbackResult) { + for (let i = 0; i < this.messageCallbacks.length; i++) { + const callback = this.messageCallbacks[i]; + callback(options); + } + } + /** + * 通过 WebSocket 连接发送数据 + */ + send(options : SendSocketMessageOptions) : void { + if (this.client == null) { + const fail = options.fail; + const complete = options.complete; + let result : GeneralCallbackResult = { + errMsg: "sendSocketMessage:fail WebSocket is not connected" + } + fail?.(result); + complete?.(result); + return + } + this.client?.send(options) + } + /** + * 关闭 WebSocket 连接 + */ + close(options : CloseSocketOptions) : void { + if (this.client == null) { + const fail = options.fail; + const complete = options.complete; + let result : GeneralCallbackResult = { + errMsg: "closeSocket:fail WebSocket is not connected" + } + fail?.(result); + complete?.(result); + return + } + this.client?.close(options) + } + /** + * 监听 WebSocket 连接打开事件 + */ + onOpen(callback : (result : OnSocketOpenCallbackResult) => void) : void { + this.openCallbacks.push(callback); + } + /** + * 监听 WebSocket 连接关闭事件 + */ + onClose(callback : (result : any) => void) : void { + this.closeCallbacks.push(callback); + } + /** + * 监听 WebSocket 错误 + */ + onError(callback : (result : GeneralCallbackResult) => void) : void { + this.errorCallbacks.push(callback); + } + /** + * 监听WebSocket接受到服务器的消息事件 + */ + onMessage(callback : (result : OnSocketMessageCallbackResult) => void) : void { + this.messageCallbacks.push(callback); + } +} + +export interface WebSocketManagerListener { + onOpen(client : WebsockerClient, header : any) : void; + + onMessage(client : WebsockerClient, data : string) : void; + + onClose(client : WebsockerClient, code : number, reason : string) : void; + + onError(client : WebsockerClient, msg : string) : void; +} + +export class WebSocketManager implements WebSocketManagerListener { + private static instance : WebSocketManager | null = null; + + // 数组存储sockettask , 当uni.开头调用的时候,只作用于0元素. 这个task数组,当error或者close的时候 ,会删除. + private socketTasks : SocketTask[] = []; + + /** + * client与task的绑定关系, 用于通过client查找task. + */ + private taskMap : Map = new Map(); + + private openCallback : OnSocketOpenCallback | null = null; + + private messageCallback : OnSocketMessageCallback | null = null; + + private closeCallback : OnSocketCloseCallback | null = null; + + private errorCallback : OnSocketErrorCallback | null = null; + + + public static getInstance() : WebSocketManager { + if (this.instance == null) { + this.instance = new WebSocketManager(); + } + return this.instance!; + } + + + public connectSocket(options : ConnectSocketOptions) : SocketTask { + const webscoketClient = new WebsockerClient(options, this); + let task = new SimpleSocketTask(webscoketClient); + this.taskMap.set(webscoketClient, task); + this.socketTasks.push(task); + const success = options.success; + const complete = options.complete; + let result : ConnectSocketSuccess = { + errMsg: "connectSocket:ok" + } + success?.(result) + complete?.(result) + webscoketClient.connect(); + return task; + } + + public sendSocketMessage(options : SendSocketMessageOptions) : void { + if (this.socketTasks.length > 0) { + const task = this.socketTasks[0]; + task.send(options); + } else { + const fail = options.fail; + const complete = options.complete; + let result : GeneralCallbackResult = { + errMsg: "sendSocketMessage:fail WebSocket is not connected" + } + fail?.(result); + complete?.(result); + } + } + + public closeSocket(options : CloseSocketOptions) : void { + if (this.socketTasks.length > 0) { + const task = this.socketTasks[0]; + task.close(options); + } else { + const fail = options.fail; + const complete = options.complete; + let result : GeneralCallbackResult = { + errMsg: "closeSocket:fail WebSocket is not connected" + } + fail?.(result); + complete?.(result); + } + } + + + public onSocketOpen(callback : OnSocketOpenCallback) : void { + this.openCallback = callback; + } + + public onSocketError(callback : OnSocketErrorCallback) : void { + this.errorCallback = callback; + } + + public onSocketClose(callback : OnSocketCloseCallback) : void { + this.closeCallback = callback; + } + + public onSocketMessage(callback : OnSocketMessageCallback) : void { + this.messageCallback = callback; + } + + getTaskWithClient(client : WebsockerClient) : SocketTask | null { + return this.taskMap.get(client); + } + + + onOpen(client : WebsockerClient, header : any) : void { + const task = this.getTaskWithClient(client); + if (task == null) { + return + } + let result : OnSocketOpenCallbackResult = { + header: header + } + + const openCallback = this.openCallback; + if (this.socketTasks.length > 0 && task === this.socketTasks[0] && openCallback != null) { + openCallback(result); + } + + const simpleTask = task as SimpleSocketTask; + simpleTask.dispatchOpen(result); + } + + onMessage(client : WebsockerClient, data : string) : void { + const task = this.getTaskWithClient(client); + if (task == null) { + return + } + let result : OnSocketMessageCallbackResult = { + data: data + } + + const messageCallback = this.messageCallback; + if (this.socketTasks.length > 0 && task === this.socketTasks[0] && messageCallback != null) { + messageCallback(result); + } + + const simpleTask = task as SimpleSocketTask; + simpleTask.dispatchMessage(result); + } + + onClose(client : WebsockerClient, code : number, reason : string) : void { + const task = this.getTaskWithClient(client); + if (task == null) { + return + } + let result : OnSocketCloseCallbackResult = { + code: code, + reason: reason + } + + const closeCallback = this.closeCallback; + if (this.socketTasks.length > 0 && task === this.socketTasks[0] && closeCallback != null) { + closeCallback(result); + } + + const index = this.socketTasks.indexOf(task); + this.socketTasks.splice(index, 1) + + const simpleTask = task as SimpleSocketTask; + simpleTask.dispatchClose(result); + + this.taskMap.delete(client); + } + + onError(client : WebsockerClient, msg : string) : void { + const task = this.getTaskWithClient(client); + if (task == null) { + return + } + const errorCallback = this.errorCallback; + if (this.socketTasks.length > 0 && task === this.socketTasks[0] && errorCallback != null) { + let result : OnSocketErrorCallbackResult = { + errMsg: msg + } + errorCallback(result); + } + + const index = this.socketTasks.indexOf(task); + this.socketTasks.splice(index, 1) + + let result : GeneralCallbackResult = { + errMsg: msg + } + + const simpleTask = task as SimpleSocketTask; + simpleTask.dispatchError(result); + + this.taskMap.delete(client); + } +} \ No newline at end of file diff --git a/uni_modules/uni-websocket/utssdk/app-android/websocket/WebsockerClient.uts b/uni_modules/uni-websocket/utssdk/app-android/websocket/WebsockerClient.uts new file mode 100644 index 0000000000000000000000000000000000000000..fa9d99ff639d0eac3035ff0c736178cd00d8afa2 --- /dev/null +++ b/uni_modules/uni-websocket/utssdk/app-android/websocket/WebsockerClient.uts @@ -0,0 +1,299 @@ +import { WebSocketManagerListener } from "./WebSocketManager"; +import { ConnectSocketOptions, SendSocketMessageOptions, CloseSocketOptions, GeneralCallbackResult } from "../../interface"; +import { ConnectSocketFailImpl } from "../../unierror.uts"; +import OkHttpClient from 'okhttp3.OkHttpClient'; +import TimeUnit from 'java.util.concurrent.TimeUnit'; +import ConnectionPool from 'okhttp3.ConnectionPool'; +import Request from 'okhttp3.Request'; +import WebSocketListener from 'okhttp3.WebSocketListener'; +import WebSocket from 'okhttp3.WebSocket'; +import Response from 'okhttp3.Response'; +import Okio from 'okio.Okio'; +import ByteString from 'okio.ByteString'; +import UTSAndroid from 'io.dcloud.uts.UTSAndroid'; +import Base64 from 'android.util.Base64'; +import JSONObject from 'com.alibaba.fastjson.JSONObject'; +import Handler from 'android.os.Handler'; +import Looper from 'android.os.Looper'; + +export class WebsockerClient { + private listener : WebSocketManagerListener | null = null; + private options : ConnectSocketOptions | null = null; + + private static connectPool : ConnectionPool | null = null; + + private websocketDelegate : WebsocketDelegate = new WebsocketDelegate(); + constructor(options : ConnectSocketOptions, listener : WebSocketManagerListener) { + this.options = options; + this.listener = listener; + } + + + public connect() { + const kParam = this.options; + if (kParam != null) { + const httpClient = this.createHttpClient(); + + const request = this.createRequest(kParam); + + if (request == null) { + return + } + + // 临时解决方案 , 连接时延迟100ms , 等到js层注册了onOpen后再连接. + setTimeout(() => { + httpClient.newWebSocket(request, new SimpleWebsocketListener(this.listener!, this.websocketDelegate, this)) + }, 100); + + } + } + + public send(options : SendSocketMessageOptions) { + const success = options.success; + const fail = options.fail; + const complete = options.complete; + if (this.websocketDelegate.websocket == null) { + let result : GeneralCallbackResult = { + errMsg: "sendSocketMessage:fail WebSocket is not connected" + } + fail?.(result); + complete?.(result); + return + } + + try { + this.websocketDelegate.websocket?.send(options.data as string); + let result : GeneralCallbackResult = { + errMsg: "sendSocketMessage:ok" + } + success?.(result); + complete?.(result); + } catch (e : Exception) { + let result : GeneralCallbackResult = { + errMsg: e.message ?? "" + } + fail?.(result); + complete?.(result); + this.listener?.onError(this, e.message ?? ""); + } + } + + public close(options : CloseSocketOptions) { + const success = options.success; + const fail = options.fail; + const complete = options.complete; + if (this.websocketDelegate.websocket == null) { + let result : GeneralCallbackResult = { + errMsg: "closeSocket:fail WebSocket is not connected" + } + fail?.(result); + complete?.(result); + return + } + try { + var code : Int = 1000; + if (options.code != null) { + code = options.code as Int + } + this.websocketDelegate.websocket?.close(code, options.reason ?? ""); + let result : GeneralCallbackResult = { + errMsg: "closeSocket:ok" + } + success?.(result); + complete?.(result); + } catch (e : Exception) { + let result : GeneralCallbackResult = { + errMsg: e.message ?? "" + } + fail?.(result); + complete?.(result); + this.listener?.onError(this, e.message ?? ""); + } + } + + private createHttpClient() : OkHttpClient { + let clientBuilder = OkHttpClient.Builder(); + clientBuilder.readTimeout(24, TimeUnit.HOURS); + clientBuilder.writeTimeout(24, TimeUnit.HOURS); + + + if (WebsockerClient.connectPool == null) { + WebsockerClient.connectPool = new ConnectionPool(); + } + clientBuilder.connectionPool(WebsockerClient.connectPool); + + return clientBuilder.build(); + } + + private createRequest(options : ConnectSocketOptions) : Request | null { + let requestBilder = new Request.Builder(); + try { + requestBilder.url(options.url); + } catch (e : Exception) { + let option = new ConnectSocketFailImpl(600009); + const listener = options.fail; + if (listener != null) { + listener(option); + } + return null; + } + + + const protocols = options.protocols; + if (protocols != null) { + let protocolsStr = protocols.join(","); + requestBilder.addHeader("Sec-WebSocket-Protocol", protocolsStr) + } + + const header = options.header; + let hasOrigin = false; + + if (header != null) { + let map = header.toMap(); + if (map != null) { + for (key in map.keys) { + if (key.equals("Origin", true)) { + hasOrigin = true; + } + requestBilder.addHeader(key, "" + map[key]); + } + } + } + + if (!hasOrigin) { + requestBilder.addHeader("Origin", "http://localhost"); // 测试时,有服务器检测Origin导致403,∴加上该句 + } + + return requestBilder.build(); + } + + +} + +class WebsocketDelegate { + websocket : WebSocket | null = null; +} + +class RunnableTask extends Runnable { + private callback : () => void | null; + private looper : Looper | null = null; + constructor(looper : Looper | null, callback : () => void) { + super(); + this.looper = looper; + this.callback = callback + } + + override run() { + this.callback?.() + } + + public execute() { + if (this.looper == null) { + this.run(); + } else { + new Handler(this.looper!!).post(this); + } + } +} + + + + +class SimpleWebsocketListener extends WebSocketListener { + private listener : WebSocketManagerListener | null = null; + private websocketDelegate : WebsocketDelegate | null = null; + private client : WebsockerClient | null = null; + private looper : Looper | null = null; + constructor(listener : WebSocketManagerListener, delegate : WebsocketDelegate, client : WebsockerClient) { + super(); + this.listener = listener; + this.websocketDelegate = delegate; + this.client = client; + this.looper = Looper.myLooper(); + } + + override onOpen(webSocket : WebSocket, response : Response) : void { + new RunnableTask(this.looper, () => { + const delegate = this.websocketDelegate; + if (delegate != null) { + delegate.websocket = webSocket; + } + const client = this.client; + if (client != null) { + const wsHeaders = response.headers(); + const headers = new Map(); + for (key in wsHeaders.names()) { + headers.set(key, wsHeaders.values(key).toString()) + } + this.listener?.onOpen(client, headers); + } + }).execute(); + } + + override onMessage(webSocket : WebSocket, bytes : ByteString) : void { + new RunnableTask(this.looper, () => { + let sources = bytes.toByteArray(); + let base64Str = Base64.encodeToString(sources, Base64.NO_WRAP); + let object = new JSONObject(); + object.put("@type", "binary"); + object.put("base64", base64Str); + const client = this.client; + if (client != null) { + this.listener?.onMessage(client, object.toJSONString()); + } + }).execute(); + } + override onMessage(webSocket : WebSocket, text : string) : void { + new RunnableTask(this.looper, () => { + const client = this.client; + if (client != null) { + this.listener?.onMessage(client, text); + } + }).execute(); + } + override onClosing(webSocket : WebSocket, code : Int, reason : string) : void { + new RunnableTask(this.looper, () => { + const delegate = this.websocketDelegate; + if (delegate != null) { + delegate.websocket = null; + } + const client = this.client; + if (client != null) { + this.listener?.onClose(client, code, reason); + } + }).execute(); + + } + + override onClosed(webSocket : WebSocket, code : Int, reason : string) : void { + new RunnableTask(this.looper, () => { + const delegate = this.websocketDelegate; + if (delegate != null) { + delegate.websocket = null; + } + const client = this.client; + if (client != null) { + this.listener?.onClose(client, code, reason); + } + }).execute(); + + } + + override onFailure(webSocket : WebSocket, t : Throwable, response : Response | null) : void { + new RunnableTask(this.looper, () => { + const delegate = this.websocketDelegate; + if (delegate != null) { + delegate.websocket = null; + } + const client = this.client; + if (client != null) { + if (t.javaClass.simpleName == "EOFException") { + this.listener?.onClose(client, 1000, "CLOSE_NORMAL"); + } else { + this.listener?.onError(client, t.message ?? ""); + } + } + }).execute(); + } + +} \ No newline at end of file diff --git a/uni_modules/uni-websocket/utssdk/app-ios/index.uts b/uni_modules/uni-websocket/utssdk/app-ios/index.uts new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/uni_modules/uni-websocket/utssdk/interface.type.uts b/uni_modules/uni-websocket/utssdk/interface.type.uts new file mode 100644 index 0000000000000000000000000000000000000000..3a947bdf9a5cf1a1b25878a0ff2f6ce22e24b190 --- /dev/null +++ b/uni_modules/uni-websocket/utssdk/interface.type.uts @@ -0,0 +1,38 @@ + +export type SocketDataOptions = + /** + * @description 字符串 + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "x", + * "uniVer": "√", + * "unixVer": "x" + * }, + * "ios": { + * "osVer": "x", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + */ + String + /** + * @description ArrayBuffer + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "x", + * "uniVer": "√", + * "unixVer": "x" + * }, + * "ios": { + * "osVer": "x", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + */ + | ArrayBuffer; \ No newline at end of file diff --git a/uni_modules/uni-websocket/utssdk/interface.uts b/uni_modules/uni-websocket/utssdk/interface.uts new file mode 100644 index 0000000000000000000000000000000000000000..b6e27a6de4cdb89d03042d4a0eef7fd801637e79 --- /dev/null +++ b/uni_modules/uni-websocket/utssdk/interface.uts @@ -0,0 +1,529 @@ +export interface Uni { + /** + * ConnectSocket() + * @description + * 创建一个 WebSocket 连接。 + * @param {ConnectSocketOptions} options + * @return {SocketTask} + * @tutorial https://uniapp.dcloud.net.cn/api/request/websocket.html#connectsocket + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + uni.connectSocket({ + url: "ws://192.168.12.106:8080/ws", + complete: (e) => { + console.log("socket :", e); + } + }); + ``` + */ + connectSocket(options: ConnectSocketOptions): SocketTask; + /** + * OnSocketOpen() + * @description + * 监听WebSocket连接打开事件。 + * @param {OnSocketOpenCallback} options + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/websocket.html#onsocketopen + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + uni.onSocketOpen(function (res) { + console.log('WebSocket连接已打开!'); + }); + ``` + */ + onSocketOpen(options: OnSocketOpenCallback): void; + + /** + * OnSocketError() + * @description + * 下载文件资源到本地,客户端直接发起一个 HTTP GET 请求,返回文件的本地临时路径。 + * @param {OnSocketErrorCallback} callback + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/websocket.html#onsocketerror + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + uni.onSocketError(function (res) { + console.log('WebSocket连接打开失败,请检查!'); + }); + ``` + */ + onSocketError(callback: OnSocketErrorCallback): void; + + /** + * SendSocketMessage() + * @description + * 通过 WebSocket 连接发送数据,需要先 uni.connectSocket,并在 uni.onSocketOpen 回调之后才能发送。 + * @param {SendSocketMessageOptions} options + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/websocket.html#sendsocketmessage + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + uni.sendSocketMessage({ + data: msg + }); + ``` + */ + sendSocketMessage(options: SendSocketMessageOptions): void; + /** + * OnSocketMessage() + * @description + * 监听WebSocket接受到服务器的消息事件。 + * @param {OnSocketMessageCallback} callback + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/websocket.html#onsocketmessage + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + uni.onSocketMessage(function (res) { + console.log('收到服务器内容:' + res.data); + }); + ``` + */ + onSocketMessage(callback: OnSocketMessageCallback): void; + /** + * CloseSocket() + * @description + * 关闭 WebSocket 连接。 + * @param {CloseSocketOptions} options + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/websocket.html#closesocket + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + uni.closeSocket(); + ``` + */ + closeSocket(options: CloseSocketOptions): void; + /** + * OnSocketClose() + * @description + * 监听WebSocket关闭。 + * @param {OnSocketCloseCallback} callback + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/websocket.html#onsocketclose + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + uni.onSocketClose(function (res) { + console.log('WebSocket 已关闭!'); + }); + ``` + */ + onSocketClose(callback: OnSocketCloseCallback): void; +} + +export type ConnectSocket = (options: ConnectSocketOptions) => SocketTask; +export type ConnectSocketSuccess = { + errMsg: string +}; +type ConnectSocketSuccessCallback = (result: ConnectSocketSuccess) => void; +/** + * 错误码 + * - 600009 URL格式不合法 + */ +export type ConnectSocketErrorCode = 600009; + +/** + * 连接调用失败的错误回调参数 + */ +export interface ConnectSocketFail extends IUniError { + errCode: ConnectSocketErrorCode; +}; + +type ConnectSocketFailCallback = (result: ConnectSocketFail) => void; +type ConnectSocketComplete = any; +type ConnectSocketCompleteCallback = (result: ConnectSocketComplete) => void; +export type ConnectSocketOptions = { + /** + * 开发者服务器接口地址,必须是 wss 协议,且域名必须是后台配置的合法域名 + */ + url: string, + /** + * HTTP 请求 Header,header 中不能设置 Referer + * @defaultValue null + */ + header: UTSJSONObject | null, + /** + * 子协议数组 + * @defaultValue null + */ + protocols: (string[]) | null, + /** + * 接口调用成功的回调函数 + * @defaultValue null + */ + success?: ConnectSocketSuccessCallback | null, + /** + * 接口调用失败的回调函数 + * @defaultValue null + */ + fail?: ConnectSocketFailCallback | null, + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + * @defaultValue null + */ + complete?: ConnectSocketCompleteCallback | null +}; +export type GeneralCallbackResult = { + /** + * 错误信息 + */ + errMsg: string +}; +export type SendSocketMessageOptions = { + /** + * 需要发送的内容 + * @type string | ArrayBuffer + * @type {SocketDataOptions} + */ + data: any, + /** + * 接口调用成功的回调函数 + * @defaultValue null + */ + success?: ((result: GeneralCallbackResult) => void) | null, + /** + * 接口调用失败的回调函数 + * @defaultValue null + */ + fail?: ((result: GeneralCallbackResult) => void) | null, + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + * @defaultValue null + */ + complete?: ((result: GeneralCallbackResult) => void) | null +}; +export type CloseSocketOptions = { + /** + * 一个数字值表示关闭连接的状态号,表示连接被关闭的原因。如果这个参数没有被指定,默认的取值是1000 (表示正常连接关闭) + * @defaultValue 1000 + */ + code?: number | null, + /** + * 一个可读的字符串,表示连接被关闭的原因。这个字符串必须是不长于123字节的UTF-8 文本(不是字符) + * @defaultValue "" + */ + reason?: string | null, + /** + * 接口调用成功的回调函数 + * @defaultValue null + */ + success?: ((result: GeneralCallbackResult) => void) | null, + /** + * 接口调用失败的回调函数 + * @defaultValue null + */ + fail?: ((result: GeneralCallbackResult) => void) | null, + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + * @defaultValue null + */ + complete?: ((result: GeneralCallbackResult) => void) | null +}; +export type OnSocketOpenCallbackResult = { + /** + * 连接成功的 HTTP 响应 Header + */ + header: any +}; +export type OnSocketMessageCallbackResult = { + /** + * 服务器返回的消息 + * @type {SocketDataOptions} + */ + data: any +}; +export interface SocketTask { + /** + * send() + * @description + * 通过 WebSocket 连接发送数据 + * @param {SendSocketMessageOptions} options + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/socket-task.html#sockettask-send + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + task.send({data:"halo"}); + ``` + */ + send(options: SendSocketMessageOptions): void; + /** + * close() + * @description + * 关闭 WebSocket 连接 + * @param {CloseSocketOptions} options + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/socket-task.html#sockettask-close + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + task.close(); + ``` + */ + close(options: CloseSocketOptions): void; + /** + * onOpen() + * @description + * 监听 WebSocket 连接打开事件 + * @param {OnSocketOpenCallbackResult} options + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/socket-task.html#sockettask-onopen + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + task.onOpen((res) => {}) + ``` + */ + onOpen(callback: (result: OnSocketOpenCallbackResult) => void): void; + /** + * onClose() + * @description + * 监听 WebSocket 连接关闭事件 + * @param {(result : any) => void} callback + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/socket-task.html#sockettask-onclose + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + task.onClose((res) => { + }) + ``` + */ + onClose(callback: (result: any) => void): void; + /** + * onError() + * @description + * 监听 WebSocket 错误 + * @param {(result : GeneralCallbackResult) => void} callback + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/socket-task.html#sockettask-onerror + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + task.onError((res) => { + }) + ``` + */ + onError(callback: (result: GeneralCallbackResult) => void): void; + /** + * onMessage() + * @description + * 监听 WebSocket 接受到服务器的消息事件 + * @param {(result : OnSocketMessageCallbackResult) => void} callback + * @return {void} + * @tutorial https://uniapp.dcloud.net.cn/api/request/socket-task.html#sockettask-onmessage + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4", + * "uniVer": "√", + * "unixVer": "3.9+" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "√", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + task.onMessage((res) => { + }) + ``` + */ + onMessage(callback: (result: OnSocketMessageCallbackResult) => void): void; +}; +export type OnSocketOpenCallback = (result: OnSocketOpenCallbackResult) => void; + +export type OnSocketOpen = (options: OnSocketOpenCallback) => void; +export type OnSocketErrorCallbackResult = { + /** + * 错误信息 + */ + errMsg: string +}; +export type OnSocketErrorCallback = (result: OnSocketErrorCallbackResult) => void; + +export type OnSocketError = (callback: OnSocketErrorCallback) => void; + +export type SendSocketMessage = (options: SendSocketMessageOptions) => void; +export type OnSocketMessageCallback = (result: OnSocketMessageCallbackResult) => void; + +export type OnSocketMessage = (callback: OnSocketMessageCallback) => void; + +export type CloseSocket = (options: CloseSocketOptions) => void; +export type OnSocketCloseCallbackResult = { + /** + * 一个数字值表示关闭连接的状态号,表示连接被关闭的原因。 + */ + code: number, + /** + * 一个可读的字符串,表示连接被关闭的原因。 + */ + reason: string +}; +export type OnSocketCloseCallback = (result: OnSocketCloseCallbackResult) => void; + +export type OnSocketClose = (callback: OnSocketCloseCallback) => void; diff --git a/uni_modules/uni-websocket/utssdk/unierror.uts b/uni_modules/uni-websocket/utssdk/unierror.uts new file mode 100644 index 0000000000000000000000000000000000000000..9d9177f86b8c329d5441f4fb3ca6b0f247b83bd0 --- /dev/null +++ b/uni_modules/uni-websocket/utssdk/unierror.uts @@ -0,0 +1,26 @@ +import {ConnectSocketFail ,ConnectSocketErrorCode} from "./interface.uts" + +/** + * 错误主题 + */ +export const UniErrorSubject = 'uni-websocket'; +/** + * 错误码 + * @UniError + */ +export const UniErrors : Map = new Map([ + /** + * URL 格式不合法 + */ + [600009, 'invalid URL'], +]); + + +export class ConnectSocketFailImpl extends UniError implements ConnectSocketFail { + constructor(errCode : ConnectSocketErrorCode) { + super(); + this.errSubject = UniErrorSubject; + this.errCode = errCode; + this.errMsg = UniErrors[errCode] ?? "" + } +} \ No newline at end of file