mirror of
https://github.com/crystelf/crystelf-core.git
synced 2025-07-04 14:49:19 +00:00
还是优化ws模块
This commit is contained in:
parent
5bdb43b32f
commit
55627d586c
@ -7,6 +7,7 @@
|
||||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.8.4",
|
||||
"chalk": "4",
|
||||
"dotenv": "^16.0.0",
|
||||
"express": "^4.18.0",
|
||||
|
79
pnpm-lock.yaml
generated
79
pnpm-lock.yaml
generated
@ -8,6 +8,9 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
axios:
|
||||
specifier: ^1.8.4
|
||||
version: 1.8.4
|
||||
chalk:
|
||||
specifier: '4'
|
||||
version: 4.1.2
|
||||
@ -153,6 +156,12 @@ packages:
|
||||
array-flatten@1.1.1:
|
||||
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
|
||||
|
||||
asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
|
||||
axios@1.8.4:
|
||||
resolution: {integrity: sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==}
|
||||
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
@ -205,6 +214,10 @@ packages:
|
||||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
@ -243,6 +256,10 @@ packages:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
delayed-stream@1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
denque@2.1.0:
|
||||
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
|
||||
engines: {node: '>=0.10'}
|
||||
@ -293,6 +310,10 @@ packages:
|
||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-set-tostringtag@2.1.0:
|
||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
escape-html@1.0.3:
|
||||
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
||||
|
||||
@ -312,6 +333,19 @@ packages:
|
||||
resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
follow-redirects@1.15.9:
|
||||
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
|
||||
form-data@4.0.2:
|
||||
resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
forwarded@0.2.0:
|
||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@ -359,6 +393,10 @@ packages:
|
||||
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
hasown@2.0.2:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -511,6 +549,9 @@ packages:
|
||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
qs@6.13.0:
|
||||
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
|
||||
engines: {node: '>=0.6'}
|
||||
@ -805,6 +846,16 @@ snapshots:
|
||||
|
||||
array-flatten@1.1.1: {}
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
axios@1.8.4:
|
||||
dependencies:
|
||||
follow-redirects: 1.15.9
|
||||
form-data: 4.0.2
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
@ -874,6 +925,10 @@ snapshots:
|
||||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
content-disposition@0.5.4:
|
||||
@ -896,6 +951,8 @@ snapshots:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
|
||||
denque@2.1.0: {}
|
||||
|
||||
depd@2.0.0: {}
|
||||
@ -930,6 +987,13 @@ snapshots:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
||||
es-set-tostringtag@2.1.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
get-intrinsic: 1.3.0
|
||||
has-tostringtag: 1.0.2
|
||||
hasown: 2.0.2
|
||||
|
||||
escape-html@1.0.3: {}
|
||||
|
||||
etag@1.8.1: {}
|
||||
@ -986,6 +1050,15 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
follow-redirects@1.15.9: {}
|
||||
|
||||
form-data@4.0.2:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
es-set-tostringtag: 2.1.0
|
||||
mime-types: 2.1.35
|
||||
|
||||
forwarded@0.2.0: {}
|
||||
|
||||
fresh@0.5.2: {}
|
||||
@ -1034,6 +1107,10 @@ snapshots:
|
||||
|
||||
has-symbols@1.1.0: {}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
dependencies:
|
||||
has-symbols: 1.1.0
|
||||
|
||||
hasown@2.0.2:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
@ -1156,6 +1233,8 @@ snapshots:
|
||||
forwarded: 0.2.0
|
||||
ipaddr.js: 1.9.1
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
qs@6.13.0:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
|
@ -34,7 +34,7 @@ class SampleController {
|
||||
if (!name) {
|
||||
return response.error(res, '姓名不能为空!', 400);
|
||||
}
|
||||
const result = sampleService.generateGreeting(name);
|
||||
const result = await sampleService.generateGreeting(name);
|
||||
await response.success(res, result);
|
||||
} catch (error) {
|
||||
await response.error(res, '请求失败了..', 500, error);
|
||||
|
@ -1 +0,0 @@
|
||||
keep
|
@ -19,40 +19,54 @@ class WSServer {
|
||||
}
|
||||
|
||||
private init(): void {
|
||||
this.wss.on('connection', (socket: AuthenticatedSocket) => {
|
||||
this.wss.on('connection', (socket: AuthenticatedSocket, req) => {
|
||||
const ip = req.socket.remoteAddress || 'unknown';
|
||||
logger.info(`收到来自 ${ip} 的 WebSocket 连接请求..`);
|
||||
|
||||
socket.heartbeat = WsTools.setUpHeartbeat(socket);
|
||||
|
||||
socket.on('message', async (raw) => {
|
||||
const msg = WsTools.parseMessage<WSMessage>(raw);
|
||||
if (!msg) return this.handleInvalidMessage(socket);
|
||||
logger.debug(`Received raw message from ${ip}: ${raw.toString()}`);
|
||||
|
||||
await this.routeMessage(socket, msg);
|
||||
const msg = WsTools.parseMessage<WSMessage>(raw);
|
||||
if (!msg) return this.handleInvalidMessage(socket, ip);
|
||||
|
||||
await this.routeMessage(socket, msg, ip);
|
||||
});
|
||||
|
||||
socket.on('close', () => {
|
||||
logger.info(`ws断开连接 ${ip} (${socket.clientId || 'unauthenticated'})`);
|
||||
this.handleDisconnect(socket);
|
||||
});
|
||||
|
||||
socket.on('error', (err) => {
|
||||
logger.error(`WS error from ${ip}: ${err.message}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async handleInvalidMessage(socket: WebSocket) {
|
||||
private async handleInvalidMessage(socket: WebSocket, ip: string) {
|
||||
logger.warn(`Invalid message received from ${ip}`);
|
||||
await WsTools.send(socket, {
|
||||
type: 'error',
|
||||
message: 'Invalid message format',
|
||||
});
|
||||
}
|
||||
|
||||
private async routeMessage(socket: AuthenticatedSocket, msg: WSMessage) {
|
||||
private async routeMessage(socket: AuthenticatedSocket, msg: WSMessage, ip: string) {
|
||||
if (!socket.isAuthed) {
|
||||
if (this.isAuthMessage(msg)) {
|
||||
await this.handleAuth(socket, msg);
|
||||
logger.info(`Attempting auth from ${ip} as ${msg.clientId}`);
|
||||
await this.handleAuth(socket, msg, ip);
|
||||
} else {
|
||||
logger.warn(`Received message before auth from ${ip}: ${JSON.stringify(msg)}`);
|
||||
await this.handleInvalidMessage(socket, ip);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (socket.clientId) {
|
||||
await wsHandler.handle(socket, socket.clientId, msg);
|
||||
}
|
||||
logger.debug(`Routing message from ${socket.clientId}: ${JSON.stringify(msg)}`);
|
||||
await wsHandler.handle(socket, socket.clientId!, msg);
|
||||
}
|
||||
|
||||
private isAuthMessage(msg: WSMessage): msg is AuthMessage {
|
||||
@ -63,20 +77,25 @@ class WSServer {
|
||||
);
|
||||
}
|
||||
|
||||
private async handleAuth(socket: AuthenticatedSocket, msg: AuthMessage) {
|
||||
private async handleAuth(socket: AuthenticatedSocket, msg: AuthMessage, ip: string) {
|
||||
if (msg.secret === this.secret) {
|
||||
socket.isAuthed = true;
|
||||
socket.clientId = msg.clientId;
|
||||
wsClientManager.add(msg.clientId, socket);
|
||||
logger.info(`Auth success from ${ip}, clientId: ${msg.clientId}`);
|
||||
await WsTools.send(socket, { type: 'auth', success: true });
|
||||
} else {
|
||||
logger.warn(`Auth failed from ${ip} (invalid secret), clientId: ${msg.clientId}`);
|
||||
await WsTools.send(socket, { type: 'auth', success: false });
|
||||
}
|
||||
}
|
||||
|
||||
private handleDisconnect(socket: AuthenticatedSocket) {
|
||||
if (socket.heartbeat) clearInterval(socket.heartbeat);
|
||||
if (socket.clientId) wsClientManager.remove(socket.clientId);
|
||||
if (socket.clientId) {
|
||||
wsClientManager.remove(socket.clientId);
|
||||
logger.info(`Removed client ${socket.clientId} from manager`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
69
src/test/wsTestClient.ts
Normal file
69
src/test/wsTestClient.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import WebSocket from 'ws';
|
||||
import axios from 'axios';
|
||||
|
||||
const WS_URL = 'ws://127.0.0.1:3001';
|
||||
const WS_SECRET = '114514';
|
||||
const CLIENT_ID = 'test';
|
||||
|
||||
function createWebSocketClient() {
|
||||
const socket = new WebSocket(WS_URL);
|
||||
|
||||
socket.on('open', () => {
|
||||
console.log('[WS] Connected to server');
|
||||
|
||||
const authPayload = {
|
||||
type: 'auth',
|
||||
secret: WS_SECRET,
|
||||
clientId: CLIENT_ID,
|
||||
};
|
||||
socket.send(JSON.stringify(authPayload));
|
||||
});
|
||||
|
||||
socket.on('message', (raw) => {
|
||||
const msg = JSON.parse(raw.toString());
|
||||
console.log('[WS] Message from server:', msg);
|
||||
|
||||
if (msg.type === 'auth' && msg.success === true) {
|
||||
socket.send(JSON.stringify({ type: 'test' }));
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('close', () => {
|
||||
console.log('[WS] Connection closed');
|
||||
});
|
||||
|
||||
socket.on('error', (err) => {
|
||||
console.error('[WS] Error:', err);
|
||||
});
|
||||
}
|
||||
|
||||
async function testGetAPI() {
|
||||
try {
|
||||
const response = await axios.get('http://localhost:3000/api/sample/hello');
|
||||
console.log('[HTTP][GET] Response:', response.data);
|
||||
} catch (err) {
|
||||
console.error('[HTTP][GET] Error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function testPostAPI() {
|
||||
try {
|
||||
const response = await axios.post('http://localhost:3000/api/sample/greet', {
|
||||
name: 'Jerry',
|
||||
});
|
||||
console.log('[HTTP][POST] Response:', response.data);
|
||||
} catch (err) {
|
||||
console.error('[HTTP][POST] Error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
createWebSocketClient();
|
||||
|
||||
setTimeout(() => {
|
||||
testGetAPI();
|
||||
testPostAPI();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
main();
|
Loading…
x
Reference in New Issue
Block a user