README
@ailhc/enet
介绍
enet 是一个基于TypeScript的零依赖、跨平台、灵活、高可扩展的网络库
可以轻松集成到任意js/ts项目中
可以根据项目需要进行多层次定制(socket层,协议层,网络通信反馈层)
🧰有用工具和库
protobuf协议处理库@ailhc/enet-pbws
proto文件编译生成静态js文件的工具egf-protobuf
😆Demo
基于CocosCreator2.4.2的聊天室demo(含服务端实现):egf-net-ws
灵感来源
特性
- 跨平台:适用于任意ts/js项目
- 灵活、高可扩展:可以根据项目需要进行多层次定制
- 零依赖
- 强类型:基于TypeScript
- 功能强大:提供完整的基本实现:握手、心跳、重连
- 可靠:完善的单元测试
如何使用?
安装
通过npm安装
npm i @ailhc/enet
本地link npm包
a. clone 项目仓库
git clone https://github.com/AILHC/EasyGameFrameworkOpen
b. 在packages/enet文件夹下
npm link
c. 在项目游戏项目目录下
npm link @ailhc/enet
使用源码
a. clone 项目仓库
git clone https://github.com/AILHC/EasyGameFrameworkOpen
b. 直接复制packages/enet/src下的ts源码到项目文件夹下使用(排除index.ts)
使用
- 基础使用(三步)
//三行代码初始化
import { NetNode } from "@ailhc/enet";
const net = new NetNode<string>();
net.init();
//一行代码连接服务器
net.connect("wss://echo.websocket.org/");
//两行代码发收个消息
net.onPush<string>("Msg", (dpkg) => { console.log(dpkg.data) });
net.notify<string>("Msg", msg);
//一步两步,就这么简单~ 如丝般
进阶使用(不改源码定制属于你的网络层)
定制网络通信的反馈和交互逻辑
每个项目都有自己的网络通信的反馈和交互逻辑和定制需求,而且希望能够通用且能可控。
//两步轻松定制 //实现网络事件处理器接口enet.INetEventHandler //这些方法大部分是可选的,按需求自定义 export class ProtobufNetTest implements enet.INetEventHandler { init(){ const netMgr = new NetNode<string>(); //注入处理器 netMgr.init({ netEventHandler: this }) } onStartConnenct?(connectOpt: enet.IConnectOptions): void { console.log(`start connect:${connectOpt.url}`) } onConnectEnd?(connectOpt: enet.IConnectOptions): void { console.log(`connect end:${connectOpt.url}`); } onError?(event: any, connectOpt: enet.IConnectOptions): void { console.error(`socket error`); console.error(event); } onClosed?(event: any, connectOpt: enet.IConnectOptions): void { console.error(`socket close`); console.error(event); } onStartReconnect(reConnectCfg: enet.IReconnectConfig, connectOpt: enet.IConnectOptions): void { console.log(`start reconnect:${connectOpt.url}`); } onReconnecting?(curCount: number, reConnectCfg: enet.IReconnectConfig, connectOpt: enet.IConnectOptions): void { console.log(`url:${connectOpt.url} reconnect count:${curCount},less count:${reConnectCfg.reconnectCount}`); } onReconnectEnd?(isOk: boolean, reConnectCfg: enet.IReconnectConfig, connectOpt: enet.IConnectOptions): void { console.log(`url:${connectOpt.url}reconnect end ${isOk ? "ok" : "fail"} `); } onStartRequest?(reqCfg: enet.IRequestConfig, connectOpt: enet.IConnectOptions): void { console.log(`start request:${reqCfg.protoKey},id:${reqCfg.reqId}`); } onData?(dpkg: enet.IDecodePackage<any>, connectOpt: enet.IConnectOptions): void { console.log(`data :${dpkg.key}`); } onRequestTimeout?(reqCfg: enet.IRequestConfig, connectOpt: enet.IConnectOptions): void { console.warn(`request timeout:${reqCfg.protoKey}`); } onCustomError?(dpkg: enet.IDecodePackage<any>, connectOpt: enet.IConnectOptions): void { console.error(`proto key:${dpkg.key},reqId:${dpkg.reqId},code:${dpkg.code},errorMsg:${dpkg.errorMsg}`); } onKick(dpkg: enet.IDecodePackage<any>, copt: enet.IConnectOptions) { console.log(`be kick`); } }
定制协议处理层
可能很多都会用protobuf作为通信协议处理,但它只是编解码数据的一个库,一些特殊和业务向的通信则需要自己处理 这个网络库提供这个协议处理扩展的能力
//两步轻松定制 //默认的协议处理是将数据序列化成字符串,然后发送出去 //第一步实现协议处理接口,可以将字符串转成二进制数据 import { Byte } from "./byte"; //ByteProtoHandler.ts //#region class ByteProtoHandler<ProtoKeyType> implements enet.IProtoHandler<ProtoKeyType> { private _heartbeatCfg: enet.IHeartBeatConfig; protected _byteUtil: Byte = new Byte();//这个二进制处理库可以在egf-pbws库里拿源码(这个是复制Laya的二进制处理库) public get heartbeatConfig(): enet.IHeartBeatConfig { return this._heartbeatCfg; } encodePkg(pkg: enet.IPackage<any>, useCrypto?: boolean): enet.NetData { const byteUtil = this._byteUtil; byteUtil.clear(); byteUtil.endian = Byte.LITTLE_ENDIAN; const str = JSON.stringify(pkg); byteUtil.writeUTFString(str); const buffer = byteUtil.buffer; byteUtil.clear(); return buffer; } protoKey2Key(protoKey: ProtoKeyType): string { return protoKey as any; } encodeMsg<T>(msg: enet.IMessage<T, ProtoKeyType>, useCrypto?: boolean): enet.NetData { return this.encodePkg({ type: PackageType.DATA, data: msg } as enet.IPackage); } decodePkg(data: enet.NetData): enet.IDecodePackage<any> { const byteUtil = this._byteUtil; byteUtil.clear(); byteUtil.endian = Byte.LITTLE_ENDIAN; byteUtil.writeArrayBuffer(data); byteUtil.pos = 0; const dataStr = byteUtil.readUTFString(); const parsedData: enet.IDecodePackage = JSON.parse(dataStr); const pkgType = parsedData.type; if (parsedData.type === PackageType.DATA) { const msg: enet.IMessage = parsedData.data; return { key: msg && msg.key, type: pkgType, data: msg.data, reqId: parsedData.data && parsedData.data.reqId } as enet.IDecodePackage; } else { if (pkgType === PackageType.HANDSHAKE) { this._heartbeatCfg = parsedData.data; } return { type: pkgType, data: parsedData.data } as enet.IDecodePackage; } } } //#endregion //第二步 注入协议处理器 //...省略代码 const netMgr = new NetNode<string>(); const protoHandler = new ByteProtoHandler(); netMgr.init({ protoHandler: protoHandler }) //...
定制socket层
使用ts/js开发项目的,大部分是使用websocket进行socket网络通信,当然也有可能使用socket.io,现在越来越少了。 也可能有的项目会自己基于tcp、udp进行socket通信 同样这个socket层也是可以定制的
//两步轻松定制 //socket的定制实现可以参考enet/src/wsocket.ts //比如 export class IOSocket implements enet.ISocket { //...省略 } // //...省略 //注入socket const netMgr = new NetNode<string>(); const ioSocket = new IOSocket(); netMgr.init({ socket: ioSocket }) //...
其他使用(重连、心跳)
提供丰富的配置项进行配置
/** * 重连配置接口 */ interface IReconnectConfig { /** * 重连次数 * 默认:4 */ reconnectCount?: number; /** * 连接超时时间,单位毫秒 * 默认: 120000 2分钟 */ connectTimeout?: number; } interface INodeConfig { /** * 底层socket实现 */ socket?: ISocket; /** * 网络事件处理器 * 默认:使用log输出方式 */ netEventHandler?: INetEventHandler; /** * 协议编码,解码处理器 * 默认: 使用字符串协议处理器 */ protoHandler?: IProtoHandler; /** * 重连配置,有默认值 */ reConnectCfg?: IReconnectConfig; /**心跳间隔阈值 ,默认100*/ heartbeatGapThreashold?: number; /**使用加密 */ useCrypto?: boolean; }