TCP, UDP, DNS — клиент-сервер на Node.js
Модули
net(TCP),dgram(UDP) иdnsдают прямой доступ к транспортному уровню — без HTTP-надстройки. Для бинарных протоколов, real-time игр, прокси, парсеров.
TCP — net.createServer
const net = require('net');
const server = net.createServer((socket) => {
console.log(`Client connected: ${socket.remoteAddress}:${socket.remotePort}`);
socket.setNoDelay(true); // отключить алгоритм Нэйгла — без задержки
socket.setEncoding('utf8');
socket.on('data', (chunk) => {
// ВНИМАНИЕ: TCP пакеты режутся и склеиваются — нет гарантии что один write = один data
console.log('Got:', chunk);
});
socket.on('end', () => console.log('Client disconnected'));
socket.on('error', (err) => console.error(err));
socket.write('Hello\r\n');
});
server.listen(7000, '0.0.0.0', () => console.log('TCP on 7000'));
Накопление буфера
let buffer = '';
socket.on('data', (chunk) => {
buffer += chunk;
let idx;
while ((idx = buffer.indexOf('\n')) >= 0) {
const line = buffer.slice(0, idx);
buffer = buffer.slice(idx + 1);
handleMessage(line); // обрабатываем по одному сообщению
}
});
UDP — dgram
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
console.log(`UDP from ${rinfo.address}:${rinfo.port}:`, msg.toString());
server.send('pong', rinfo.port, rinfo.address);
});
server.bind(41234);
// Клиент
const client = dgram.createSocket('udp4');
client.send(Buffer.from('ping'), 41234, '127.0.0.1', () => client.close());
UDP — пакетный, без соединения. Быстрее TCP но без гарантий доставки/порядка. Подходит для голоса, игр, DNS, локальной телеметрии.
DNS
const dns = require('dns').promises;
const records = await dns.resolve4('github.com'); // ['140.82.121.4']
const mx = await dns.resolveMx('gmail.com'); // [{exchange, priority}]
const reverse = await dns.reverse('8.8.8.8'); // ['dns.google']
const all = await dns.resolveAny('github.com'); // A, AAAA, MX, TXT
dns.lookup использует getaddrinfo (блокирует libuv thread pool), dns.resolve* — нативный DNS-клиент (не блокирует пул).
Подводные камни
- TCP
data≠ одно сообщение — нужен парсер сообщений с границами (длина + payload, или разделитель) - socket держит процесс —
socket.unrefесли не критично для выхода - Connection reset (ECONNRESET) — без обработчика
errorкрашит процесс throwв обработчике сокета — теряется коннект и процесс может упасть- UDP MTU — пакет > 1500 байт фрагментируется; держи payload < 1400
- DNS round-robin:
resolve4возвращает IP в случайном порядке для балансировки dns.lookupблокирует thread pool — увеличьUV_THREADPOOL_SIZEили используйresolve*
🎓 Источники
- 🎓 [Клиент-сервер на Node.js TCP и UDP, DNS] · 2018-10-03 · YouTube · [Marp](../../../Documents/TimurShemsedinov/2018-10-03 — Клиент-сервер на Node.js TCP и UDP, DNS (bHn-wTlTTR0).md)
- Тезисы: net и dgram, createServer + listener, события сокета, pause/resume, setNoDelay и алгоритм Нэйгла, UTF-8 символ != 1 байт, накопление буфера для логических пакетов («TCP пакеты режутся и склеиваются»), ECONNRESET обработка, socket.unref, UDP — пакетный без соединения, dns.resolveAny, асинхронный DNS
- Цитата: «TCP — это поток байт, не сообщений. Гранят пакеты ОС и сеть, не ты»
- 🎓 [Обзор встроенного Node.js API] · 2018-09-26 · YouTube
- Тезисы:
netмодуль для TCP/UDP,dnsуже на промисах
- Тезисы: