diff --git a/packages/feiyun/package.json b/packages/feiyun/package.json index 656c020..528fd41 100644 --- a/packages/feiyun/package.json +++ b/packages/feiyun/package.json @@ -1,7 +1,7 @@ { "name": "feiyun", "repository": "https://github.com/hxg2050/feiyun.git", - "version": "0.2.15", + "version": "0.2.16", "private": false, "type": "module", "main": "./src/index.ts", diff --git a/packages/handler/package.json b/packages/handler/package.json index 3e9df15..e853f2c 100644 --- a/packages/handler/package.json +++ b/packages/handler/package.json @@ -54,7 +54,7 @@ "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", "@types/node": "^20.11.19", - "feiyun": "^0.2.11", + "feiyun": "^0.2.16", "rollup": "^4.12.0", "rollup-plugin-ts": "^3.4.5", "tslib": "^2.6.2", diff --git a/packages/server/package.json b/packages/server/package.json index 2d87c11..38a23c9 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -2,7 +2,7 @@ "name": "@feiyun/server", "repository": "https://github.com/hxg2050/feiyun.git", "private": false, - "version": "0.0.6", + "version": "0.1.0", "type": "module", "main": "./src/index.ts", "engines": { diff --git a/packages/server/src/BaseServer.ts b/packages/server/src/BaseServer.ts new file mode 100644 index 0000000..3c36da7 --- /dev/null +++ b/packages/server/src/BaseServer.ts @@ -0,0 +1,43 @@ +import { Socket } from "./socket"; + +export abstract class BaseServer { + sockets = new Map(); + clients = new Map(); + uidSocketIds = new Map() + + + handlerCallback?: ((client: Socket, data: string) => void) | undefined; + + bindUid(uid: number, socket: Socket): void { + this.uidSocketIds.set(uid, socket.id) + } + unbindUid(uid: number): void { + this.uidSocketIds.delete(uid) + } + + abstract send(socket: T, name: string | number, data: any): void; + + sendTo(uid: number, name: string, data: any): void { + const tcpSocket = this.clients.get(uid); + if (!tcpSocket) { + return; + } + this.send(tcpSocket, name, data) + } + sendToUid(uid: number, name: string, data: any): void { + const socketId = this.uidSocketIds.get(uid) + + if (!socketId) { + return; + } + this.sendTo(socketId, name, data) + } + reply(socketId: number, requestId: number, data: any): void { + const sws = this.clients.get(socketId); + if (!sws) { + return; + } + this.send(sws, requestId, data) + } + +} \ No newline at end of file diff --git a/packages/server/src/IServer.ts b/packages/server/src/IServer.ts new file mode 100644 index 0000000..b861159 --- /dev/null +++ b/packages/server/src/IServer.ts @@ -0,0 +1,42 @@ +import { Socket } from "./socket" + +export interface IServer { + + start(): void + + handlerCallback?: (client: Socket, data: string) => void + + /** + * 绑定uid + * @param uid + * @param socket + */ + bindUid(uid: number, socket: Socket): void + /** + * 解除用户绑定 + * @param uid + */ + unbindUid(uid: number): void + /** + * 给指定id发送消息 + * @param uid + * @param name + * @param data + */ + sendTo(uid: number, name: string, data: any): void + /** + * 给自定用户发送消息 + * @param uid + * @param name + * @param data + */ + sendToUid(uid: number, name: string, data: any): void + + /** + * 回复消息 + * @param id + * @param requestId + * @param data + */ + reply(id: number, requestId: number, data: any): void; +} \ No newline at end of file diff --git a/packages/server/src/feiyun.ts b/packages/server/src/feiyun.ts index f1a454a..23fb6bf 100644 --- a/packages/server/src/feiyun.ts +++ b/packages/server/src/feiyun.ts @@ -1,10 +1,12 @@ import type { Socket } from './socket' import { compose, type Middleware } from './compose' import { Server } from './server' +import { IServer } from './IServer' export interface ApplicationConfig { host: string port: number + customServer?: (config: ApplicationConfig) => IServer } export class Request { @@ -33,36 +35,16 @@ export class Feiyun { port: 3000, } - public server!: Server + public server!: IServer constructor(config: Partial = {}) { this.config = { ...this.config, ...config } } - listen(port?: number) { - // const wss = new WebSocketServer(this.config); - // wss.on('connection', (ws, request) => { - // ws.on('open', () => { - - // }); - // ws.on('message', () => { - - // }); - // ws.on('error', () => { - - // }); - // ws.on('close', () => { - - // }); - // }); - // wss.on('close', () => { - - // }); - - // wss.on('error', () => { - - // }); - this.server = new Server(this.config) + listen() { + this.server = this.config.customServer ? this.config.customServer(this.config) : new Server({ + port: this.config.port + }) this.server.start() this.server.handlerCallback = (client, data) => { const ctx = new Context() @@ -87,13 +69,6 @@ export class Feiyun { console.log('server listen:', `ws://${this.config.host}:${this.config.port}`) } - // callback() { - // const ctx = new Context(); - // return (client, data) => { - - // } - // } - use(fn: FeiyunMiddleware) { this.middleware.push(fn) return this @@ -101,28 +76,7 @@ export class Feiyun { async responseHandler(ctx: Context) { if (ctx.response.data) { - this.server.reply(ctx.socket.socket, ctx.request.id, ctx.response.data) + this.server.reply(ctx.socket.id, ctx.request.id, ctx.response.data) } } -} - -// const app = new Feiyun(); -// app.use(async (ctx, next) => { -// const msg = JSON.parse(ctx.message); -// const [rid, route, req]: [number, string, any] = msg; - -// const handler = this.handlers.get(route); - -// ctx.response.body = await handler(); - -// if (!handler) { -// return; -// } - -// const res = await handler(req, client); - -// // 如果有返回值,那么直接回应 -// if (res) { -// this.reply(client.socket, rid, res); -// } -// }); +} \ No newline at end of file diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index f48dfe6..2910b83 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -1,7 +1,7 @@ import { Socket } from './socket' -import { createWebsocketServer } from './ws' +import { SWS, WebSocketData, createWebsocketServer } from './ws' import { IWebsocketServer } from './IWebsocketServer' -import { ServerWebSocket } from 'bun' +import { IServer } from './IServer' type Handler = (msg: any, client: Socket) => Promise | any @@ -12,27 +12,23 @@ interface ServerConfig { /** * 服务器 */ -export class Server { +export class Server implements IServer { handlers = new Map() clientIndex: number = 0 - wss?: IWebsocketServer + wss?: IWebsocketServer - clients: Map = new Map() - clientsFromServerWebSocket: Map = new Map(); - clientsFromUid: Map = new Map() + clientsFromServerWebSocket: Map = new Map(); + + clients: Map = new Map() + clientsFromUid: Map = new Map() /** * 配置 * @param config 配置 */ constructor(private config: ServerConfig) { - // this.wss = new WebSocketServer(config); - // this.wss.on('connection', (socket, request) => { - // this.onConnection(socket, request); - // }); - // console.log('ws://127.0.0.1:' + config.port); } /** @@ -43,8 +39,9 @@ export class Server { port: this.config.port }); this.wss.open((ws) => { - const client = new Socket(++this.clientIndex, this, ws) - this.clients.set(this.clientIndex, client) + const client = new Socket(this) + ws.data.socketId = client.id; + this.clients.set(this.clientIndex, ws) this.clientsFromServerWebSocket.set(ws, client); }); this.wss.message((ws, data) => { @@ -77,39 +74,76 @@ export class Server { this.handlers.set(path, handler) } + unbindUid(uid: number): void { + this.clientsFromUid.delete(uid) + } + /** * 发送消息到客户端 * @param sokcet * @param name * @param data */ - send(sokcet: ServerWebSocket, name: string | number, data: any) { + send(sokcet: SWS, name: string | number, data: any) { sokcet.send(JSON.stringify([1, name, data])) } + + sendTo(id: number, name: string, data: any): void { + const sws = this.clients.get(id); + if (!sws) { + return; + } + this.send(sws, name, data) + } + /** * 发送消息到客户端(uid) */ - sendToUid(uid: string | number, name: string, data: any) { - const socket = this.clientsFromUid.get(uid) + sendToUid(uid: number, name: string, data: any) { + const socketId = this.clientsFromUid.get(uid) - if (!socket) { - return false + if (!socketId) { + return; } + this.sendTo(socketId, name, data) + } - socket.send(name, data) + /** + * 绑定uid到连接上 + * @param uid + * @param socket + */ + bindUid(uid: number, socket: Socket) { + this.clientsFromUid.set(uid, socket.id); + } - return true + /** + * 获取用户连接 + * @param uid + * @returns + */ + getByUid(uid: number) { + return this.clientsFromUid.get(uid); + } + + /** + * 获取在线状态 + */ + isOnline(uid: number) { + const socket = this.clients.get(uid); + return socket?.readyState === 1 } /** * 给客户端回复消息 - * @param socket - * @param id - * @param data */ - reply(socket: ServerWebSocket, id: number, data: any) { - this.send(socket, id, data) + reply(socketId: number, requestId: number, data: any) { + const sws = this.clients.get(socketId); + if (!sws) { + return; + } + this.send(sws, requestId, data) } isDebug = false diff --git a/packages/server/src/socket.ts b/packages/server/src/socket.ts index b753ddf..4349cb7 100644 --- a/packages/server/src/socket.ts +++ b/packages/server/src/socket.ts @@ -1,8 +1,16 @@ -import { ServerWebSocket } from 'bun' -import type { Server } from './server' +import { IServer } from './IServer'; + +const createId = (() => { + let id = 0 + return () => { + return ++ id; + } +})(); export class Socket { - constructor(public id: number, public server: Server, public socket: ServerWebSocket) { + id = createId(); + + constructor(public server: IServer) { } @@ -15,15 +23,14 @@ export class Socket { this.data[name] = value } - private _uid?: number | string + private _uid?: number get uid() { return this._uid } - set uid(val) { this._uid = val if (this._uid !== undefined) { - this.server.clientsFromUid.set(this._uid, this) + this.server.bindUid(this._uid, this); } } @@ -31,6 +38,13 @@ export class Socket { * 发送消息 */ send(name: string, data?: any) { - this.server.send(this.socket, name, data) + this.server.sendTo(this.id, name, data); + } + + /** + * 断开链接 + */ + close() { + } } diff --git a/packages/server/src/tcp/TcpServer.ts b/packages/server/src/tcp/TcpServer.ts new file mode 100644 index 0000000..91a19c6 --- /dev/null +++ b/packages/server/src/tcp/TcpServer.ts @@ -0,0 +1,43 @@ +import { BaseServer } from "../BaseServer"; +import { IServer } from "../IServer"; +import { Socket } from "../socket"; +import { Socket as TcpSocket } from "bun"; + +type SocketData = { socketId: number }; + +export class TcpServer extends BaseServer> implements IServer { + + constructor(public port: number) { + super(); + } + + start(): void { + Bun.listen({ + hostname: "0.0.0.0", + port: this.port, + socket: { + open: (socket) => { + const client = new Socket(this) + socket.data.socketId = client.id; + this.clients.set(client.id, socket) + this.sockets.set(client.id, client); + }, + data: (socket, data) => { + const client = this.sockets.get(socket.data.socketId)!; + try { + this.handlerCallback?.(client, data.toString()) + } catch (error) { + console.error(error) + } + }, + close: (socket) => { + this.sockets.delete(socket.data.socketId); + } + }, + }); + } + + send(socket: TcpSocket, name: string | number, data: any): void { + socket.write(JSON.stringify([1, name, data])); + } +} \ No newline at end of file diff --git a/packages/server/src/ws.ts b/packages/server/src/ws.ts index 718cf03..e96dcd4 100644 --- a/packages/server/src/ws.ts +++ b/packages/server/src/ws.ts @@ -1,11 +1,17 @@ import { ServerWebSocket } from "bun"; import { IWebsocketServer } from "./IWebsocketServer"; +export type WebSocketData = { + socketId: number +} + +export type SWS = ServerWebSocket; + const ALL = '_:world'; -export const createWebsocketServer = (options: { port?: number, timeout?: number } = {}): IWebsocketServer => { +export const createWebsocketServer = (options: { port?: number, timeout?: number } = {}): IWebsocketServer => { - const allTimeout: Map = new Map(); - const ping = (ws: ServerWebSocket) => { + const allTimeout: Map = new Map(); + const ping = (ws: SWS) => { if (!options.timeout || options.timeout < 0) { return } @@ -13,14 +19,14 @@ export const createWebsocketServer = (options: { port?: number, timeout?: number allTimeout.set(ws, setTimeout(ws.close, options.timeout)); } - const stopPing = (ws: ServerWebSocket) => { + const stopPing = (ws: SWS) => { clearTimeout(allTimeout.get(ws)); allTimeout.delete(ws); } - let openHandler: (ws: ServerWebSocket) => void; - let messageHandler: (ws: ServerWebSocket, message: string | Buffer) => void; - let closeHandler: (ws: ServerWebSocket) => void; + let openHandler: (ws: SWS) => void; + let messageHandler: (ws: SWS, message: string | Buffer) => void; + let closeHandler: (ws: SWS) => void; const open = (handler: typeof openHandler) => { openHandler = handler; @@ -33,7 +39,7 @@ export const createWebsocketServer = (options: { port?: number, timeout?: number closeHandler = handler; } - Bun.serve({ + Bun.serve({ port: 3000, fetch(req, server) { if (