Сетевое программирование на TypeScript: от сокетов до WebSocket
Когда мы говорим о сетевом программировании на TypeScript, то часто представляем себе сложные распределённые системы, чаты реального времени или игровые серверы. И это не случайно: TypeScript с его строгой типизацией и возможностями статического анализа делает разработку сетевых приложений не только безопасной, но и по-настоящему удобной.
В этой статье мы разберём, как создавать клиент-серверные приложения на TypeScript, используя встроенные модули Node.js — net и dgram — а также библиотеки для работы с WebSocket. Вы получите готовые примеры кода, которые можно сразу применить в проектах.
Почему TypeScript для сетевого программирования?
Многие разработчики привыкли использовать JavaScript для бэкенда, но TypeScript сетевое программирование даёт ряд преимуществ:
- Типизация данных — вы заранее определяете структуру пакетов и сообщений
- Автодополнение — IDE подсказывает методы сокетов и их параметры
- Меньше ошибок — компилятор ловит несоответствия типов ещё до запуска
Давайте посмотрим на реальный пример. Представьте, что мы пишем TCP-сервер. В JavaScript вы бы писали:
const server = require('net').createServer(socket => {
socket.on('data', data => {
// data — это Buffer, нужно помнить об этом
});
});
В TypeScript же мы получаем чёткие типы:
import * as net from 'net';
const server: net.Server = net.createServer((socket: net.Socket) => {
socket.on('data', (data: Buffer) => {
// Тип Buffer известен, работаем уверенно
});
});
Кажется мелочью, но на больших проектах эта разница спасает часы отладки.
Основы: TCP-сервер и клиент на TypeScript
Начнём с классики — TCP. Создадим простой эхо-сервер, который возвращает клиенту то, что получил.
TCP-сервер
import * as net from 'net';
const PORT: number = 3000;
const HOST: string = '127.0.0.1';
const server: net.Server = net.createServer((socket: net.Socket) => {
console.log(`Клиент подключился: ${socket.remoteAddress}:${socket.remotePort}`);
socket.on('data', (data: Buffer) => {
const message: string = data.toString().trim();
console.log(`Получено: ${message}`);
socket.write(`Эхо: ${message}\n`);
});
socket.on('end', () => {
console.log('Клиент отключился');
});
socket.on('error', (err: Error) => {
console.error('Ошибка сокета:', err.message);
});
});
server.listen(PORT, HOST, () => {
console.log(`TCP-сервер запущен на ${HOST}:${PORT}`);
});
Обратите внимание: мы типизировали все переменные — server, socket, data, err. Это позволяет нам видеть, какие методы доступны у сокета, и избежать случайных ошибок.
TCP-клиент
import * as net from 'net';
const client: net.Socket = new net.Socket();
client.connect(3000, '127.0.0.1', () => {
console.log('Подключено к серверу');
client.write('Привет, сервер!');
});
client.on('data', (data: Buffer) => {
console.log('Ответ сервера:', data.toString());
client.destroy();
});
client.on('close', () => {
console.log('Соединение закрыто');
});
Этот код можно запустить в любом проекте на Node.js с TypeScript. Просто установите @types/node и компилируйте.
UDP: когда скорость важнее надёжности
Если вам нужно передавать данные без установления соединения (например, для стриминга или игр), используйте UDP сокеты TypeScript. Модуль dgram предоставляет всё необходимое.
UDP-сервер
import * as dgram from 'dgram';
const server: dgram.Socket = dgram.createSocket('udp4');
server.on('message', (msg: Buffer, rinfo: dgram.RemoteInfo) => {
console.log(`Получено от ${rinfo.address}:${rinfo.port}: ${msg.toString()}`);
server.send(msg, rinfo.port, rinfo.address);
});
server.on('listening', () => {
const address = server.address();
console.log(`UDP-сервер слушает ${address.address}:${address.port}`);
});
server.bind(4000);
UDP-клиент
import * as dgram from 'dgram';
const client: dgram.Socket = dgram.createSocket('udp4');
const message: Buffer = Buffer.from('Привет по UDP!');
client.send(message, 4000, '127.0.0.1', (err: Error | null) => {
if (err) {
console.error('Ошибка отправки:', err);
} else {
console.log('Сообщение отправлено');
}
client.close();
});
UDP не гарантирует доставку, но работает быстрее TCP. Выбирайте протокол под задачу: для передачи файлов — TCP, для видеотрансляций — UDP.
WebSocket: двусторонняя связь в реальном времени
Современные веб-приложения редко обходятся без WebSocket. WebSocket TypeScript позволяет организовать постоянный канал связи между клиентом и сервером. Используем популярную библиотеку ws.
Установите зависимости:
npm install ws
npm install -D @types/ws
WebSocket-сервер
import { WebSocketServer, WebSocket } from 'ws';
const wss: WebSocketServer = new WebSocketServer({ port: 8080 });
wss.on('connection', (ws: WebSocket) => {
console.log('Новое WebSocket-соединение');
ws.on('message', (data: Buffer) => {
const message: string = data.toString();
console.log(`Получено: ${message}`);
// Отправляем сообщение всем подключённым клиентам
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(`Сервер: ${message}`);
}
});
});
ws.on('close', () => {
console.log('Клиент отключился');
});
ws.send('Добро пожаловать на WebSocket-сервер!');
});
console.log('WebSocket сервер запущен на ws://localhost:8080');
WebSocket-клиент (браузер или Node.js)
import WebSocket from 'ws';
const ws: WebSocket = new WebSocket('ws://localhost:8080');
ws.on('open', () => {
console.log('Подключено к серверу');
ws.send('Привет от клиента!');
});
ws.on('message', (data: Buffer) => {
console.log('Сообщение от сервера:', data.toString());
});
ws.on('close', () => {
console.log('Соединение закрыто');
});
Обратите внимание: в TypeScript мы явно указываем тип WebSocket и Buffer. Это помогает избежать путаницы с разными типами данных.