first commit

This commit is contained in:
ytc1012
2025-11-18 18:38:53 +08:00
commit bea9db4488
1582 changed files with 335346 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
export type NetData = (string | ArrayBufferLike | Blob | ArrayBufferView);
export class RequestObject {
public json: any = null; // 请求的json
public rspName: string = ""; // 接口名
public autoReconnect: number = 0; // -1 永久重连0不自动重连其他正整数为自动重试次数
public seq:number = 0; // 消息的序号
public sended:boolean = false; // 是否发送
public otherData:any = {};
public startTime:number = 0
public destroy():void{
this.json = null;
this.rspName = "";
this.autoReconnect = 0;
this.seq = 0;
this.sended = false;
this.otherData = {};
this.startTime = 0;
}
}
// Socket接口
export interface ISocket {
onConnected: (event) => void; // 连接回调
onMessage: (msg: NetData) => void; // 消息回调
onJsonMessage: (msg: NetData) => void; // 消息回调
onError: (event) => void; // 错误回调
onClosed: (event) => void; // 关闭回调
connect(options: any); // 连接接口
send(buffer: NetData); // 数据发送接口
close(code?: number, reason?: string); // 关闭接口
}
// 请求对象
export class NetEvent {
public static ServerTimeOut:string = "ServerTimeOut";
public static ServerConnected:string = "ServerConnected";
public static ServerHandShake:string = "ServerHandShake";
public static ServerCheckLogin:string = "ServerCheckLogin";
public static ServerRequesting:string = "ServerRequesting";
public static ServerRequestSucess:string = "ServerRequestSucess";
}

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "e98e24b5-8b3c-4d33-b19e-e255ec74757f",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,40 @@
import { _decorator } from 'cc';
import { NetNode, NetConnectOptions } from "./NetNode";
export class NetManager {
private static _instance: NetManager = null;
protected _netNode: NetNode = null;
public static getInstance(): NetManager {
if (this._instance == null) {
this._instance = new NetManager();
}
return this._instance;
}
constructor(){
this._netNode = new NetNode();
this._netNode.init();
}
public connect(options: NetConnectOptions) :void{
this._netNode.connect(options);
}
public send(send_data: any, otherData:any = {},force: boolean = false) :void{
if(send_data.seq == undefined){
send_data.seq = 0;
}
this._netNode.send(send_data,otherData,force);
}
public close(code?: number, reason?: string):void {
this._netNode.closeSocket(code, reason);
}
public changeConnect(options: NetConnectOptions):void {
this._netNode.changeConect(options);
}
public tryConnet():void{
this._netNode.tryConnet();
}
}

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "939d67e2-2bd1-4d45-8009-a1a404324dfa",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,405 @@
import { RequestObject, NetEvent } from "./NetInterface";
import { NetTimer } from "./NetTimer";
import { WebSock } from "./WebSock";
import { EventMgr } from "../../utils/EventMgr";
export enum NetTipsType {
Connecting,
ReConnecting,
Requesting,
}
export enum NetNodeState {
Closed, // 已关闭
Connecting, // 连接中
Checking, // 验证中
Working, // 可传输数据
}
export enum NetNodeType {
BaseServer, //主要服务器
ChatServer, //聊天服务器
}
export interface NetConnectOptions {
host?: string, // 地址
port?: number, // 端口
url?: string, // url与地址+端口二选一
autoReconnect?: number, // -1 永久重连0不自动重连其他正整数为自动重试次数
type?:NetNodeType, //服务器类型
}
export class NetNode {
protected _connectOptions: NetConnectOptions = null;
protected _autoReconnect: number = 0;
protected _autoReconnectMax: number = 3;
protected _state: NetNodeState = NetNodeState.Closed; // 节点当前状态
protected _socket: WebSock = null; // Socket对象可能是原生socket、websocket、wx.socket...)
protected _timer:NetTimer = null;
protected _keepAliveTimer: any = null; // 心跳定时器
protected _reconnectTimer: any = null; // 重连定时器
protected _heartTime: number = 10*1000; // 心跳间隔
protected _receiveTime: number = 15*1000; // 多久没收到数据断开
protected _reconnetTimeOut: number = 2*1000; // 重连间隔
protected _requests: RequestObject[] = Array<RequestObject>(); // 请求列表
protected _maxSeqId :number = 1000000;
protected _seqId :number = 1;
protected _invokePool:any = [];
/********************** 网络相关处理 *********************/
public init() {
console.log(`NetNode init socket`);
this._socket = new WebSock();
this.initSocket();
this._timer = new NetTimer();
this.initTimer();
this._invokePool = [];
}
public connect(options: NetConnectOptions): boolean {
console.log(`NetNode connect socket:`,options);
if (this._socket && this._state == NetNodeState.Closed) {
this._state = NetNodeState.Connecting;
if (!this._socket.connect(options)) {
this.updateNetTips(NetTipsType.Connecting, false);
return false;
}
if (this._connectOptions == null) {
options.autoReconnect = options.autoReconnect;
}
this._connectOptions = options;
this.updateNetTips(NetTipsType.Connecting, true);
return true;
}
return false;
}
/**
* 更换线路
* @param options
*/
public changeConect(options: NetConnectOptions){
if(options == this._connectOptions){
return;
}
if(this._state != NetNodeState.Closed){
this.closeSocket();
}
this.connect(options);
}
protected initSocket() {
this._autoReconnect = this._autoReconnectMax;
this._socket.onConnected = (event) => { this.onConnected(event) };
this._socket.onJsonMessage = (msg) => { this.onMessage(msg) };
this._socket.onError = (event) => { this.onError(event) };
this._socket.onClosed = (event) => { this.onClosed(event) };
this._socket.onGetKey = () => { this.onGetKey() };
EventMgr.on(NetEvent.ServerHandShake, this.onChecked, this);
}
protected onGetKey(){
this._state = NetNodeState.Working;
// if(this._connectOptions.type == NetNodeType.BaseServer){
// }else{
// this.onChecked();
// }
EventMgr.emit(NetEvent.ServerCheckLogin);
}
protected initTimer(){
this._timer.init();
EventMgr.on(NetEvent.ServerTimeOut, this.onTimeOut, this);
}
protected onTimeOut(msg:any){
console.log("NetNode onTimeOut!",msg)
//超时删除 请求队列
for (var i = 0; i < this._requests.length;i++) {
let req = this._requests[i];
if(msg.name == req.rspName && msg.seq == req.seq){
this._requests.splice(i, 1);
this.destroyInvoke(req);
i--;
}
}
}
protected updateNetTips(tipsType: NetTipsType, isShow: boolean) {
if (tipsType == NetTipsType.Requesting) {
// EventMgr.emit(NetEvent.ServerRequesting, isShow);
} else if (tipsType == NetTipsType.Connecting) {
} else if (tipsType == NetTipsType.ReConnecting) {
}
}
// 网络连接成功
protected onConnected(event) {
console.log("NetNode onConnected!")
this._autoReconnect = this._autoReconnectMax;
this.clearTimer();
// 启动心跳
this.resetHearbeatTimer();
// EventMgr.emit(NetEvent.ServerConnected);
}
// 连接验证成功,进入工作状态
protected onChecked() {
console.log("NetNode onChecked!")
// 关闭连接或重连中的状态显示
this.updateNetTips(NetTipsType.Connecting, false);
this.updateNetTips(NetTipsType.ReConnecting, false);
if (this._requests.length > 0) {
for (var i = 0; i < this._requests.length;i++) {
let req = this._requests[i];
if(req.sended == false){
this.socketSend(req);
}
}
// 如果还有等待返回的请求,启动网络请求层
}
}
// 接收到一个完整的消息包
protected onMessage(msg): void {
// console.log(`NetNode onMessage msg ` ,msg);
if(msg){
// 接受到数据,重新定时收数据计时器
//推送
if(msg.seq == 0){
EventMgr.emit(msg.name, msg);
// console.log("all_push:",msg.name, msg);
}else{
this.cannelMsgTimer(msg);
// console.log("this._requests.length:",this._requests.length)
for (var i = 0; i < this._requests.length;i++) {
let req = this._requests[i];
if(msg.name == req.rspName && msg.seq == req.seq && req.sended == true){
this._requests.splice(i, 1);
i--;
// console.log("返回:",msg.name,"耗时:",new Date().getTime() - req.startTime)
EventMgr.emit(msg.name, msg , req.otherData);
this.destroyInvoke(req);
EventMgr.emit(NetEvent.ServerRequestSucess,msg);
}
}
}
}
}
protected onError(event) {
console.log("onError:",event);
//出错后清空定时器 那后断开服务 尝试链接
this.clearTimer();
this.restReq();
this.tryConnet();
}
protected onClosed(event) {
console.log("onClosed:",event);
//出错后
this.clearTimer();
this.tryConnet();
}
protected restReq(){
for (var i = 0; i < this._requests.length;i++) {
let req = this._requests[i];
req.sended = false;
}
}
/**
* 重连
*/
public tryConnet(){
console.log("tryConnet",this._autoReconnect)
if (this.isAutoReconnect()) {
this.updateNetTips(NetTipsType.ReConnecting, true);
this._socket.close();
this._state = NetNodeState.Closed;
this._reconnectTimer = setTimeout(() => {
console.log("NetNode tryConnet!")
this.connect(this._connectOptions);
if (this._autoReconnect > 0) {
this._autoReconnect -= 1;
}
}, this._reconnetTimeOut);
} else {
this._state = NetNodeState.Closed;
}
}
// 只是关闭Socket套接字仍然重用缓存与当前状态
public closeSocket(code?: number, reason?: string) {
this.clearTimer();
this._requests.length = 0;
this._seqId = 1;
this._autoReconnect = 0;
if (this._socket) {
this._socket.close(code, reason);
} else {
this._state = NetNodeState.Closed;
}
}
public send(send_data:any,otherData:any,force: boolean = false):void{
var data = this.createInvoke();//new RequestObject();
data.json = send_data;
data.rspName = send_data.name;
data.otherData = otherData;
this.sendPack(data,force);
}
// 发起请求,如果当前处于重连中,进入缓存列表等待重连完成后发送
public sendPack(obj: RequestObject, force: boolean = false): boolean {
if (this._state == NetNodeState.Working || force) {
this.socketSend(obj);
this._requests.push(obj);
}
else if (this._state == NetNodeState.Checking ||
this._state == NetNodeState.Connecting) {
this._requests.push(obj);
}
else if(this._state == NetNodeState.Closed){
this.connect(this._connectOptions);
this._requests.push(obj);
}
else {
console.error("NetNode request error! current state is " + this._state);
}
return false;
}
/**
* 打包发送
* @param obj
*/
public socketSend(obj:RequestObject){
obj.seq = obj.json.seq = this._seqId;
obj.startTime = new Date().getTime()
this._socket.packAndSend(obj.json);
this._seqId+=1;
obj.sended = true;
this._timer.schedule(obj.json,this._receiveTime);
}
/**
* 心跳
*/
public getHearbeat(){
var obj = this.createInvoke();//new RequestObject();
obj.json = {name:"heartbeat",msg:{ctime:new Date().getTime()},seq:0};
obj.rspName = "heartbeat";
obj.seq = 0;
return obj;
}
/********************** 心跳、超时相关处理 *********************/
protected cannelMsgTimer(data:any = null) {
this._timer.cancel(data);
}
protected resetHearbeatTimer() {
if (this._keepAliveTimer !== null) {
clearInterval(this._keepAliveTimer);
}
this._keepAliveTimer = setInterval(() => {
this.sendPack(this.getHearbeat());
}, this._heartTime);
}
protected clearTimer() {
if (this._keepAliveTimer !== null) {
clearTimeout(this._keepAliveTimer);
}
if (this._reconnectTimer !== null) {
clearTimeout(this._reconnectTimer);
}
this._timer.destroy();
}
public isAutoReconnect() {
return this._autoReconnect != 0;
}
public rejectReconnect() {
this._autoReconnect = 0;
this.clearTimer();
}
protected createInvoke():RequestObject{
// console.log("createInvoke_invokePool :",this._invokePool.length)
if (this._invokePool.length > 0) {
return this._invokePool.shift();
}
return new RequestObject();
}
protected destroyInvoke(invoke:RequestObject):void {
invoke.destroy();
this._invokePool.push(invoke);
// console.log("destroyInvoke_invokePool :",this._invokePool.length)
}
}

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "2347520e-e635-4077-82bf-f16e1d41505a",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,88 @@
import { _decorator } from 'cc';
import { NetEvent } from "./NetInterface";
import { EventMgr } from '../../utils/EventMgr';
export class NetTimerData {
public name:string = "";
public seq:number = 0;
public timeId:number = 0;
}
export class NetTimer {
private _tokens:any = null;
private _tokenId:number = 0;
public init(){
this._tokens = new Map();
this._tokenId = 0;
}
public schedule(data:any,delay:number = 0):void{
var self = this;
var token = this._tokenId++;
var id = setTimeout(function() { self.handleTimeout(token); }, delay);
var timerData = new NetTimerData();
timerData.name = data.name;
timerData.seq = data.seq;
timerData.timeId = id;
// console.log("NetTimer token size:",this._tokens.size,token)
this._tokens.set(token,timerData);
}
private handleTimeout(id:number = 0):void{
var data = this._tokens.get(id);
if(data){
EventMgr.emit(NetEvent.ServerTimeOut, data);
this._tokens.delete(id);
}
}
public cancel(data:any):void{
if(data == null){
return
}
var id = -1;
if(typeof(data)=='object'){
id = this.getKey(data);
}else{
id = data;
}
// console.log("NetTimer token id:",id)
if(id >= 0){
this._tokens.delete(id);
clearTimeout(id);
// console.log("NetTimer token cancel:",this._tokens.size)
}
}
private getKey(data:any):number{
var return_key = -1;
this._tokens.forEach((value , key) =>{
if(value.name == data.name && value.seq == data.seq){
return_key = key;
}
});
return return_key;
}
public destroy():void{
var self = this;
this._tokens.forEach(function(key, value){
self.cancel(key);
});
this._tokens.clear();
}
}

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "ac6aa200-3f4b-408d-a5a5-86759a7df57d",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,198 @@
import { ISocket } from "./NetInterface";
import * as crypto from "../../libs/crypto/crypto"
import * as gzip from "../../libs/gzip/gzip";
import { convert } from "../../libs/convert";
export class WebSock implements ISocket {
private _ws: WebSocket = null; // websocket对象
private _key:String = "";
onConnected(event):void{
console.log("websocket onConnected:",event);
}
onJsonMessage(data:any){
}
onGetKey(){
}
onMessage(msg):void{
// console.log("websocket onMessage0:",msg)
var ab = msg
var view = new Uint8Array(ab)
var undata = gzip.unzip(view)
var c = new convert()
msg = c.byteToString(undata)
// console.log("websocket onMessage1:",msg)
//第一次
if(this._key == ""){
try {
var hand_data = JSON.parse(msg);
console.log("hand_data:",hand_data)
if(hand_data.name == "handshake"){
this._key = hand_data.msg.key;
this.onGetKey();
return;
}
} catch (error) {
console.log("handshake parse error:",error)
}
}
// console.log("websocket onMessage2:",msg)
var decrypted = null;
try {
decrypted = this.getAnddecrypt(msg);
} catch (error) {
console.log("message ecrypt error:",error)
}
if(decrypted == ""){
this._key = "";
}
// console.log("onMessage decrypted :",decrypted);
if(decrypted){
var json = JSON.parse(decrypted);
this.onJsonMessage(json);
}
}
onError(event):void{
console.log("websocket onError:",event)
}
onClosed(event):void{
console.log("websocket onClosed:",event)
}
connect(options: any) {
if (this._ws) {
if (this._ws.readyState === WebSocket.CONNECTING) {
console.log("websocket connecting, wait for a moment...")
return false;
}
}
let url = null;
if(options.url) {
url = options.url;
} else {
let ip = options.ip;
let port = options.port;
let protocol = options.protocol;
url = `${protocol}://${ip}:${port}`;
}
console.log()
this._ws = new WebSocket(url);
this._ws.binaryType = options.binaryType ? options.binaryType : "arraybuffer";
this._ws.onmessage = (event) => {
this.onMessage(event.data);
};
this._ws.onopen = this.onConnected;
this._ws.onerror = this.onError;
this._ws.onclose = this.onClosed;
return true;
}
send(buffer: any) {
if (this._ws.readyState == WebSocket.OPEN)
{
this._ws.send(buffer);
return true;
}
return false;
}
close(code?: number, reason?: string) {
this._key = "";
this._ws.close(code, reason);
}
/**
* json 加密打包
* @param send_data
*/
public packAndSend(send_data:any){
//console.log("WebSocke packAndSend:",send_data)
var encrypt = this._key == ""?send_data:this.encrypt(send_data);
//console.log("encrypt:",encrypt);
var data = gzip.zip(encrypt, {level:9});
var c = new convert()
var undata = gzip.unzip(data)
//var msg = c.byteToString(undata)
//console.log("unzip:", msg);
var buffer = new ArrayBuffer(data.length);
var i8arr = new Int8Array(buffer);
for(var i = 0; i < i8arr.length; i++){
i8arr[i]= data[i];
}
// console.log("i8arr:",i8arr)
this.send(i8arr)
}
/**
* 解密
* @param msg
*/
public getAnddecrypt(get_msg:any){
var decrypt = this._key == ""?get_msg:this.decrypt(get_msg);
// console.log("decrypt:",decrypt);
return decrypt
}
encrypt(data:any) {
var key = crypto.enc.Utf8.parse(this._key);
var iv = crypto.enc.Utf8.parse(this._key);
if(typeof(data)=='object'){
data = JSON.stringify(data);
}
let srcs = crypto.enc.Utf8.parse(data);
let encrypted = crypto.AES.encrypt(srcs, key, { iv: iv, mode: crypto.mode.CBC, padding: crypto.pad.ZeroPadding });
return encrypted.ciphertext.toString()
}
decrypt(message:string) {
var key = crypto.enc.Utf8.parse(this._key);
var iv = crypto.enc.Utf8.parse(this._key);
let encryptedHexStr = crypto.enc.Hex.parse(message);
let srcs = crypto.Base64.stringify(encryptedHexStr);
let decrypt = crypto.AES.decrypt(srcs, key, { iv: iv, mode: crypto.mode.CBC, padding: crypto.pad.ZeroPadding });
// console.log("decrypt:", decrypt);
let decryptedStr = decrypt.toString(crypto.enc.Utf8);
// console.log("decryptedStr 1111:", typeof(decryptedStr), decryptedStr, decryptedStr.length);
var str = decryptedStr.replaceAll("\u0000", "")
// console.log("decryptedStr 2222:", typeof(str), str, str.length);
return str;
}
}

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "bd622a27-7676-4b74-bf36-1a4c3eb61cbb",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}