Node.js Net 模块(实战指南)

Node.js Net 模块:从零构建网络通信应用

在现代 Web 开发中,网络通信是绕不开的核心能力。无论是构建 API 服务、实时聊天系统,还是自定义协议的通信工具,背后都离不开底层网络编程的支持。Node.js 作为 JavaScript 的服务器端运行环境,提供了强大的原生模块来处理网络通信,其中最基础、最核心的模块就是 Node.js Net 模块

这个模块让你能够轻松创建 TCP 服务器和客户端,实现点对点的数据传输。它不像 Express 那样封装得高高在上,而是直接暴露底层的连接机制,适合想深入理解网络通信原理的开发者。今天,我们就来手把手带你掌握 Node.js Net 模块 的核心用法,从最简单的“Hello World”连接开始,逐步构建一个完整的通信系统。


什么是 Node.js Net 模块?

简单来说,Node.js Net 模块 提供了创建 TCP 服务器和客户端的能力。TCP 是传输控制协议,它保证了数据在传输过程中的可靠性——不会丢失、不会乱序,是大多数网络应用的基础。

你可以把 TCP 比作一条高速公路,而 Net 模块就是你用来修建这条高速路的工具包。你只需要告诉它“我要从哪个出口进出”(端口),它就会帮你建立连接、发送数据、接收响应。

这个模块是 Node.js 的内置模块,无需安装,直接导入即可使用:

const net = require('net');

它不依赖任何第三方库,性能极高,适合构建高性能、低延迟的网络服务。


创建一个 TCP 服务器:从监听端口开始

让我们先来创建一个最基础的 TCP 服务器。它的任务很简单:监听某个端口,当有客户端连接时,就回送一条欢迎消息。

// server.js
const net = require('net');

// 创建一个 TCP 服务器实例
const server = net.createServer((socket) => {
  // 当有客户端连接时,这个回调函数会被触发
  console.log('客户端已连接,远程地址:', socket.remoteAddress, ',端口:', socket.remotePort);

  // 向客户端发送欢迎信息
  socket.write('欢迎连接到 Node.js Net 服务器!\n');

  // 监听客户端发来的数据
  socket.on('data', (data) => {
    console.log('收到客户端消息:', data.toString());
    // 回复客户端
    socket.write('服务器已收到:' + data.toString());
  });

  // 客户端断开连接时
  socket.on('end', () => {
    console.log('客户端已断开连接');
  });
});

// 启动服务器,监听 3000 端口
server.listen(3000, '127.0.0.1', () => {
  console.log('TCP 服务器正在监听 3000 端口,地址:127.0.0.1');
});

代码详解

  • net.createServer():创建一个 TCP 服务器,参数是一个回调函数,当有新连接时调用。
  • socket:代表一个客户端连接的“通道”,你可以通过它发送和接收数据。
  • socket.write():向客户端发送数据(注意:必须是 Buffer 或字符串)。
  • socket.on('data', ...):监听客户端发来的数据,数据以 Buffer 形式传入,需用 .toString() 转为字符串。
  • socket.on('end'):客户端主动断开连接时触发。
  • server.listen(3000, '127.0.0.1'):让服务器开始监听 3000 端口。

运行这个服务器后,你可以用命令行工具测试连接:

telnet 127.0.0.1 3000

你会看到服务器返回欢迎语,并可以输入任意内容,服务器会原样回复。


创建 TCP 客户端:主动连接服务器

服务器建好了,接下来我们写一个客户端去连接它。客户端的作用是主动发起连接,发送请求,接收响应。

// client.js
const net = require('net');

// 创建一个 TCP 客户端
const client = new net.Socket();

// 连接到服务器(IP 和端口)
client.connect(3000, '127.0.0.1', () => {
  console.log('成功连接到服务器');
  // 连接成功后,发送一条消息
  client.write('你好,服务器!我是客户端。\n');
});

// 监听服务器发来的数据
client.on('data', (data) => {
  console.log('服务器回复:', data.toString());
});

// 客户端关闭连接时
client.on('close', () => {
  console.log('客户端已关闭连接');
});

// 连接出错时
client.on('error', (err) => {
  console.error('连接错误:', err.message);
});

代码说明

  • new net.Socket():创建一个客户端套接字。
  • client.connect(3000, '127.0.0.1'):主动连接到服务器的 3000 端口。
  • client.write():发送数据。
  • client.on('data'):接收服务器返回的数据。
  • client.on('close'):连接关闭时触发。
  • client.on('error'):连接失败时的错误处理。

运行客户端代码后,你会看到服务器收到消息,客户端也收到回复。整个通信过程完成。


多客户端支持:服务器如何应对并发?

上面的例子只支持单个客户端。但现实场景中,服务器要同时处理多个客户端连接。Net 模块天生支持多客户端并发,因为每个连接都会生成一个独立的 socket 实例。

我们来改写服务器代码,让其支持多个客户端:

const net = require('net');

const server = net.createServer((socket) => {
  const clientAddr = `${socket.remoteAddress}:${socket.remotePort}`;
  console.log('新客户端连接:', clientAddr);

  // 发送欢迎信息
  socket.write(`欢迎连接,你是第 ${server.clients.length} 个客户端!\n`);

  // 监听消息
  socket.on('data', (data) => {
    const msg = data.toString().trim();
    console.log(`来自 ${clientAddr} 的消息:${msg}`);

    // 广播消息给所有客户端(除了发送者)
    server.clients.forEach((client) => {
      if (client !== socket) {
        client.write(`[广播] ${clientAddr} 说:${msg}\n`);
      }
    });
  });

  // 客户端断开
  socket.on('end', () => {
    console.log('客户端断开:', clientAddr);
  });

  // 错误处理
  socket.on('error', (err) => {
    console.error('客户端连接错误:', err.message);
  });
});

// 设置最大连接数
server.maxConnections = 10;

// 启动服务器
server.listen(3000, '127.0.0.1', () => {
  console.log('TCP 服务器已启动,监听 3000 端口');
});

// 可选:监控连接数
server.on('connection', (socket) => {
  console.log('当前连接数:', server.connections);
});

关键点解析

  • server.clients:一个数组,保存所有连接的客户端 socket。
  • server.connections:当前连接数(只读)。
  • server.maxConnections:最大并发连接数,超过后拒绝新连接。
  • 广播机制:遍历所有 server.clients,向除发送者外的每个客户端发送消息。

现在,你可以用多个 telnet 连接测试,每个客户端发送的消息都会被广播给其他所有人。


数据格式与协议设计:如何让通信更高效?

在真实项目中,你不会只发“你好”这种简单消息。你需要定义通信协议,比如 JSON 格式、自定义分隔符等。

我们来设计一个简单的消息协议:每条消息以 \n 结尾,内容为 JSON 格式。

服务器端处理 JSON 消息

server.on('connection', (socket) => {
  socket.on('data', (chunk) => {
    // 将 Buffer 拆分为多条消息(按换行符分隔)
    const messages = chunk.toString().split('\n');
    messages.forEach((msg) => {
      if (msg.trim() === '') return; // 跳过空消息
      try {
        const data = JSON.parse(msg);
        console.log('解析成功:', data);

        // 根据消息类型处理
        if (data.type === 'chat') {
          server.clients.forEach((client) => {
            if (client !== socket) {
              client.write(JSON.stringify({
                type: 'chat',
                from: data.from,
                message: data.message,
                time: new Date().toISOString()
              }) + '\n');
            }
          });
        } else if (data.type === 'ping') {
          socket.write(JSON.stringify({ type: 'pong', time: new Date().toISOString() }) + '\n');
        }
      } catch (e) {
        console.error('JSON 解析失败:', msg);
      }
    });
  });
});

客户端发送 JSON 消息

client.write(JSON.stringify({ type: 'chat', from: 'Alice', message: 'Hello everyone!' }) + '\n');
client.write(JSON.stringify({ type: 'ping' }) + '\n');

这样,通信就变得结构清晰、可扩展性强。


实用技巧与最佳实践

技巧 说明
使用 socket.setTimeout() 设置连接超时时间,防止长时间无响应
及时关闭 socket 避免内存泄漏,尤其是大量连接时
使用 server.close() 平滑关闭服务器,等待连接完成
错误处理要全面 socket.on('error')server.on('error') 都要监听
数据分包处理 大数据需手动拆包,避免粘包问题

总结

Node.js Net 模块 是构建网络应用的基石。它虽然不像 Express 那样“高大上”,却提供了最纯粹、最灵活的网络控制能力。无论是实现一个简单的聊天服务器,还是开发高性能的内部通信协议,Net 模块都能胜任。

通过本文的学习,你已经掌握了:

  • 如何创建 TCP 服务器与客户端
  • 如何处理多客户端并发连接
  • 如何设计简单的通信协议
  • 如何进行错误处理和资源管理

下一步,你可以尝试:

  • 用 Net 模块实现一个简单的文件传输工具
  • 构建一个基于 TCP 的命令行调试工具
  • 与 WebSocket 结合,实现混合通信方案

网络通信是编程能力的重要体现。掌握 Node.js Net 模块,你就拥有了“直连底层”的能力,真正理解了“数据是如何从一台机器跑到另一台的”。这不仅是技术,更是一种思维的提升。