FORMA

网络编程

Node.js 网络编程详解

Node.js 提供了丰富的内置模块,用于构建网络应用。下面从 HTTP/HTTPS、WebSocket、TCP/UDP 以及 DNS 解析四个方面进行深入介绍。

一、HTTP/HTTPS

httphttps 模块是 Node.js 核心模块,用于创建 HTTP 服务器和客户端。

1. 创建服务器(http.createServer

js
const http = require("http");

const server = http.createServer((req, res) => {
    // req: 请求对象,包含请求方法、URL、头部等
    // res: 响应对象,用于发送响应
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Hello World\n");
});

server.listen(3000, () => {
    console.log("Server running at http://localhost:3000/");
});
  • reqhttp.IncomingMessage 实例,可读取请求行、头部、数据。
  • reshttp.ServerResponse 实例,用于设置状态码、头部并写入响应体。

2. 处理请求/响应对象

获取请求信息

js
const { method, url, headers } = req;
let body = "";
req.on("data", chunk => {
    body += chunk.toString();
});
req.on("end", () => {
    console.log("请求体:", body);
});

设置响应

js
res.statusCode = 404;
res.setHeader("X-Custom", "MyValue");
res.write("Partial content");
res.end(); // 结束响应

常用便捷方法:

  • res.writeHead(statusCode, statusMessage, headers):同时设置状态码和头部。
  • res.end([data]):结束响应,可选最后发送数据。

解析请求体:对于 JSON 或表单数据,可使用中间件或手动解析。也可借助 express 等框架。

3. 创建客户端请求(http.requesthttp.get

js
const http = require("http");

// 使用 http.request
const req = http.request(
    {
        hostname: "example.com",
        port: 80,
        path: "/",
        method: "GET",
        headers: { Accept: "application/json" },
    },
    res => {
        console.log(`状态码: ${res.statusCode}`);
        res.setEncoding("utf8");
        res.on("data", chunk => {
            console.log(chunk);
        });
    }
);
req.on("error", e => console.error(e));
req.end();

// 简化 GET: http.get
http.get("http://example.com", res => {
    /* ... */
});

对于 HTTPS,使用 https.request,默认端口 443。

4. 代理、Keep-Alive、超时设置

  • 代理:可设置 agent 选项自定义 HTTP 代理。例如使用 http.Agent 管理连接池,或使用 globalAgent。也可通过 HttpsProxyAgent 实现代理转发。
js
const { Agent } = require("http");
const agent = new Agent({ keepAlive: true, maxSockets: 10 });
const req = http.request({ host: "...", agent });
  • Keep-Alive:默认 agent.keepAlivefalse,需手动开启:
js
const keepAliveAgent = new http.Agent({ keepAlive: true });
http.request({ agent: keepAliveAgent, ... });
  • 超时设置
    • 服务器端:server.setTimeout(timeout, callback)req.setTimeout(timeout, callback)
    • 客户端:req.setTimeout(timeout, callback) 或设置 timeout 选项(request 的 options 中)。
js
// 服务器超时
server.setTimeout(5000, socket => {
    console.log("请求超时");
    socket.destroy();
});

// 客户端超时
req.setTimeout(3000, () => {
    req.destroy();
    console.error("请求超时");
});

二、WebSocket:ws 库集成与简单聊天

WebSocket 实现了全双工通信,适合实时应用。ws 是 Node.js 中流行的 WebSocket 库。

安装

bash
npm install ws

服务器端(与 HTTP 服务器共享端口)

js
const http = require("http");
const WebSocket = require("ws");

const server = http.createServer((req, res) => {
    if (req.url === "/" && req.method === "GET") {
        res.end("WebSocket server running");
    }
});

const wss = new WebSocket.Server({ server });

wss.on("connection", (ws, req) => {
    console.log("客户端连接");
    ws.on("message", message => {
        console.log("收到: %s", message);
        // 广播给所有客户端
        wss.clients.forEach(client => {
            if (client !== ws && client.readyState === WebSocket.OPEN) {
                client.send(message);
            }
        });
    });
    ws.send("欢迎加入聊天室");
});

server.listen(8080);

客户端(浏览器或 Node.js)

js
const ws = new WebSocket("ws://localhost:8080");
ws.onopen = () => ws.send("Hello");
ws.onmessage = e => console.log("收到:", e.data);

功能扩展

  • 心跳检测:使用定时器发送 ping/pong。
  • 房间管理:维护 SetMap 存储客户端分组。

三、TCP/UDP

1. TCP 服务器与客户端(net 模块)

TCP 服务器

js
const net = require("net");

const server = net.createServer(socket => {
    console.log("客户端已连接");
    socket.on("data", data => {
        console.log(`收到: ${data}`);
        socket.write("Echo: " + data);
    });
    socket.on("end", () => console.log("客户端断开"));
    socket.write("欢迎\r\n");
});

server.listen(3000, () => console.log("TCP 服务器监听 3000"));

TCP 客户端

js
const net = require("net");
const client = net.createConnection({ port: 3000 }, () => {
    console.log("已连接");
    client.write("Hello 服务器\r\n");
});
client.on("data", data => console.log(data.toString()));
client.on("end", () => console.log("服务器关闭连接"));

2. UDP 通信(dgram 模块)

UDP 无连接,适用于低延迟、丢包容忍的场景。

UDP 服务器

js
const dgram = require("dgram");
const server = dgram.createSocket("udp4");

server.on("message", (msg, rinfo) => {
    console.log(`收到来自 ${rinfo.address}:${rinfo.port} 的消息: ${msg}`);
    server.send("已收到", rinfo.port, rinfo.address);
});

server.bind(41234, () => console.log("UDP 服务器启动 41234"));

UDP 客户端

js
const dgram = require("dgram");
const client = dgram.createSocket("udp4");
client.send("Hello UDP", 41234, "localhost", err => {
    if (err) console.error(err);
    else console.log("消息已发送");
});
client.on("message", msg => console.log("服务端回复:", msg.toString()));

四、DNS 解析:dns.lookup vs dns.resolve

dns 模块提供域名解析功能。核心区别:

方法底层是否涉及系统 DNS 缓存返回类型适用场景
dns.lookupgetaddrinfo 相同,依赖操作系统配置(/etc/hosts、系统 DNS)返回单个 IPv4/IPv6 地址(或第一项)网络连接,例如 net.createConnection 内部使用
dns.resolve直接使用 DNS 协议查询,不经过系统缓存返回完整记录数组(可指定类型如 AAAAAMX 等)获取完整 DNS 记录,或编程式排查

示例

js
const dns = require("dns");

dns.lookup("example.com", (err, address, family) => {
    console.log("lookup:", address); // 通常一个IP
});

dns.resolve4("example.com", (err, addresses) => {
    console.log("resolve4:", addresses); // IP数组
});

dns.resolveMx("gmail.com", (err, addresses) => {
    console.log("MX记录:", addresses);
});

选择建议:一般使用 dns.lookup 进行网络连接;若需要获取所有记录或特定类型(如 MX、TXT),使用 dns.resolve

总结

模块/功能核心用途常用 API
http / https构建 Web 服务器和客户端createServer, request, get, Agent
wsWebSocket 实时通信WebSocket.Server, ws.send
netTCP 流式通信createServer, createConnection
dgramUDP 数据报通信createSocket, send, bind
dns域名解析lookup, resolve 系列

掌握这些模块,可以搭建各种网络应用,从简单的 HTTP 服务到复杂的实时聊天系统。推荐结合官方文档和实际项目练习,加深理解。

相关文章

Series

core

3 / 7