Node.js HTTP模块与Net模块网络编程实战
前言
各位老铁,今天咱们来聊点硬核的——Node.js的网络编程。在Node.js里,最核心的两个模块就是http和net,前者帮你搭建Web服务器,后者让你能玩TCP/UDP编程。
别看这俩模块API看起来不复杂,里面的门道可不少。我会从实际项目出发,带你一步步搞定这些网络编程的核心技能。
HTTP模块——Web服务器轻松建
1. 最简单的HTTP服务器
一行代码就能搞一个服务器,你敢信?
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Hello Node.js!</h1>');
});
server.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
运行后打开浏览器访问http://localhost:3000,就能看到"Hello Node.js!"了。
2. 处理不同路由
实际项目中,我们肯定要处理不同的URL路径:
const http = require('http');
const server = http.createServer((req, res) => {
const url = req.url;
const method = req.method;
// 设置CORS头
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
if (url === '/api/users' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify([{id: 1, name: '张三'}, {id: 2, name: '李四'}]));
} else if (url === '/api/status' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', uptime: process.uptime() }));
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
});
server.listen(8080, () => {
console.log('API服务器运行在 http://localhost:8080');
});
3. 读取请求体
POST请求带过来的数据怎么读?
const http = require('http');
const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/submit') {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
console.log('收到数据:', body);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, message: '收到数据啦' }));
});
} else {
res.writeHead(404);
res.end();
}
});
server.listen(3000);
4. 发送HTTP请求
除了做服务器,HTTP模块还能发请求:
const http = require('http');
const options = {
hostname: 'jsonplaceholder.typicode.com',
port: 80,
path: '/posts/1',
method: 'GET'
};
const req = http.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('响应:', JSON.parse(data));
});
});
req.on('error', (err) => {
console.error('请求出错:', err);
});
req.end();
更简单的方式是用http.get:
http.get('http://jsonplaceholder.typicode.com/posts/1', (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => console.log(JSON.parse(data)));
});
Net模块——TCP/UDP编程
1. TCP服务器
net模块让你能创建TCP服务器,实现更低层的网络通信:
const net = require('net');
const server = net.createServer((socket) => {
console.log('客户端连接:', socket.remoteAddress);
// 发送欢迎消息
socket.write('欢迎连接到聊天服务器!\n');
// 接收数据
socket.on('data', (data) => {
console.log('收到:', data.toString());
// 回显
socket.write('服务器收到:' + data);
});
// 断开连接
socket.on('close', () => {
console.log('客户端断开连接');
});
socket.on('error', (err) => {
console.error('socket错误:', err);
});
});
server.listen(8888, () => {
console.log('TCP服务器运行在 0.0.0.0:8888');
});
2. TCP客户端
连接刚才的服务器:
const net = require('net');
const client = net.createConnection({ port: 8888 }, () => {
console.log('已连接服务器');
client.write('你好服务器!');
});
client.on('data', (data) => {
console.log('服务器回应:', data.toString());
});
client.on('close', () => {
console.log('连接关闭');
});
client.on('error', (err) => {
console.error('连接错误:', err);
});
3. 构建简单的聊天室
结合TCP服务器,我们来做一个多客户端聊天室:
const net = require('net');
const clients = new Set();
const server = net.createServer((socket) => {
clients.add(socket);
console.log('新客户端加入,当前在线:', clients.size);
// 广播欢迎消息
broadcast(`${socket.remoteAddress} 加入了聊天室\n`);
socket.on('data', (data) => {
const message = data.toString().trim();
if (message) {
broadcast(`客户端: ${message}\n`);
}
});
socket.on('close', () => {
clients.delete(socket);
console.log('客户端离开,剩余在线:', clients.size);
broadcast('某用户离开了聊天室\n');
});
socket.on('error', (err) => {
console.error('客户端错误:', err);
clients.delete(socket);
});
});
function broadcast(message) {
for (const client of clients) {
client.write(message);
}
}
server.listen(3000, () => {
console.log('聊天室服务器运行在 0.0.0.0:3000');
});
4. 实现心跳检测
网络连接可能会"僵死",这时候需要心跳检测:
const net = require('net');
const server = net.createServer((socket) => {
socket.setKeepAlive(true, 30000); // 30秒空闲后发送探测
socket.on('data', () => {
// 收到数据,重置心跳计时
});
// 检查空闲连接
const heartbeat = setInterval(() => {
if (!socket.destroyed) {
socket.write('ping');
} else {
clearInterval(heartbeat);
}
}, 60000);
socket.on('close', () => {
clearInterval(heartbeat);
});
});
server.listen(9000);
实战案例:API代理服务
来一个实际项目中常见的场景——API代理,解决跨域和接口聚合问题:
const http = require('http');
const url = require('url');
// 代理配置
const proxyConfig = {
'/api/github': { host: 'api.github.com', port: 443 },
'/api/weather': { host: 'api.weather.com', port: 80 }
};
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url);
const pathname = parsedUrl.pathname;
// 检查是否需要代理
let target = null;
for (const [path, config] of Object.entries(proxyConfig)) {
if (pathname.startsWith(path)) {
target = config;
break;
}
}
if (!target) {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
return;
}
// 构建目标URL
const targetPath = pathname.replace(/^\/api\/\w+/, '') + (parsedUrl.search || '');
const options = {
hostname: target.host,
port: target.port,
path: targetPath,
method: req.method,
headers: req.headers
};
const proxyReq = http.request(options, (proxyRes) => {
res.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.pipe(res);
});
req.pipe(proxyReq);
proxyReq.on('error', (err) => {
console.error('代理请求错误:', err);
res.writeHead(502, { 'Content-Type': 'text/plain' });
res.end('Proxy Error');
});
});
server.listen(8080, () => {
console.log('代理服务器运行在 http://localhost:8080');
});
常见坑和注意事项
1. 中文乱码问题
// ❌ 可能乱码
res.end('你好');
// ✅ 指定编码
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end('你好');
2. 端口占用
const server = http.createServer((req, res) => {
res.end('Hello');
});
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error('端口被占用,尝试其他端口...');
server.listen(0); // 自动分配空闲端口
}
});
server.listen(3000);
3. 请求超时
const req = http.request(options, (res) => {
// 处理响应
});
req.setTimeout(5000); // 5秒超时
req.on('timeout', () => {
req.destroy();
console.error('请求超时');
});
req.end();
4. Keep-Alive连接池
频繁创建HTTP请求时,用Agent复用连接:
const http = require('http');
const agent = new http.Agent({
keepAlive: true,
maxSockets: 5,
maxFreeSockets: 2
});
const options = {
hostname: 'api.example.com',
port: 80,
path: '/data',
method: 'GET',
agent: agent
};
// 使用agent发送请求...
总结
今天我们学习了Node.js的网络编程核心:
- http模块:创建Web服务器、发送HTTP请求、处理路由和请求体
- net模块:TCP服务器/客户端、聊天室、心跳检测
- 实战技巧:API代理、连接复用、错误处理
这些都是做后端开发的基础功,学会了这些,你就可以尝试搭建自己的Web服务了。