使用 TypeScript 和 WebSockets 实现实时数据:分步指南

实时应用已成为各行各业的主流。无论是即时聊天、实时通知、协同编辑,还是股票价格更新,高效地提供实时数据都至关重要。WebSockets 是一种通过单个 TCP 连接提供全双工通信通道的协议,是构建此类应用程序的强大工具。与确保类型安全的 TypeScript 配合使用,您可以构建健壮且可维护的实时应用程序。在本指南中,我们将使用 TypeScript 和 WebSockets 来构建实时应用程序,重点关注类型安全和性能。

设置项目

首先,让我们建立一个新的 Node.js 项目并安装必要的依赖项。

mkdir real-time-app 
cd real-time-app 
npm init -y 
npm install typescript ts-node express ws 
npm install @types/node @types/express @types/ws - save-dev 
npx tsc - init

创建服务器

我们先使用 ws 库创建一个 WebSocket 服务器。在项目根目录下创建名为 server.ts 的文件。

import express from 'express';
import { createServer } from 'http';
import { WebSocketServer, WebSocket } from 'ws';

const app = express();

const port = 3000;
const server = createServer(app);

const wss = new WebSocketServer({ server });

interface Message {
   type: string;
   payload: any;
}

wss.on('connection', (ws: WebSocket) => {
 
  console.log('Client connected');
  
  ws.on('message', (message: string) => {
    const parsedMessage: Message = JSON.parse(message);
    console.log('Received:', parsedMessage);
  
    // Broadcast the message to all connected clients
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify(parsedMessage));
      }
    });
  });

  ws.on('close', () => {
    console.log('Client disconnected');
  });
});

server.listen(port, () => {
  console.log(`Server is listening on http://localhost:${port}`);
});

创建客户端

接下来,创建一个简单的客户端来连接 WebSocket 服务器。创建一个名为 client.ts 的文件。

import WebSocket from 'ws';

const ws = new WebSocket('ws://localhost:3000');

ws.on('open', () => {
  console.log('Connected to the server');

  const message = {
    type: 'greeting',
    payload: 'Hello, Server!',
  };
  
  ws.send(JSON.stringify(message));

});

ws.on('message', (data: string) => {
   const message = JSON.parse(data);
   console.log('Received:', message);
});

ws.on('close', () => {
   console.log('Disconnected from the server');
});

添加类型安全

为确保应用程序的类型安全,我们将为消息定义类型,并在代码中执行这些类型。创建一个名为 types.ts 的文件。

export interface Message {
  type: string;
  payload: any;
}

export interface GreetingMessage extends Message {
  type: 'greeting';
  payload: string;
}

export interface ChatMessage extends Message {
  type: 'chat';
  payload: {
     user: string;
     message: string;
  };
}

export type AppMessage = GreetingMessage | ChatMessage;

现在,更新 server.tsclient.ts 以使用这些类型。

server.ts

import express from 'express';
import { createServer } from 'http';
import { WebSocketServer, WebSocket } from 'ws';
import { AppMessage } from './types';

const app = express();

const port = 3000;
const server = createServer(app);
const wss = new WebSocketServer({ server });

wss.on('connection', (ws: WebSocket) => {
  console.log('Client connected');
   
  ws.on('message', (message: string) => {
     const parsedMessage: AppMessage = JSON.parse(message);
     console.log('Received:', parsedMessage);
    
     switch (parsedMessage.type) {
       case 'greeting':
          handleGreeting(parsedMessage);
          break;
       case 'chat':
          handleChat(parsedMessage);
          break;
       default:
          console.error('Unknown message type:', parsedMessage.type);
     }

     // Broadcast the message to all connected clients
     wss.clients.forEach((client) => {
        if (client.readyState === WebSocket.OPEN) {
           client.send(JSON.stringify(parsedMessage));
        }
     });
  });
  ws.on('close', () => {
    console.log('Client disconnected');
  });
});

function handleGreeting(message: AppMessage) {
 if (message.type === 'greeting') {
 console.log('Greeting:', message.payload);
 }
}

function handleChat(message: AppMessage) {
 if (message.type === 'chat') {
 console.log(`[${message.payload.user}]: ${message.payload.message}`);
 }
}

server.listen(port, () => {
  console.log(`Server is listening on http://localhost:${port}`);
});

client.ts

import WebSocket from 'ws';
import { AppMessage, GreetingMessage, ChatMessage } from './types';

const ws = new WebSocket('ws://localhost:3000');

ws.on('open', () => {
  console.log('Connected to the server');
  const message: GreetingMessage = {
    type: 'greeting',
    payload: 'Hello, Server!',
  };

  ws.send(JSON.stringify(message));

});

ws.on('message', (data: string) => {
  const message: AppMessage = JSON.parse(data);
  console.log('Received:', message);
});

ws.on('close', () => {
  console.log('Disconnected from the server');
});

处理不同的消息类型

为了更有效地处理不同的消息类型,我们可以使用 switch 语句或处理程序映射。下面是一个如何更新服务器以处理不同消息类型的示例:

import express from 'express';
import { createServer } from 'http';
import { WebSocketServer, WebSocket } from 'ws';
import { AppMessage, GreetingMessage, ChatMessage } from './types';

const app = express();

const port = 3000;
const server = createServer(app);
const wss = new WebSocketServer({ server });

wss.on('connection', (ws: WebSocket) => {
  console.log('Client connected');
  ws.on('message', (message: string) => {
    const parsedMessage: AppMessage = JSON.parse(message);
    console.log('Received:', parsedMessage);
    
    switch (parsedMessage.type) {
      case 'greeting':
        handleGreeting(parsedMessage);
        break;
      case 'chat':
        handleChat(parsedMessage);
        break;
      default:
        console.error('Unknown message type:', parsedMessage.type);
    }

    // Broadcast the message to all connected clients
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify(parsedMessage));
      }
    });

  });

  ws.on('close', () => {
    console.log('Client disconnected');
  });

});

function handleGreeting(message: GreetingMessage) {
  console.log('Greeting:', message.payload);
}

function handleChat(message: ChatMessage) {
  console.log(`[${message.payload.user}]: ${message.payload.message}`);
}

server.listen(port, () => {
  console.log(`Server is listening on http://localhost:${port}`);
});

改进类型处理

让我们创建一个实用程序,更优雅地处理不同的消息类型,从而进一步改进我们的类型处理。

types.ts

export interface Message {
  type: string;
  payload: any;
}

export interface GreetingMessage extends Message {
  type: 'greeting';
  payload: string;
}
export interface ChatMessage extends Message {
  type: 'chat';
  payload: {
    user: string;
    message: string;
  };
}

export type AppMessage = GreetingMessage | ChatMessage;

export const isGreetingMessage = (message: AppMessage): message is GreetingMessage => message.type === 'greeting';

export const isChatMessage = (message: AppMessage): message is ChatMessage => message.type === 'chat';

现在,可以在服务器中使用这些类型保护。

server.ts

import express from 'express';
import { createServer } from 'http';
import { WebSocketServer, WebSocket } from 'ws';
import { AppMessage, isGreetingMessage, isChatMessage } from './types';

const app = express();

const port = 3000;
const server = createServer(app);
const wss = new WebSocketServer({ server });

wss.on('connection', (ws: WebSocket) => {
  console.log('Client connected');

  ws.on('message', (message: string) => {
    const parsedMessage: AppMessage = JSON.parse(message);
    console.log('Received:', parsedMessage);
    if (isGreetingMessage(parsedMessage)) {
      handleGreeting(parsedMessage);
    } else if (isChatMessage(parsedMessage)) {
      handleChat(parsedMessage);
    } else {
      console.error('Unknown message type:', parsedMessage.type);
    }
    
    // Broadcast the message to all connected clients
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify(parsedMessage));
      }
    });

  });

  ws.on('close', () => {
    console.log('Client disconnected');
  });

});

function handleGreeting(message: GreetingMessage) {
    console.log('Greeting:', message.payload);
}

function handleChat(message: ChatMessage) {
    console.log(`[${message.payload.user}]: ${message.payload.message}`);
}

server.listen(port, () => {
    console.log(`Server is listening on http://localhost:${port}`);
});

处理客户端断开连接

要处理客户端断开连接并正确清理资源,可以修改 server.ts 文件:

wss.on('connection', (ws: WebSocket) => {
    console.log('Client connected');

  ws.on('message', (message: string) => {
      const parsedMessage: AppMessage = JSON.parse(message);
      console.log('Received:', parsedMessage);
      if (isGreetingMessage(parsedMessage)) {
        handleGreeting(parsedMessage);
      } else if (isChatMessage(parsedMessage)) {
        handleChat(parsedMessage);
      } else {
        console.error('Unknown message type:', parsedMessage.type);
      }
  
      // Broadcast the message to all connected clients
      wss.clients.forEach((client) => {
         if (client.readyState === WebSocket.OPEN) {
            client.send(JSON.stringify(parsedMessage));
         }
      });
   });
   
   ws.on('close', () => {
     console.log('Client disconnected');
   });
    
   ws.on('error', (error) => {
      console.error('WebSocket error:', error);
   });

});

改进客户端处理

您可以通过处理不同的消息类型并作出适当的响应来改进客户端代码。

client.ts(已改进)

import WebSocket from 'ws';
import { AppMessage, GreetingMessage, ChatMessage } from './types';

const ws = new WebSocket('ws://localhost:3000');

ws.on('open', () => {
    console.log('Connected to the server');

    const message: GreetingMessage = {
        type: 'greeting',
        payload: 'Hello, Server!',
    };

    ws.send(JSON.stringify(message));
});

ws.on('message', (data: string) => {
    const message: AppMessage = JSON.parse(data);
    handleMessage(message);
});

ws.on('close', () => {
    console.log('Disconnected from the server');
});

function handleMessage(message: AppMessage) {
    switch (message.type) {
        case 'greeting':
            console.log('Greeting:', message.payload);
            break;
        case 'chat':
            console.log(`[${message.payload.user}]: ${message.payload.message}`);
            break;
        default:
            console.error('Unknown message type:', message.type);
    }
}

性能考虑因素

  • 高效数据处理:尽量减少通过 WebSocket 连接发送的数据量,以减少延迟和带宽占用。如果 JSON 格式过于冗长,可使用二进制格式(如 Protocol Buffers 或 MessagePack)进行更高效的序列化。
  • 连接管理:在客户端实施重新连接逻辑,以优雅地处理断开连接。使用 reconnecting-websocket 等库可简化这一过程。
  • 负载平衡:对于大型应用程序,使用负载平衡器将 WebSocket 连接分配到多个服务器,以确保可扩展性和可靠性。
  • 安全性:使用 wss://(WebSocket Secure)确保 WebSocket 连接的安全性,以加密客户端和服务器之间传输的数据。实施身份验证和授权机制来控制访问。

通过利用 TypeScript 和 WebSockets,您可以构建健壮且类型安全的实时应用程序。TypeScript 的类型系统可确保代码的可靠性和可维护性,而 WebSockets 则为实时通信提供了必要的基础架构。按照本指南中概述的步骤进行操作,您就能在 TypeScript 应用程序中很好地掌握实时数据处理。

作者:Jacob MacInnis

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论