Node.js 的 TCP 和 UDP使用示例代碼詳解
TCP
Node.js 的 net 模塊是其內(nèi)置模塊之一,主要用于創(chuàng)建基于 TCP(Transmission Control Protocol)的網(wǎng)絡(luò)應(yīng)用,包括服務(wù)器和客戶(hù)端。
核心功能與 API
1. 創(chuàng)建 TCP 服務(wù)器
使用 net.createServer() 方法創(chuàng)建服務(wù)器,通過(guò)回調(diào)函數(shù)處理連接事件:
const net = require('net');
const server = net.createServer((socket) => {
// 處理客戶(hù)端連接
console.log('客戶(hù)端已連接');
// 監(jiān)聽(tīng)數(shù)據(jù)事件(客戶(hù)端發(fā)送數(shù)據(jù)時(shí)觸發(fā))
socket.on('data', (data) => {
console.log(`收到數(shù)據(jù):${data.toString()}`);
socket.write('服務(wù)器已收到消息'); // 向客戶(hù)端發(fā)送響應(yīng)
});
// 監(jiān)聽(tīng)連接關(guān)閉事件
socket.on('end', () => {
console.log('客戶(hù)端已斷開(kāi)連接');
});
});
// 啟動(dòng)服務(wù)器,監(jiān)聽(tīng)指定端口
server.listen(3000, () => {
console.log('服務(wù)器已啟動(dòng),監(jiān)聽(tīng)端口 3000');
});2. 創(chuàng)建 TCP 客戶(hù)端
使用 net.createConnection() 方法創(chuàng)建客戶(hù)端,連接到服務(wù)器:
const net = require('net');
const client = net.createConnection({ port: 3000 }, () => {
// 連接成功后發(fā)送數(shù)據(jù)
console.log('已連接到服務(wù)器');
client.write('你好,這是客戶(hù)端發(fā)送的消息');
});
// 監(jiān)聽(tīng)服務(wù)器響應(yīng)
client.on('data', (data) => {
console.log(`收到服務(wù)器響應(yīng):${data.toString()}`);
client.end(); // 關(guān)閉連接
});
// 監(jiān)聽(tīng)連接關(guān)閉事件
client.on('end', () => {
console.log('已斷開(kāi)與服務(wù)器的連接');
});3. 核心對(duì)象與事件
net.Server:服務(wù)器對(duì)象,常用方法:
server.listen(port, callback):?jiǎn)?dòng)服務(wù)器監(jiān)聽(tīng)。server.close():關(guān)閉服務(wù)器。server.maxConnections:設(shè)置最大連接數(shù)。
net.Socket:表示單個(gè)客戶(hù)端連接,常用事件和方法:
事件:
'data':收到數(shù)據(jù)時(shí)觸發(fā)。'end':連接關(guān)閉時(shí)觸發(fā)。'error':發(fā)生錯(cuò)誤時(shí)觸發(fā)。'connect':客戶(hù)端連接成功時(shí)觸發(fā)。
方法:
socket.write(data):向?qū)Ψ桨l(fā)送數(shù)據(jù)。socket.end():關(guān)閉連接(允許發(fā)送緩沖區(qū)數(shù)據(jù))。socket.destroy():立即關(guān)閉連接。socket.pipe(destination):數(shù)據(jù)管道傳輸。
完整案例:簡(jiǎn)單的 TCP 聊天應(yīng)用
服務(wù)器端代碼
const net = require('net');
// 存儲(chǔ)所有連接的客戶(hù)端
const clients = new Set();
const server = net.createServer((socket) => {
// 為每個(gè)客戶(hù)端分配唯一標(biāo)識(shí)符
const clientId = `客戶(hù)端${Date.now().toString().slice(-4)}`;
clients.add(socket);
console.log(`${clientId} 已連接`);
socket.write(`歡迎,你是 ${clientId}`);
// 廣播消息給其他客戶(hù)端
socket.on('data', (data) => {
const message = `${clientId}: ${data.toString()}`;
clients.forEach((client) => {
if (client !== socket) {
client.write(message);
}
});
});
// 客戶(hù)端斷開(kāi)連接
socket.on('end', () => {
clients.delete(socket);
console.log(`${clientId} 已斷開(kāi)連接`);
});
// 錯(cuò)誤處理
socket.on('error', (err) => {
console.error(`客戶(hù)端 ${clientId} 發(fā)生錯(cuò)誤:`, err.message);
clients.delete(socket);
socket.destroy();
});
});
server.listen(3000, () => {
console.log('聊天服務(wù)器已啟動(dòng),監(jiān)聽(tīng)端口 3000');
});客戶(hù)端代碼
const net = require('net');
const readline = require('readline');
// 創(chuàng)建標(biāo)準(zhǔn)輸入輸出接口
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
// 連接到服務(wù)器
const client = net.createConnection({ port: 3000 }, () => {
console.log('已連接到聊天服務(wù)器');
console.log('輸入消息并按 Enter 發(fā)送,輸入 "exit" 退出');
// 從標(biāo)準(zhǔn)輸入讀取用戶(hù)消息并發(fā)送給服務(wù)器
rl.on('line', (input) => {
if (input.trim().toLowerCase() === 'exit') {
client.end();
rl.close();
} else {
client.write(input);
}
});
});
// 接收服務(wù)器消息并打印
client.on('data', (data) => {
console.log(data.toString());
});
// 處理連接關(guān)閉
client.on('end', () => {
console.log('已斷開(kāi)與服務(wù)器的連接');
rl.close();
});
// 錯(cuò)誤處理
client.on('error', (err) => {
console.error('連接錯(cuò)誤:', err.message);
rl.close();
});使用步驟
啟動(dòng)服務(wù)器:
node server.js
啟動(dòng)多個(gè)客戶(hù)端(在不同終端窗口):
node client.js
測(cè)試:
- 在客戶(hù)端輸入消息,按 Enter 發(fā)送。
- 所有客戶(hù)端(包括發(fā)送者)都會(huì)收到消息。
- 輸入
exit退出客戶(hù)端。
注意事項(xiàng)
- 數(shù)據(jù)編碼:默認(rèn)情況下,
net模塊以二進(jìn)制形式傳輸數(shù)據(jù),需通過(guò)toString()轉(zhuǎn)換為字符串。 - 錯(cuò)誤處理:務(wù)必監(jiān)聽(tīng)
'error'事件,避免未捕獲的異常導(dǎo)致程序崩潰。 - 連接管理:生產(chǎn)環(huán)境中需限制最大連接數(shù)(
server.maxConnections),并實(shí)現(xiàn)心跳機(jī)制檢測(cè)死連接。 - 數(shù)據(jù)完整性:TCP 是流協(xié)議,需自行處理消息邊界(如通過(guò)分隔符或長(zhǎng)度前綴)。
UDP
Node.js 的 dgram 模塊提供了創(chuàng)建 UDP(User Datagram Protocol)服務(wù)器和客戶(hù)端的功能。UDP 是一種無(wú)連接的傳輸協(xié)議,與 TCP 相比,它更輕量、傳輸速度更快,但不保證數(shù)據(jù)的可靠傳輸和順序。下面詳細(xì)介紹其核心功能、API 及使用案例。
核心功能與 API
1. 創(chuàng)建 UDP 服務(wù)器
使用 dgram.createSocket() 方法創(chuàng)建 UDP 套接字,監(jiān)聽(tīng)消息:
const dgram = require('dgram');
// 創(chuàng)建 UDP 服務(wù)器(UDP4 表示 IPv4,UDP6 表示 IPv6)
const server = dgram.createSocket('udp4');
// 監(jiān)聽(tīng)消息事件
server.on('message', (msg, rinfo) => {
console.log(`收到來(lái)自 ${rinfo.address}:${rinfo.port} 的消息: ${msg.toString()}`);
// 向客戶(hù)端發(fā)送響應(yīng)
const response = Buffer.from('服務(wù)器已收到消息');
server.send(response, rinfo.port, rinfo.address, (err) => {
if (err) console.error('發(fā)送響應(yīng)失敗:', err);
});
});
// 監(jiān)聽(tīng)錯(cuò)誤事件
server.on('error', (err) => {
console.error('服務(wù)器錯(cuò)誤:', err);
server.close();
});
// 監(jiān)聽(tīng)啟動(dòng)成功事件
server.on('listening', () => {
const address = server.address();
console.log(`服務(wù)器監(jiān)聽(tīng)在 ${address.address}:${address.port}`);
});
// 綁定端口啟動(dòng)服務(wù)器
server.bind(3000);2. 創(chuàng)建 UDP 客戶(hù)端
同樣使用 dgram.createSocket() 創(chuàng)建套接字,向服務(wù)器發(fā)送消息并接收響應(yīng):
const dgram = require('dgram');
// 創(chuàng)建 UDP 客戶(hù)端
const client = dgram.createSocket('udp4');
// 準(zhǔn)備要發(fā)送的消息
const message = Buffer.from('你好,這是客戶(hù)端發(fā)送的消息');
// 向服務(wù)器發(fā)送消息
client.send(message, 3000, 'localhost', (err) => {
if (err) {
console.error('發(fā)送消息失敗:', err);
client.close();
return;
}
console.log('消息已發(fā)送到服務(wù)器');
});
// 監(jiān)聽(tīng)服務(wù)器響應(yīng)
client.on('message', (msg, rinfo) => {
console.log(`收到來(lái)自 ${rinfo.address}:${rinfo.port} 的響應(yīng): ${msg.toString()}`);
client.close();
});
// 監(jiān)聽(tīng)錯(cuò)誤事件
client.on('error', (err) => {
console.error('客戶(hù)端錯(cuò)誤:', err);
client.close();
});3. 核心對(duì)象與事件
dgram.Socket:UDP 套接字對(duì)象,常用方法:socket.send(msg, port, address, callback):向指定地址和端口發(fā)送消息。socket.bind(port, [address], [callback]):綁定端口啟動(dòng)監(jiān)聽(tīng)(服務(wù)器端使用)。socket.close():關(guān)閉套接字。socket.setBroadcast(flag):設(shè)置是否允許廣播。
常用事件:
'message':收到消息時(shí)觸發(fā),回調(diào)參數(shù)為(msg, rinfo),其中rinfo包含發(fā)送方的地址和端口信息。'listening':套接字開(kāi)始監(jiān)聽(tīng)時(shí)觸發(fā)。'error':發(fā)生錯(cuò)誤時(shí)觸發(fā)。'close':套接字關(guān)閉時(shí)觸發(fā)。
完整案例:簡(jiǎn)單的 UDP 消息應(yīng)用
服務(wù)器端代碼
const dgram = require('dgram');
// 創(chuàng)建 UDP 服務(wù)器
const server = dgram.createSocket('udp4');
// 存儲(chǔ)所有客戶(hù)端信息(實(shí)際應(yīng)用中可能需要更復(fù)雜的管理)
const clients = new Set();
// 監(jiān)聽(tīng)消息事件
server.on('message', (msg, rinfo) => {
const clientKey = `${rinfo.address}:${rinfo.port}`;
// 首次收到消息時(shí)記錄客戶(hù)端信息
if (!clients.has(clientKey)) {
clients.add(clientKey);
console.log(`新客戶(hù)端 ${clientKey} 已連接`);
}
console.log(`收到 ${clientKey} 的消息: ${msg.toString()}`);
// 廣播消息給所有客戶(hù)端(除發(fā)送者外)
clients.forEach((client) => {
if (client !== clientKey) {
const [address, port] = client.split(':');
server.send(`${clientKey}: ${msg.toString()}`, parseInt(port), address);
}
});
});
// 監(jiān)聽(tīng)錯(cuò)誤事件
server.on('error', (err) => {
console.error('服務(wù)器錯(cuò)誤:', err);
server.close();
});
// 監(jiān)聽(tīng)啟動(dòng)成功事件
server.on('listening', () => {
const address = server.address();
console.log(`服務(wù)器監(jiān)聽(tīng)在 ${address.address}:${address.port}`);
});
// 綁定端口啟動(dòng)服務(wù)器
server.bind(3000);客戶(hù)端代碼
const dgram = require('dgram');
const readline = require('readline');
// 創(chuàng)建標(biāo)準(zhǔn)輸入輸出接口
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
// 創(chuàng)建 UDP 客戶(hù)端
const client = dgram.createSocket('udp4');
// 服務(wù)器地址和端口
const serverAddress = 'localhost';
const serverPort = 3000;
// 提示用戶(hù)輸入消息
console.log('輸入消息并按 Enter 發(fā)送,輸入 "exit" 退出');
// 從標(biāo)準(zhǔn)輸入讀取用戶(hù)消息并發(fā)送給服務(wù)器
rl.on('line', (input) => {
if (input.trim().toLowerCase() === 'exit') {
client.close();
rl.close();
return;
}
const message = Buffer.from(input);
client.send(message, serverPort, serverAddress, (err) => {
if (err) console.error('發(fā)送消息失敗:', err);
});
});
// 監(jiān)聽(tīng)服務(wù)器或其他客戶(hù)端的消息
client.on('message', (msg, rinfo) => {
console.log(`收到 ${rinfo.address}:${rinfo.port} 的消息: ${msg.toString()}`);
});
// 監(jiān)聽(tīng)錯(cuò)誤事件
client.on('error', (err) => {
console.error('客戶(hù)端錯(cuò)誤:', err);
client.close();
rl.close();
});
// 綁定客戶(hù)端端口(可選,不指定時(shí)系統(tǒng)會(huì)分配隨機(jī)端口)
client.bind(() => {
const address = client.address();
console.log(`客戶(hù)端已啟動(dòng),使用端口 ${address.port}`);
});使用步驟
啟動(dòng)服務(wù)器:
node udp_server.js
啟動(dòng)多個(gè)客戶(hù)端(在不同終端窗口):
node udp_client.js
測(cè)試:
- 在任一客戶(hù)端輸入消息,按 Enter 發(fā)送。
- 所有客戶(hù)端(包括發(fā)送者)都會(huì)收到消息。
- 輸入
exit退出客戶(hù)端。
注意事項(xiàng)
- 消息大小限制:UDP 數(shù)據(jù)包有最大大小限制(通常為 65,507 字節(jié)),超過(guò)限制的消息會(huì)被丟棄。
- 不可靠傳輸:UDP 不保證消息的可靠到達(dá)和順序,適合對(duì)實(shí)時(shí)性要求高但對(duì)數(shù)據(jù)完整性要求較低的場(chǎng)景(如視頻流、游戲)。
- 廣播和組播:UDP 支持廣播(
255.255.255.255)和組播(如224.0.0.1),可通過(guò)socket.setBroadcast(true)啟用。 - 錯(cuò)誤處理:盡管 UDP 不可靠,但仍需監(jiān)聽(tīng)
'error'事件處理網(wǎng)絡(luò)錯(cuò)誤。
到此這篇關(guān)于Node.js 的 TCP 和 UDP的文章就介紹到這了,更多相關(guān)Node.js TCP 和 UDP內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
更新npm項(xiàng)目依賴(lài)的實(shí)現(xiàn)方法
依賴(lài)管理是其中一個(gè)至關(guān)重要的環(huán)節(jié),本文主要介紹了更新npm項(xiàng)目依賴(lài)的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
一文詳解Node中module.exports和exports區(qū)別
這篇文章主要介紹了一文詳解Node中module.exports和exports區(qū)別2023-03-03
輕松創(chuàng)建nodejs服務(wù)器(7):阻塞操作的實(shí)現(xiàn)
這篇文章主要介紹了輕松創(chuàng)建nodejs服務(wù)器(7):阻塞操作的實(shí)現(xiàn),本文先是組出了代碼,然后對(duì)代碼一一分析,需要的朋友可以參考下2014-12-12
用node擼一個(gè)監(jiān)測(cè)復(fù)聯(lián)4開(kāi)售短信提醒的實(shí)現(xiàn)代碼
這篇文章主要介紹了用node擼一個(gè)監(jiān)測(cè)復(fù)聯(lián)4開(kāi)售短信提醒的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04

