Node.js HTTP模块与Net模块网络编程实战

前言

各位老铁,今天咱们来聊点硬核的——Node.js的网络编程。在Node.js里,最核心的两个模块就是httpnet,前者帮你搭建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的网络编程核心:

这些都是做后端开发的基础功,学会了这些,你就可以尝试搭建自己的Web服务了。