diff --git a/CHANGELOG.md b/CHANGELOG.md index a82df92..e2adec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,4 +41,8 @@ ## 1.1.4 事件模块拆分 - 拆分事件模块,使用 `npm install kunpocc-event` 安装 - * 仓库地址: https://github.com/Gongxh0901/kunpocc-event \ No newline at end of file + * 仓库地址: https://github.com/Gongxh0901/kunpocc-event + +## 1.1.5 网络模块拆分 +- 拆分网络模块,使用 `npm install kunpocc-net` 安装 + * 仓库地址: https://github.com/Gongxh0901/kunpocc-net \ No newline at end of file diff --git a/README.md b/README.md index ee5b16e..2bfb274 100644 --- a/README.md +++ b/README.md @@ -37,25 +37,23 @@ npm set registry https://npm.aliyun.com # 目录 1. [使用教程 (新手必看)](./docs/Noviciate.md) - 2. [项目配置](./docs/Basic.md) 3. [UI模块](./docs/UI.md) -4. [ec模块](https://github.com/Gongxh0901/kunpo-ec) -5. [ecs模块](https://github.com/Gongxh0901/kunpo-esc) -6. [网络模块](./docs/HTTP.md) -7. [四叉树](https://github.com/Gongxh0901/kunpo-quadtree) -8. [行为树](https://github.com/Gongxh0901/kunpocc-behaviortree) -9. [资源管理](https://github.com/Gongxh0901/kunpocc-assets) -10. [条件显示节点 (一般用于UI上的红点)](./docs/Condition.md) -11. [全局事件](./docs/Event.md) -12. [全局计时器](./docs/Timer.md) -13. [平台工具](./docs/Platform.md) -14. [屏幕尺寸](./docs/Screen.md) -15. [小工具](./docs/Tools.md) -16. [时间](./docs/Time.md) -17. [socket网络模块](./docs/Socket.md) -18. [小游戏接口封装](./docs/MiniGame.md) -19. [热更新](./docs/HotUpdate.md) +5. [全局计时器](./docs/Timer.md) +6. [平台工具](./docs/Platform.md) +7. [屏幕尺寸](./docs/Screen.md) +8. [小工具](./docs/Tools.md) +9. [时间](./docs/Time.md) +10. [小游戏接口封装](./docs/MiniGame.md) +11. [热更新](./docs/HotUpdate.md) +12. [条件显示节点 (一般用于UI上的红点)](./docs/Condition.md) +13. [ec模块](https://github.com/Gongxh0901/kunpo-ec) +14. [ecs模块](https://github.com/Gongxh0901/kunpo-esc) +15. [网络模块 http和socket](https://github.com/Gongxh0901/kunpocc-net) +16. [四叉树](https://github.com/Gongxh0901/kunpo-quadtree) +17. [行为树](https://github.com/Gongxh0901/kunpocc-behaviortree) +18. [资源管理](https://github.com/Gongxh0901/kunpocc-assets) +19. [全局事件](https://github.com/Gongxh0901/kunpocc-event) ## 类型支持 该库完全使用 TypeScript 编写,提供完整的类型定义文件。 diff --git a/docs/Event.md b/docs/Event.md deleted file mode 100644 index 4f04b04..0000000 --- a/docs/Event.md +++ /dev/null @@ -1,32 +0,0 @@ -## 全局事件系统 - -### 使用 - -```typescript -import { GlobalEvent } from 'kunpocc'; - -// 添加事件监听 -GlobalEvent.add('eventName', (arg1, arg2) => { - console.log('事件触发:', arg1, arg2); -}, this); - -// 添加一次性事件监听 -GlobalEvent.addOnce('oneTimeEvent', (data) => { - console.log('一次性事件触发:', data); -}, this); - -// 发送事件 -GlobalEvent.send('eventName', 'arg1', 'arg2'); - -// 发送事件到指定目标 -GlobalEvent.sendToTarget('eventName', target, 'arg1', 'arg2'); - -// 移除事件监听 -GlobalEvent.remove('eventName', callback, this); - -// 移除指定目标的所有事件监听 -GlobalEvent.removeByTarget(this); - -// 移除指定事件名和目标的事件监听 -GlobalEvent.removeByNameAndTarget('eventName', this); -``` diff --git a/docs/HTTP.md b/docs/HTTP.md deleted file mode 100644 index faf12b9..0000000 --- a/docs/HTTP.md +++ /dev/null @@ -1,94 +0,0 @@ -## Http模块 - -### 特点 - - 封装 XMLHttpRequest - - 完整的请求响应接口 - - 独立使用简单,一行代码发送一个请求 - - 大型项目,管理简单 - -### 使用 - -```typescript -import { HttpManager, IHttpEvent, HttpResponseType } from 'kunpocc'; - -// 1. 使用回调方式处理响应 -const event: IHttpEvent = { - name: "login", - onComplete: (response) => { - console.log('请求成功:', response.data); - }, - onError: (response) => { - console.log('请求失败:', response.error); - } -}; - -// POST 请求 -HttpManager.post( - "https://api.example.com/login", - { username: "test", password: "123456" }, - "json", // 响应类型:'json' | 'text' | 'arraybuffer' - event, - ["Content-Type", "application/json"], // 请求头 - 5 // 超时时间(秒) -); - -// GET 请求 -HttpManager.get( - "https://api.example.com/users", - { id: 1 }, - "json", - event -); - -// 2. 使用全局事件方式处理响应 -GlobalEvent.add(HttpManager.HttpEvent, (result, response) => { - if (result === "succeed") { - console.log('请求成功:', response.data); - } else { - console.log('请求失败:', response.error); - } -}, this); - -// 发送请求(不传入 event 参数) -HttpManager.post("https://api.example.com/data", { /* data */ }); -``` - -#### *请求方法* -- `post(url, data, responseType?, event?, headers?, timeout?)` -- `get(url, data, responseType?, event?, headers?, timeout?)` -- `put(url, data, responseType?, event?, headers?, timeout?)` -- `head(url, data, responseType?, event?, headers?, timeout?)` - -#### *参数说明* -- `url`: 请求地址 -- `data`: 请求数据 -- `responseType`: 响应类型(可选,默认 'json') - - `'json'`: JSON 格式 - - `'text'`: 文本格式 - - `'arraybuffer'`: 二进制数据 -- `event`: 请求事件回调(可选) -- `headers`: 请求头(可选) -- `timeout`: 超时时间,单位秒(可选,0表示不超时) - -#### *响应处理* -1. 回调方式(通过 IHttpEvent): -```typescript -const event: IHttpEvent = { - name: "自定义名称", - data?: "自定义数据", // 可选 - onComplete: (response) => { - // 成功回调 - }, - onError: (response) => { - // 失败回调 - } -}; -``` - -2. 全局事件方式: -```typescript -GlobalEvent.add(HttpManager.HttpEvent, (result, response) => { - // result: "succeed" | "fail" - // response: IHttpResponse -}, this); -``` diff --git a/docs/Socket.md b/docs/Socket.md deleted file mode 100644 index 9b1be97..0000000 --- a/docs/Socket.md +++ /dev/null @@ -1,45 +0,0 @@ -## socket网络模块 - -* 目的抹平小游戏平台和原生平台的使用差异 - - `各个小游戏平台都是自己封装的socket 和 浏览器标准的websocket在用法上有一定的差异` - - - -#### 使用 - -```typescript -import { Socket } from "kunpocc"; - -// 创建一个连接 -let url = "wss:xxxxxxxx" -let socket = new Socket(url, { binaryType: "arraybuffer" }); - -// 监听连接open事件 -socket.onopen = () => { - log("连接成功"); -} - -// 监听收到服务端的消息 -socket.onmessage = (data: string | ArrayBuffer) => { - log("收到消息", data); -} - -// 监听连接关闭的事件 -socket.onclose = (code: number, reason: string) => { - log("连接关闭", code, reason); - socket = null; -} - -// 发送字符串消息 -socket.send("发送给服务端的消息"); - -// 发送二进制数据 一般都是使用ProtoBuf,具体使用可参考Demo -socket.sendBuffer(buffer); - -// 主动断开连接 -socket.close(3001, "主动断开连接"); -``` - - - diff --git a/image/image-basic-config.png b/image/image-basic-config.png index e5fe3cb..38d4f10 100644 Binary files a/image/image-basic-config.png and b/image/image-basic-config.png differ diff --git a/package.json b/package.json index d43d68a..fc1ce10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kunpocc", - "version": "1.1.4", + "version": "1.1.5", "description": "基于creator3.0+的kunpocc库", "main": "./dist/kunpocc.cjs", "module": "./dist/kunpocc.mjs", @@ -44,7 +44,8 @@ }, "dependencies": { "fairygui-cc": "^1.2.2", - "kunpocc-event": "^0.0.2" + "kunpocc-event": "^0.0.2", + "kunpocc-net": "^0.0.2" }, "devDependencies": { "@cocos/creator-types": "^3.8.0", diff --git a/src/hotupdate/HotUpdate.ts b/src/hotupdate/HotUpdate.ts index 46bc9a3..af2c25c 100644 --- a/src/hotupdate/HotUpdate.ts +++ b/src/hotupdate/HotUpdate.ts @@ -5,8 +5,8 @@ */ import { game, native, sys } from "cc"; +import { ReadNetFile } from "kunpocc-net"; import { ICheckUpdatePromiseResult, IPromiseResult } from "../interface/PromiseResult"; -import { ReadNetFile } from "../net/nettools/ReadNetFile"; import { debug, warn } from "../tool/log"; import { Time } from "../tool/Time"; import { Utils } from "../tool/Utils"; diff --git a/src/kunpocc.ts b/src/kunpocc.ts index 7f95d6e..b78161d 100644 --- a/src/kunpocc.ts +++ b/src/kunpocc.ts @@ -12,19 +12,6 @@ export { MathTool } from "./tool/Math"; export { md5 } from "./tool/MD5"; export { Time } from "./tool/Time"; -/** Http */ -export * from "./net/http/HttpManager"; -export { HttpTask } from "./net/http/HttpTask"; -export { IHttpEvent } from "./net/http/IHttpEvent"; -export { IHttpRequest } from "./net/http/IHttpRequest"; -export { IHttpResponse } from "./net/http/IHttpResponse"; - -/** Socket */ -export { Socket } from "./net/socket/Socket"; - -/** 读取网络文件 */ -export { ReadNetFile } from "./net/nettools/ReadNetFile"; - /** UI */ export { Window } from "./fgui/Window"; export { WindowHeader } from "./fgui/WindowHeader"; diff --git a/src/net/http/HttpManager.ts b/src/net/http/HttpManager.ts deleted file mode 100644 index 20ef654..0000000 --- a/src/net/http/HttpManager.ts +++ /dev/null @@ -1,102 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2024-12-28 - * @Description: 网络请求管理器 - */ - -import { HttpRequest } from "./HttpRequest"; -import { IHttpEvent } from "./IHttpEvent"; -import { IHttpResponse } from "./IHttpResponse"; - -/** http请求方法 */ -export type HttpRequestMethod = "GET" | "POST" | "HEAD" | "PUT" -/** http响应类型 */ -export type HttpResponseType = "text" | "json" | "arraybuffer"; -/** http响应数据类型 */ -export type HttpResponseDataType = string | ArrayBuffer | object; - -export class HttpManager { - public static HttpEvent: string = "event::http"; - - /** - * 发送post请求 - * @param {string} url 请求地址 - * @param {any} data 请求数据 - * @param {HttpResponseType} responseType 响应类型 - * @param {IHttpEvent} netEvent 网络事件 - * @param {any[]} headers 请求头 [key1, value1, key2, value2, ...] 形式 - * @param {number} timeout (单位s) 请求超时时间 默认0 (0表示不超时) - */ - public static post(url: string, data: any, responseType: HttpResponseType = "json", netEvent: IHttpEvent, headers?: any[], timeout: number = 0): HttpRequest { - return this._send("POST", url, data, responseType, netEvent, headers, timeout); - } - - /** - * 发送get请求 - * @param {string} url 请求地址 - * @param {any} data 请求数据 - * @param {HttpResponseType} responseType 响应类型 - * @param {IHttpEvent} netEvent 网络事件 - * @param {any[]} headers 请求头 [key1, value1, key2, value2, ...] 形式 - * @param {number} timeout (单位s) 请求超时时间 默认0 (0表示不超时) - */ - public static get(url: string, data: any, responseType: HttpResponseType = "json", netEvent: IHttpEvent, headers?: any[], timeout: number = 0): HttpRequest { - return this._send("GET", url, data, responseType, netEvent, headers, timeout); - } - - /** - * 发送put请求 - * @param {string} url 请求地址 - * @param {any} data 请求数据 - * @param {HttpResponseType} responseType 响应类型 - * @param {IHttpEvent} netEvent 网络事件 - * @param {any[]} headers 请求头 [key1, value1, key2, value2, ...] 形式 - * @param {number} timeout (单位s) 请求超时时间 默认0 (0表示不超时) - */ - public static put(url: string, data: any, responseType: HttpResponseType = "json", netEvent: IHttpEvent, headers?: any[], timeout: number = 0): HttpRequest { - return this._send("PUT", url, data, responseType, netEvent, headers, timeout); - } - - /** - * 发送head请求 - * @param {string} url 请求地址 - * @param {any} data 请求数据 - * @param {HttpResponseType} responseType 响应类型 - * @param {IHttpEvent} netEvent 网络事件 - * @param {any[]} headers 请求头 [key1, value1, key2, value2, ...] 形式 - * @param {number} timeout (单位s) 请求超时时间 默认0 (0表示不超时) - */ - public static head(url: string, data: any, responseType: HttpResponseType = "json", netEvent: IHttpEvent, headers?: any[], timeout: number = 0): HttpRequest { - return this._send("HEAD", url, data, responseType, netEvent, headers, timeout); - } - - /** - * 发送http请求 - * @param {HttpRequestMethod} method 请求方式 - * @param {string} url 请求地址 - * @param {any} data 请求数据 - * @param {HttpResponseType} responseType 响应类型 - * @param {IHttpEvent} netEvent 网络事件 - * @param {any[]} headers 请求头 [key1, value1, key2, value2, ...] 形式 - * @param {number} timeout (单位s) 请求超时时间 默认0 (0表示不超时) - * @internal - */ - private static _send(method: HttpRequestMethod, url: string, data: any, responseType: HttpResponseType, netEvent: IHttpEvent, headers?: any[], timeout?: number): HttpRequest { - let http = new HttpRequest() - http.setNetCallback((result: "succeed" | "fail", response: IHttpResponse) => { - switch (result) { - case "succeed": - netEvent?.onComplete(response); - break; - case "fail": - netEvent?.onError(response); - break; - } - }); - http.method = method; - http.timeout = timeout; - http.responseType = responseType; - http.send(url, data, headers); - return http; - } -} \ No newline at end of file diff --git a/src/net/http/HttpRequest.ts b/src/net/http/HttpRequest.ts deleted file mode 100644 index 26231d3..0000000 --- a/src/net/http/HttpRequest.ts +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2024-12-28 - * @Description: 网络请求 - */ -import { Platform } from "../../global/Platform"; -import { HttpRequestMethod, HttpResponseDataType, HttpResponseType } from "./HttpManager"; -import { IHttpRequest } from "./IHttpRequest"; -import { IHttpResponse } from "./IHttpResponse"; - -export class HttpRequest implements IHttpRequest, IHttpResponse { - /** 请求方法 */ - public method: HttpRequestMethod; - /** xhr实例 @internal */ - private _xhr: XMLHttpRequest; - /** 请求超时时间 (s) */ - public timeout: number; - /** 响应类型 */ - public responseType: HttpResponseType; - /** 信息 */ - public message: string; - /** 响应数据 */ - public data: HttpResponseDataType; - - /** 网络事件回调 @internal */ - private _callback: (result: "succeed" | "fail", response: IHttpResponse) => void; - - /** - * http相应状态码 - * @readonly - * @type {number} - */ - public get statusCode(): number { - return this._xhr.status; - } - - /** 相应头 */ - public get headers(): any { - return this._xhr.getAllResponseHeaders(); - } - - constructor() { - this._xhr = new XMLHttpRequest(); - } - - public setNetCallback(callback: (result: "succeed" | "fail", response: IHttpResponse) => void): void { - this._callback = callback; - } - - public send(url: string, data: any, headers: any[]): void { - let xhr = this._xhr; - /** 设置请求超时时间 */ - xhr.timeout = this.timeout * 1000; - /** 设置响应类型 */ - xhr.responseType = this.responseType; - xhr.onabort = this._onHttpAbort.bind(this); - xhr.onerror = this._onHttpError.bind(this); - xhr.onload = this._onHttpLoad.bind(this); - xhr.ontimeout = this._onHttpTimeout.bind(this); - xhr.open(this.method, encodeURI(url)); - if (headers) { - for (let i = 0; i < headers.length; i += 2) { - xhr.setRequestHeader(headers[i], headers[i + 1]); - } - } else if (!Platform.isMobile && Platform.isBrowser) { - if (!data || typeof data == "string") { - xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); - } else { - xhr.setRequestHeader("Content-Type", "application/json"); - } - } - xhr.send(data); - } - - /** - * 终止Http请求 - * @param {boolean} [silent=false] 如果为true则不会回调错误信息 - */ - public abort(silent: boolean = false): void { - if (silent) { - this._clear(); - } - this._xhr.abort(); - } - - /** - * 请求中断 - * @internal - */ - private _onHttpAbort(): void { - this.message = "request aborted by user"; - this.onError(); - } - - /** - * 请求错误 - * @internal - */ - private _onHttpError(): void { - this.message = "request error"; - this.onError(); - } - - /** - * @internal - */ - private _onHttpLoad(): void { - const xhr = this._xhr; - const status = xhr.status !== undefined ? xhr.status : 200; - if (status === 200 || status === 204 || status === 0) { - this.onComplete(); - } else { - this.message = 'status:' + xhr.status + 'statusText:' + xhr.statusText + "responseURL:" + xhr.responseURL; - this.onError(); - } - } - - /** - * 请求超时 - * @internal - */ - private _onHttpTimeout(): void { - this.message = "request timeout"; - this.onError(); - } - - /** - * 请求发生错误 - * @internal - */ - private onError(): void { - this._callback?.("fail", this); - this._clear(); - } - - /** - * 请求完成 - * @internal - */ - private onComplete(): void { - try { - if (this.responseType == "json") { - this.data = this._xhr.response; - } else if (this.responseType == "arraybuffer") { - this.data = this._xhr.response; - } else if (this.responseType == "text") { - this.data = this._xhr.responseText; - } - this._callback?.("succeed", this); - this._clear(); - } catch (e) { - console.warn(`http响应数据解析错误,HttpResponseType(${this.responseType})\n url: ${this._xhr.responseURL}\n error: ` + e); - this.onError(); - } - } - - /** - * 清除请求 - * @internal - */ - private _clear(): void { - this._xhr.onabort = null; - this._xhr.onerror = null; - this._xhr.onload = null; - this._xhr.ontimeout = null; - this._callback = null; - } -} \ No newline at end of file diff --git a/src/net/http/HttpTask.ts b/src/net/http/HttpTask.ts deleted file mode 100644 index ca94f30..0000000 --- a/src/net/http/HttpTask.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2024-12-28 - * @Description: 网络任务 - */ - -import { IHttpEvent } from "./IHttpEvent"; -import { IHttpResponse } from "./IHttpResponse"; - -export abstract class HttpTask implements IHttpEvent { - /** 名称 */ - public name: string; - /** 自定义参数 */ - public data?: any; - /** 请求完成 */ - public abstract onComplete(response: IHttpResponse): void; - /** 请求错误 */ - public abstract onError(response: IHttpResponse): void; - /** 请求开始 */ - public abstract start(): void; -} \ No newline at end of file diff --git a/src/net/http/IHttpEvent.ts b/src/net/http/IHttpEvent.ts deleted file mode 100644 index af7948a..0000000 --- a/src/net/http/IHttpEvent.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2024-12-28 - * @Description: 网络事件 - */ - -import { IHttpResponse } from "./IHttpResponse"; - -export interface IHttpEvent { - /** 名称 */ - name?: string; - /** 自定义参数 */ - data?: any; - /** 网络请求成功 */ - onComplete(response: IHttpResponse): void; - /** 网络请求失败 */ - onError(response: IHttpResponse): void; -} \ No newline at end of file diff --git a/src/net/http/IHttpRequest.ts b/src/net/http/IHttpRequest.ts deleted file mode 100644 index 6b4223d..0000000 --- a/src/net/http/IHttpRequest.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2024-12-28 - * @Description: 网络请求接口 - */ - -import { HttpRequestMethod, HttpResponseType } from "./HttpManager"; -export interface IHttpRequest { - /** 请求方法 */ - readonly method: HttpRequestMethod; - /** 请求超时时间 (s) */ - readonly timeout: number; - /** 响应类型 */ - readonly responseType: HttpResponseType; -} \ No newline at end of file diff --git a/src/net/http/IHttpResponse.ts b/src/net/http/IHttpResponse.ts deleted file mode 100644 index f63532e..0000000 --- a/src/net/http/IHttpResponse.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2024-12-28 - * @Description: 网络响应接口 - */ - -import { HttpResponseDataType } from "./HttpManager"; -export interface IHttpResponse { - /** 信息 */ - readonly message: string; - - /** 响应数据 */ - readonly data: HttpResponseDataType; - - /** http状态码 */ - readonly statusCode: number; - - /** 相应头 */ - readonly headers: any; -} \ No newline at end of file diff --git a/src/net/nettools/ReadNetFile.ts b/src/net/nettools/ReadNetFile.ts deleted file mode 100644 index dd83a75..0000000 --- a/src/net/nettools/ReadNetFile.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2025-04-18 - * @Description: 读取网络文件内容 - */ - -import { Time } from "../../tool/Time"; -import { Utils } from "../../tool/Utils"; -import { HttpManager } from "../http/HttpManager"; -import { IHttpResponse } from "../http/IHttpResponse"; - -export class ReadNetFile { - constructor(res: { url: string, timeout: number, responseType: "text" | "json" | "arraybuffer", onComplete: (data: any) => void, onError: (code: number, message: string) => void }) { - // 地址上带时间戳参数 确保每次请求都到服务器上请求最新配置,而不是拿到上次请求的缓存数据 - let url = Utils.addUrlParam(res.url, "timeStamp", `${Time.now()}`); - HttpManager.get(url, null, res.responseType, { - onComplete: (response: IHttpResponse) => { - res.onComplete(response.data); - }, - onError: (response: IHttpResponse) => { - res.onError(response.statusCode, response.message); - } - }, null, res.timeout || 6); - } -} \ No newline at end of file diff --git a/src/net/socket/Socket.ts b/src/net/socket/Socket.ts deleted file mode 100644 index c873ab9..0000000 --- a/src/net/socket/Socket.ts +++ /dev/null @@ -1,320 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2025-03-28 - * @Description: 网络socket - */ - -import { Platform } from "../../global/Platform"; -import { debug, warn } from "../../tool/log"; - -type BinaryType = "blob" | "arraybuffer"; - -interface SocketOptions { - /** - * 给原生平台 和 web 用 - * 一个协议字符串或者一个包含协议字符串的数组。 - * 这些字符串用于指定子协议,这样单个服务器可以实现多个 WebSocket 子协议( - * 例如,你可能希望一台服务器能够根据指定的协议(protocol)处理不同类型的交互。 - * 如果不指定协议字符串,则假定为空字符串。 - */ - protocols?: string[]; - - /** - * 使用 Blob 对象处理二进制数据。这是默认值 - * 使用 ArrayBuffer 对象处理二进制数据 - * @url https://developer.mozilla.org/docs/Web/API/WebSocket/binaryType - */ - binaryType?: BinaryType; - - /** 超时时间 默认3000毫秒 */ - timeout?: number; -} - -export class Socket { - /** - * socket对象 - * @internal - */ - private _socket: WebSocket | WechatMiniprogram.SocketTask | AliyMiniprogram.SocketTask | BytedanceMiniprogram.SocketTask; - - /** - * @param {string} url 要连接的 URL;这应该是 WebSocket 服务器将响应的 URL - * @param {SocketOptions} options 可选参数 针对不同平台的一些特殊参数 详细信息见定义 - */ - constructor(url: string, options?: SocketOptions) { - if (Platform.isWX) { - this._socket = this.createWechatSocket(url, options?.timeout || 3000, options?.protocols); - } else if (Platform.isAlipay) { - this._socket = this.createAliSocket(url, options?.timeout || 3000, options?.protocols); - } else if (Platform.isBytedance) { - this._socket = this.createBytedanceSocket(url, options?.timeout || 3000, options?.protocols); - } else { - this._socket = this.createOtherSocket(url, options?.binaryType, options?.timeout || 3000, options?.protocols); - } - } - - /** - * 微信小游戏创建socket - * @internal - */ - private createWechatSocket(url: string, timeout?: number, protocols?: string[]): WechatMiniprogram.SocketTask { - let socket = wx.connectSocket({ - url, - protocols: protocols, - timeout: timeout, - success: () => { debug("socket success") }, - fail: () => { warn("socket fail") } - }); - socket.onOpen(() => { - this.onopen && this.onopen(); - }); - socket.onMessage((res: { data: string | ArrayBuffer }) => { - this.onmessage && this.onmessage(res.data); - }); - socket.onError((res: { errMsg: string }) => { - // 微信上socket和原生平台以及浏览器不一致 所以这里特殊处理 给他一个默认的错误码 - this.onclose?.(1000, res?.errMsg); - }); - socket.onClose((res: { code: number, reason: string }) => { - this.onclose?.(res.code, res.reason); - }); - return socket; - } - - /** - * 支付宝小游戏创建socket - * @internal - */ - private createAliSocket(url: string, timeout?: number, protocols?: string[]): AliyMiniprogram.SocketTask { - let socket = my.connectSocket({ - url, - protocols: protocols, - multiple: true, - timeout: timeout, - success: () => { debug("socket success") }, - fail: () => { warn("socket fail") } - }); - - socket.onOpen((info: AliyMiniprogram.OnOpenData) => { - this.onopen && this.onopen(); - }); - socket.onMessage((info: AliyMiniprogram.OnMessageData) => { - if (!this.onmessage) { - return; - } - if (info.isBuffer) { - if (my.base64ToArrayBuffer) { - this.onmessage(my.base64ToArrayBuffer(info.data.data)); - } else if (atob) { - this.onmessage(this.base64ToArrayBuffer(info.data.data)); - } else { - this.onmessage(info.data.data); - } - this.onmessage(info.data.data); - } else { - this.onmessage(info.data.data); - } - }); - socket.onError((info: { data: AliyMiniprogram.CallBack.Fail }) => { - this.onclose && this.onclose(info.data.error, info.data.errorMessage); - }); - socket.onClose((info: { code: number, reason: string, data: { code: number, reason: string } }) => { - this.onclose && this.onclose(info.code, info.reason); - }); - return socket; - } - - /** - * 抖音小游戏创建socket - * @internal - */ - private createBytedanceSocket(url: string, timeout?: number, protocols?: string[]): BytedanceMiniprogram.SocketTask { - let socket: BytedanceMiniprogram.SocketTask = tt.connectSocket({ - url, - protocols: protocols, - success: () => { debug("socket success") }, - fail: () => { warn("socket fail") } - }); - - let timer = setTimeout(() => { - socket.close({}); - }, timeout); - - socket.onOpen(() => { - timer && clearTimeout(timer); - timer = null; - this.onopen && this.onopen(); - }); - socket.onMessage((info: { data: string | ArrayBuffer }) => { - this.onmessage && this.onmessage(info.data); - }); - socket.onError((res: { errMsg: string }) => { - timer && clearTimeout(timer); - timer = null; - // 微信上socket和原生平台以及浏览器不一致 所以这里特殊处理 给他一个默认的错误码 - this.onclose?.(1000, res.errMsg); - }); - socket.onClose((res: { code: string, reason: string }) => { - timer && clearTimeout(timer); - timer = null; - this.onclose?.(Number(res.code), res.reason); - }); - return socket; - } - - /** - * 除微信小游戏、支付宝小游戏、抖音小游戏之外的平台创建socket - * @internal - */ - private createOtherSocket(url: string, binaryType: BinaryType, timeout?: number, protocols?: string[]): WebSocket { - let socket = new WebSocket(url, protocols); - if (binaryType) { - socket.binaryType = binaryType; - } - - let timer = setTimeout(() => { - socket.close(); - }, timeout); - - socket.onopen = () => { - timer && clearTimeout(timer); - timer = null; - this.onopen?.(); - } - socket.onmessage = (event: MessageEvent) => { - this.onmessage?.(event.data); - } - socket.onerror = () => { - timer && clearTimeout(timer); - timer = null; - this.onerror?.(); - } - socket.onclose = (event: CloseEvent) => { - timer && clearTimeout(timer); - timer = null; - this.onclose?.(event?.code, event?.reason); - } - return socket; - } - - /** - * 发送文本数据 - * @param data - 文本数据 - */ - public send(data: string): void { - if (Platform.isWX) { - (this._socket as WechatMiniprogram.SocketTask).send({ data: data }); - } else if (Platform.isAlipay) { - (this._socket as AliyMiniprogram.SocketTask).send({ data: data }); - } else if (Platform.isBytedance) { - (this._socket as BytedanceMiniprogram.SocketTask).send({ data: data }); - } else { - (this._socket as WebSocket).send(data); - } - } - - /** - * 发送二进制数据 - * @param data - 二进制数据 - */ - public sendBuffer(data: ArrayBuffer): void { - if (Platform.isWX) { - (this._socket as WechatMiniprogram.SocketTask).send({ data: data }); - } else if (Platform.isAlipay) { - if (my.arrayBufferToBase64) { - (this._socket as AliyMiniprogram.SocketTask).send({ data: my.arrayBufferToBase64(data), isBuffer: true }); - } else if (btoa) { - (this._socket as AliyMiniprogram.SocketTask).send({ data: this.uint8ArrayToBase64(new Uint8Array(data)), isBuffer: true }); - } else { - (this._socket as AliyMiniprogram.SocketTask).send({ data: data }); - } - } else if (Platform.isBytedance) { - (this._socket as BytedanceMiniprogram.SocketTask).send({ data: data }); - } else { - (this._socket as WebSocket).send(data); - } - } - - /** - * 客户端主动断开 - * @param code - 关闭代码: 如果没有传这个参数,默认使用1000, 客户端可使用的数字范围: [3001-3999] - * @param reason - 关闭原因: 一个人类可读的字符串,它解释了连接关闭的原因。这个 UTF-8 编码的字符串不能超过 123 个字节 - */ - public close(code?: number, reason?: string): void { - if (Platform.isWX) { - (this._socket as WechatMiniprogram.SocketTask).close({ code: code, reason: reason }); - } else if (Platform.isAlipay) { - (this._socket as AliyMiniprogram.SocketTask).close({ code: code, reason: reason }); - } else if (Platform.isBytedance) { - (this._socket as BytedanceMiniprogram.SocketTask).close({ code: code, reason: reason }); - } else { - (this._socket as WebSocket).close(code, reason); - } - } - - /** - * 获取socket示例 - * 在微信小游戏、支付宝小游戏、抖音小游戏 返回的是他们平台的socket实例类型 - */ - public socket(): T { - return this._socket as T; - } - - /** - * socket已准备好 open成功 - * 当前连接已经准备好发送和接受数据 - */ - public onopen: () => void; - - /** - * 接收到服务端发送的消息 - * @param data - 消息数据 - */ - public onmessage: (data: string | ArrayBuffer) => void; - - /** - * 监听可能发生的错误,一般用不到 - */ - public onerror: () => void; - - /** - * 关闭连接 - * @param code - 关闭代码 - * @param reason - 关闭原因 - */ - public onclose: (code: number, reason: string) => void; - - /** - * 二进制数据转成Base64 (支付宝用) - * @internal - */ - private uint8ArrayToBase64(u8Array: Uint8Array): string { - let CHUNK_SIZE = 0x8000; // 32768 - let index = 0; - let length = u8Array.length; - let result = ''; - let slice: Uint8Array;; - - // 分段处理,避免`btoa`输入字符串过长 - for (; index < length; index += CHUNK_SIZE) { - slice = u8Array.subarray(index, Math.min(index + CHUNK_SIZE, length)); - // 将Uint8Array转换为字符串并使用btoa进行Base64编码 - result += btoa(String.fromCharCode(...slice)); - } - return result; - } - - /** - * base64转成ArrayBuffer (支付宝用) - * @internal - */ - private base64ToArrayBuffer(base64: string): ArrayBuffer { - let binary_string = atob(base64); - let len = binary_string.length; - let bytes = new Uint8Array(len); - for (var i = 0; i < len; i++) { - bytes[i] = binary_string.charCodeAt(i); - } - return bytes.buffer; - } -}