TCP/UDP programming
TCP Programming Basics
TCP (Transmission Control Protocol) is a connection-oriented, reliable transport protocol. Node.js provides TCP programming capabilities through the net
module. Creating a TCP server requires just a few lines of code:
const net = require('net');
const server = net.createServer((socket) => {
console.log('Client connected');
socket.on('data', (data) => {
console.log(`Received data: ${data}`);
socket.write(`ECHO: ${data}`);
});
socket.on('end', () => {
console.log('Client disconnected');
});
});
server.listen(8124, () => {
console.log('Server started on port 8124');
});
Implementing a TCP client is equally simple:
const net = require('net');
const client = net.connect({port: 8124}, () => {
console.log('Connected to server');
client.write('Hello TCP!');
});
client.on('data', (data) => {
console.log(data.toString());
client.end();
});
client.on('end', () => {
console.log('Disconnected from server');
});
Handling TCP Packet Sticking
TCP is a streaming protocol, so message boundary issues need to be addressed. Common solutions include:
- Fixed-Length Protocol:
// Sender
const data = Buffer.alloc(4);
data.writeInt32BE(1234, 0);
socket.write(data);
// Receiver
let buffer = Buffer.alloc(0);
socket.on('data', (chunk) => {
buffer = Buffer.concat([buffer, chunk]);
while(buffer.length >= 4) {
const value = buffer.readInt32BE(0);
console.log(value);
buffer = buffer.slice(4);
}
});
- Delimiter Protocol:
// Sender
socket.write('Message content|');
// Receiver
let buffer = '';
socket.on('data', (chunk) => {
buffer += chunk.toString();
const messages = buffer.split('|');
buffer = messages.pop();
messages.forEach(msg => console.log(msg));
});
UDP Programming Implementation
UDP is implemented via the dgram
module and is suitable for scenarios requiring high real-time performance:
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
console.log(`Server received: ${msg} from ${rinfo.address}:${rinfo.port}`);
});
server.bind(41234, () => {
console.log('UDP server started');
});
const client = dgram.createSocket('udp4');
client.send('Hello UDP', 41234, 'localhost', (err) => {
if (err) throw err;
client.close();
});
Protocol Selection Considerations
TCP Use Cases:
- Reliable transmission required (e.g., file transfer)
- Data order must be guaranteed
- Long-lived connections (e.g., real-time chat)
UDP Use Cases:
- High real-time requirements (e.g., video conferencing)
- Minor packet loss acceptable (e.g., DNS queries)
- Broadcast/multicast applications
Advanced Application Examples
TCP Proxy Server Implementation:
const net = require('net');
const proxy = net.createServer((clientSocket) => {
const serverSocket = net.connect(80, 'example.com');
clientSocket.pipe(serverSocket);
serverSocket.pipe(clientSocket);
serverSocket.on('error', (err) => {
console.error('Server connection error:', err);
clientSocket.end();
});
});
proxy.listen(8080);
UDP Multicast Example:
const dgram = require('dgram');
const socket = dgram.createSocket('udp4');
socket.on('listening', () => {
socket.addMembership('224.0.0.114');
});
socket.on('message', (msg, rinfo) => {
console.log(`Received multicast message: ${msg}`);
});
socket.bind(41234);
// Sending multicast
const client = dgram.createSocket('udp4');
client.send('Multicast test', 41234, '224.0.0.114');
Performance Optimization Techniques
- TCP Connection Pool Management:
class ConnectionPool {
constructor(maxConnections = 10) {
this.pool = [];
this.max = maxConnections;
}
getConnection() {
if(this.pool.length > 0) {
return Promise.resolve(this.pool.pop());
}
return new Promise((resolve) => {
const conn = net.createConnection(8124);
conn.on('connect', () => resolve(conn));
});
}
releaseConnection(conn) {
if(this.pool.length < this.max) {
this.pool.push(conn);
} else {
conn.end();
}
}
}
- UDP Batch Send Optimization:
function batchSend(socket, messages, port, address) {
let index = 0;
function sendNext() {
if(index >= messages.length) return;
socket.send(messages[index], port, address, (err) => {
if(err) console.error('Send failed:', err);
index++;
setImmediate(sendNext); // Avoid stack overflow
});
}
sendNext();
}
Error Handling Practices
TCP Error Handling Best Practices:
server.on('error', (err) => {
if(err.code === 'EADDRINUSE') {
console.error('Port in use, retrying...');
setTimeout(() => {
server.close();
server.listen(PORT);
}, 1000);
} else {
console.error('Server error:', err);
}
});
socket.on('error', (err) => {
console.error('Connection error:', err);
// Decide whether to reconnect based on error type
});
UDP Error Handling Patterns:
udpSocket.on('error', (err) => {
console.error(`UDP error: ${err.stack}`);
udpSocket.close();
// For critical services, attempt to restart
if(isCritical) {
setTimeout(createUDPServer, 1000);
}
});
Practical Application Examples
Implementing a Simple RPC Framework:
// TCP-RPC Server
class RPCServer {
constructor(port) {
this.methods = {};
this.server = net.createServer(this.handleConnection.bind(this));
this.server.listen(port);
}
register(name, method) {
this.methods[name] = method;
}
handleConnection(socket) {
let buffer = '';
socket.on('data', (data) => {
buffer += data;
const requests = buffer.split('\n');
buffer = requests.pop();
requests.forEach(request => {
try {
const {id, method, params} = JSON.parse(request);
const result = this.methods[method](...params);
socket.write(JSON.stringify({id, result}) + '\n');
} catch(err) {
socket.write(JSON.stringify({error: err.message}) + '\n');
}
});
});
}
}
// Usage example
const rpc = new RPCServer(8080);
rpc.register('add', (a, b) => a + b);
UDP-Based Service Discovery:
// Service Provider
const beacon = dgram.createSocket('udp4');
setInterval(() => {
const serviceInfo = JSON.stringify({
name: 'my-service',
port: 3000,
timestamp: Date.now()
});
beacon.send(serviceInfo, 8888, '255.255.255.255');
}, 5000);
// Service Discoverer
const discover = dgram.createSocket('udp4');
discover.bind(8888, () => {
discover.setBroadcast(true);
});
const services = {};
discover.on('message', (msg) => {
const info = JSON.parse(msg);
services[info.name] = {
...info,
lastSeen: Date.now()
};
});
// Clean up expired services
setInterval(() => {
const now = Date.now();
Object.keys(services).forEach(name => {
if(now - services[name].lastSeen > 10000) {
delete services[name];
}
});
}, 5000);
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:HTTPS与安全通信
下一篇:WebSocket实现