mirror of
https://github.com/wyb10a10/cocos_creator_framework.git
synced 2026-06-07 22:43:13 +08:00
server framework
This commit is contained in:
@@ -1,5 +1,62 @@
|
||||
import {WebSocket} from "ws"
|
||||
import * as http from "http"
|
||||
import { HttpUtil } from "./models/HttpUtil"
|
||||
import { buffer } from "stream/consumers"
|
||||
|
||||
export interface WsConnectionOptions {
|
||||
connId: number
|
||||
ws: WebSocket
|
||||
httpReq: http.IncomingMessage
|
||||
onClose: (conn: WsConnection, code: number, reason: string) => void
|
||||
onRecvData: (data: Buffer) => void;
|
||||
}
|
||||
|
||||
export class WsConnection {
|
||||
|
||||
options: WsConnectionOptions
|
||||
ip!: string
|
||||
|
||||
constructor (options: WsConnectionOptions) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
getConnId() : number {
|
||||
return this.options.connId;
|
||||
}
|
||||
|
||||
getIp() : string {
|
||||
return this.ip;
|
||||
}
|
||||
|
||||
getWs() : WebSocket {
|
||||
return this.options.ws;
|
||||
}
|
||||
|
||||
init(options: WsConnectionOptions) {
|
||||
this.options = options;
|
||||
|
||||
this.ip = HttpUtil.getClientIp(options.httpReq);
|
||||
|
||||
// todo: log
|
||||
this.options.ws.onclose = e => { this.options.onClose(this, e.code, e.reason);}
|
||||
this.options.ws.onerror = e => { console.log("[client_err] ", e.error)}
|
||||
this.options.ws.onmessage = e => {
|
||||
if (Buffer.isBuffer(e.data)) {
|
||||
this.options.onRecvData(e.data);
|
||||
} else {
|
||||
console.log("[dataType_err]", e.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(reason?: string) {
|
||||
if (this.options.ws && this.options.ws.readyState == WebSocket.OPEN) {
|
||||
this.options.ws.close(1000, reason || 'Server Closed');
|
||||
}
|
||||
this.options.ws.onopen = this.options.ws.onclose = this.options.ws.onmessage = this.options.ws.onerror = undefined as any;
|
||||
this.ip = undefined;
|
||||
}
|
||||
|
||||
getIsClosed(): boolean {
|
||||
return this.options.ws.readyState !== WebSocket.OPEN;
|
||||
}
|
||||
}
|
||||
25
assets/Script/server/models/Counter.ts
Normal file
25
assets/Script/server/models/Counter.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export class Counter {
|
||||
|
||||
private _min: number;
|
||||
private _max: number;
|
||||
private _last: number;
|
||||
|
||||
constructor(min: number = 1, max: number = Number.MAX_SAFE_INTEGER) {
|
||||
this._min = min;
|
||||
this._max = max;
|
||||
this._last = max;
|
||||
}
|
||||
|
||||
/** 复位:从新从0开始计数 */
|
||||
reset() {
|
||||
this._last = this._max;
|
||||
}
|
||||
|
||||
getNext() {
|
||||
return this._last >= this._max ? (this._last = this._min) : ++this._last;
|
||||
}
|
||||
|
||||
get last() {
|
||||
return this._last;
|
||||
}
|
||||
}
|
||||
16
assets/Script/server/models/HttpUtil.ts
Normal file
16
assets/Script/server/models/HttpUtil.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import * as http from "http";
|
||||
|
||||
export class HttpUtil {
|
||||
static getClientIp(req: http.IncomingMessage) {
|
||||
var ipAddress;
|
||||
var forwardedIpsStr = req.headers['x-forwarded-for'] as string | undefined;
|
||||
if (forwardedIpsStr) {
|
||||
var forwardedIps = forwardedIpsStr.split(',');
|
||||
ipAddress = forwardedIps[0];
|
||||
}
|
||||
if (!ipAddress) {
|
||||
ipAddress = req.socket.remoteAddress;
|
||||
}
|
||||
return ipAddress ? ipAddress.replace(/^::ffff:/, '') : '';
|
||||
};
|
||||
}
|
||||
32
assets/Script/server/models/connMgr.ts
Normal file
32
assets/Script/server/models/connMgr.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { WsConnection } from "../connection";
|
||||
|
||||
export class ConnManager {
|
||||
private static _idConnMap : {[connId: number]: WsConnection | undefined};
|
||||
private static activeConnNum : number = 0;
|
||||
|
||||
public static getActiveConnNum() {
|
||||
return this.activeConnNum;
|
||||
}
|
||||
|
||||
public static getConn(connId : number) {
|
||||
return this._idConnMap[connId];
|
||||
}
|
||||
|
||||
public static addConn(conn : WsConnection) {
|
||||
this._idConnMap[conn.options.connId] = conn
|
||||
this.activeConnNum++;
|
||||
}
|
||||
|
||||
public static remConn(conn : WsConnection) {
|
||||
this._idConnMap[conn.options.connId] = undefined;
|
||||
this.activeConnNum--;
|
||||
}
|
||||
|
||||
public static sendMsg(connId: number, msg : Buffer) {
|
||||
let conn = this._idConnMap[connId];
|
||||
if (conn === undefined) {
|
||||
console.log("conn is not exists");
|
||||
}
|
||||
conn.options.ws.send(msg)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import * as WebSocket from 'ws';
|
||||
import { Server as WebSocketServer } from 'ws';
|
||||
import {WsConnection} from "./connection"
|
||||
import * as http from "http"
|
||||
import { Counter } from './models/Counter';
|
||||
import { ConnManager } from './models/connMgr';
|
||||
|
||||
enum WsServerStatus {
|
||||
Initializing,
|
||||
@@ -9,7 +12,91 @@ enum WsServerStatus {
|
||||
Closed,
|
||||
}
|
||||
|
||||
export class WsServer {
|
||||
private _id2conn : {[connId : string] : WsConnection | undefined}
|
||||
export interface WsServerOptions {
|
||||
host: string
|
||||
port: number
|
||||
timeout: number
|
||||
onClientConnect: (ws : WebSocket, httpReq: http.IncomingMessage) => void
|
||||
}
|
||||
|
||||
const defaultWsServerOptions: WsServerOptions = {
|
||||
host: "0.0.0.0",
|
||||
port: 8080,
|
||||
timeout: 3000,
|
||||
onClientConnect: undefined,
|
||||
}
|
||||
|
||||
export class WsServer {
|
||||
private status: WsServerStatus = WsServerStatus.Closed;
|
||||
private options: WsServerOptions;
|
||||
|
||||
private _wsServer: WebSocketServer;
|
||||
private _connIdCounter = new Counter(1);
|
||||
|
||||
constructor (options?: WsServerOptions) {
|
||||
this.options = Object.assign({}, defaultWsServerOptions, options);
|
||||
if (this.options.onClientConnect === undefined) {
|
||||
this.options.onClientConnect = this._onClientConnect;
|
||||
}
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this._wsServer) {
|
||||
throw new Error('Server already started');
|
||||
}
|
||||
this.status = WsServerStatus.Initializing
|
||||
this._wsServer = new WebSocketServer({port: this.options.port, host: this.options.host}, () => {
|
||||
console.log(`server started, listening on ${this.options.host}:${this.options.port}...`)
|
||||
this.status = WsServerStatus.Inited
|
||||
})
|
||||
this._wsServer.on("connection", this.options.onClientConnect)
|
||||
this._wsServer.on("error", e => {
|
||||
console.log("[server_error]", e)
|
||||
})
|
||||
}
|
||||
|
||||
private _onClientConnect = (ws: WebSocket, httpReq: http.IncomingMessage) => {
|
||||
// 服务不可用 不接受新的连接
|
||||
if (this.status !== WsServerStatus.Inited) {
|
||||
ws.close();
|
||||
return;
|
||||
}
|
||||
|
||||
let connId = this.getNextConnId()
|
||||
if (isNaN(connId)) {
|
||||
ws.close(-1, "无法建立更多的连接");
|
||||
return;
|
||||
}
|
||||
|
||||
let conn = new WsConnection({
|
||||
connId: connId,
|
||||
ws: ws,
|
||||
httpReq: httpReq,
|
||||
onClose: this._onClientClose,
|
||||
onRecvData: v => { this.onData(conn, v) }
|
||||
});
|
||||
ConnManager.addConn(conn);
|
||||
|
||||
console.log('[Connected]', `ActiveConn=${ConnManager.getActiveConnNum()}`)
|
||||
};
|
||||
|
||||
getNextConnId(): number {
|
||||
for (let i = 0; i < 1000; ++i) {
|
||||
let connId = this._connIdCounter.getNext();
|
||||
if (!ConnManager.getConn(connId)) {
|
||||
return connId;
|
||||
}
|
||||
}
|
||||
return NaN;
|
||||
}
|
||||
|
||||
private _onClientClose = (conn: WsConnection, code: number, reason: string) => {
|
||||
ConnManager.remConn(conn)
|
||||
console.log('[Disconnected]', `Code=${code} ${reason ? `Reason=${reason} ` : ''} ActiveConn=${ConnManager.getActiveConnNum()}`)
|
||||
}
|
||||
|
||||
private onData(conn : WsConnection, data: Buffer) {
|
||||
console.log("conn on data", data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
39
package-lock.json
generated
39
package-lock.json
generated
@@ -8,9 +8,30 @@
|
||||
"name": "cocos_creator_framework",
|
||||
"version": "3.5.1",
|
||||
"dependencies": {
|
||||
"@types/node": "^18.7.13",
|
||||
"@types/ws": "^8.5.3",
|
||||
"http": "^0.0.1-security",
|
||||
"ws": "^8.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.7.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.13.tgz",
|
||||
"integrity": "sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw=="
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||
"integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/http": {
|
||||
"version": "0.0.1-security",
|
||||
"resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz",
|
||||
"integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.8.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz",
|
||||
@@ -33,6 +54,24 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "18.7.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.13.tgz",
|
||||
"integrity": "sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw=="
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||
"integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"http": {
|
||||
"version": "0.0.1-security",
|
||||
"resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz",
|
||||
"integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.8.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz",
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
"version": "3.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "^18.7.13",
|
||||
"@types/ws": "^8.5.3",
|
||||
"http": "^0.0.1-security",
|
||||
"ws": "^8.8.1"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user