前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# 网络TCP net

30 秒速记

  • net 模块 = Node 里的 TCP / IPC 编程接口,http 就搭在它上面
  • TCP 是可靠、有序的字节流,没有「消息」的概念 → 一次 data 不等于一条消息
  • 拆包、粘包要应用层自己定格式:按换行分隔,或者「长度头 + 内容」
  • 写数据要看背压(write() 返回 false 就等 drain),还要处理超时、ECONNRESET、半关闭
  • 解析器要限制单帧最大长度,否则对方发个超大长度头就能把内存吃满

net 是 Node 做 TCP 编程的底层模块,理解它的关键是记住 TCP 给你的是一条字节流,不是一条条消息。 打个比方,TCP 像一根水管,你分三次倒进去三杯水,对面接到的可能是一大桶,也可能是半杯半杯的,杯子的边界没了。所以对方一次 data 事件拿到的数据,可能是半条消息,也可能是两条半,必须自己约定分隔方式。另外写得太快对方收不过来时,数据会堆在内存里,这就是背压问题。真正写 TCP 服务,这两件事加上错误和超时处理才是主要工作量。

// 按换行分帧:处理粘包 / 拆包
let buf = '';
socket.setEncoding('utf8');
socket.on('data', chunk => {
  buf += chunk;
  let i;
  while ((i = buf.indexOf('\n')) !== -1) {
    const msg = buf.slice(0, i); // 一条完整消息
    buf = buf.slice(i + 1);
    console.log('收到消息:', msg);
  }
  if (buf.length > 64 * 1024) socket.destroy(); // 防恶意超长
});
@前端进阶之旅: 代码已经复制到剪贴板

理解「网络TCP net」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 network 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

TCP 编程最容易被忽视的是「消息边界」。TCP 在传输层只保证两件事:字节不丢、顺序不乱。至于你调用了几次 write,对面就按几次收到,这个 TCP 完全不管。发送端内核可能把几次小写入合并成一个包(Nagle 算法会主动这么做),一次大写入也可能被切成好几个包,接收端每次 data 拿到的就是这一刻缓冲区里有的字节。

所以几乎所有基于 TCP 的协议都自带分帧规则。常见的有两种。一种是分隔符,比如 Redis 协议和很多文本协议用 \r\n,实现简单,但内容里不能出现分隔符,或者要转义。另一种是长度前缀,先发固定字节数的长度,再发内容,二进制协议基本都这么做,解析时攒够长度头读出长度,再攒够这么多字节才算一条。

第二件事是背压。socket.write() 返回 true 表示数据已经交给内核或者缓冲还没满,返回 false 表示用户态缓冲已经超过 highWaterMark,这时候还接着写,数据只会越堆越多。正确做法是等 drain 事件再继续,或者用 stream.pipeline() 让它自动处理。

第三件事是异常路径。ECONNRESET、EPIPE、超时、对端半关闭在公网上都是常态,每个 socket 都要监听 error,不然一条连接出错就能让整个进程退出。

面试官追问

追问 1客户端连续 write 了两次 'hello',服务端一次 data 就收到了 'hellohello',是出错了吗?
参考回答

不是,这就是粘包。TCP 只保证字节顺序,不保证你的写入边界,内核会合并发送。应用层要自己分帧,比如每条消息后面加 \n,或者前 4 个字节写长度。

追问 2那「长度头」方案具体怎么做?
参考回答

发送方先写一个 4 字节的长度再写内容:buf.writeUInt32BE(body.length);接收方攒够 4 字节读出长度,再攒够这么多字节才算一条完整消息,剩下的留给下一轮。长度要设上限,超过直接断开。

追问 3给慢客户端狂 write,进程内存一路涨,怎么回事?
参考回答

write() 返回 false 时数据已经堆在用户态缓冲里了,你还在继续写。要停下来等 drain 事件再写,或者直接用 stream.pipeline(source, socket),它会自动处理背压。

追问 4线上偶尔出现 Error: read ECONNRESET 把进程搞崩了,怎么处理?
参考回答

对方粗暴断开(发了 RST)时 socket 会触发 error,没监听 error 的话会变成未捕获异常直接崩进程。每个 socket 都要 socket.on('error', ...),记日志后清理掉这条连接就行,这种错误在公网上很正常。

追问 5自己写 TCP 协议和直接用 WebSocket / HTTP,怎么选?
参考回答

能用 HTTP 或 WebSocket 就别自己造,分帧、心跳、代理穿透都有现成方案。自定义 TCP 协议适合对接已有设备协议、Redis 这类自带协议的服务,或者对延迟和包大小特别敏感的场景。

# 模块概览

30 秒速记

  • net 是核心模块,两个主角:net.Server(监听、接连接)和 net.Socket(一条连接)
  • http.Server 继承自 net.Server,每个 HTTP 请求底下都是一个 net.Socket
  • net.Socket 是双工流(Duplex),既能读也能写,pipe 那一套都能用
  • 同一套 API 既能走 TCP(host + port),也能走本地 IPC(Unix socket 或 Windows 命名管道)
  • 写 Node 服务端绕不开它:连接数、超时、keep-alive 这些问题最后都落在 socket 上

net 模块就两样东西:net.Server 负责在端口上等连接,net.Socket 代表一条已经建立的连接。 我们平时写的 http.createServer 其实就是在 net.Server 上面加了一层 HTTP 解析,req.socket 拿到的就是底下那个 net.Socket。Socket 是个双工流,一头读一头写,所以能直接 pipe。另外它不只支持 TCP,传一个路径进去就变成本机进程间通信,Docker 的 /var/run/docker.sock 就是这种。

const http = require('http');
const net = require('net');
const server = http.createServer((req, res) => {
  console.log(req.socket instanceof net.Socket); // true
  res.end('ok');
});
console.log(server instanceof net.Server); // true
@前端进阶之旅: 代码已经复制到剪贴板

net模块是同样是nodejs的核心模块。在http模块概览里提到,http.Server继承了net.Server,此外,http客户端与http服务端的通信均依赖于socket(net.Socket)。也就是说,做node服务端编程,net基本是绕不开的一个模块。

从组成来看,net模块主要包含两部分,了解socket编程的同学应该比较熟悉了:

  • net.Server:TCP server,内部通过socket来实现与客户端的通信。
  • net.Socket:tcp/本地 socket的node版实现,它实现了全双工的stream接口。

本文从一个简单的 tcp服务端/客户端 的例子开始讲解,好让读者有个概要的认识。接着再分别介绍 net.Server、net.Socket 比较重要的API、属性、事件。

对于初学者,建议把文中的例子本地跑一遍加深理解。

面试官追问

追问 1有人说 HTTP 服务和 TCP 服务是两回事,HTTP 不用管 socket,对吗?
参考回答

不对,http.Server 就是 net.Server 的子类。HTTP 只是跑在 TCP 连接上的一套文本格式,连接超时、keep-alive、连接数上限这些问题,最后都要在 socket 层面处理。

追问 2HTTP 服务连接数一直涨不下来,路由日志看着都正常,该看哪?
参考回答

看连接层。用 server.getConnections(cb) 看当前连接数,ss -tnp 看 ESTABLISHED 的连接是谁的。常见原因是客户端开着 keep-alive 一直不关,或者有请求没 res.end(),socket 一直挂着。

追问 3本机两个进程通信,用 TCP 端口还是 Unix socket?
参考回答

只在本机的话 Unix socket 更合适,不占端口、不经过网络协议栈、还能用文件权限控制谁能连。net.createServer().listen('/tmp/app.sock') 就行,代码和 TCP 版基本一样。

追问 4要做一个设备上报的纯 TCP 服务,能套 Express 吗?
参考回答

套不了,Express 只认 HTTP。设备发的是自定义二进制协议,就直接 net.createServer,在 socket 上自己做分帧和解析,这部分没框架能帮你。

# 简单的 server+client 例子

30 秒速记

基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
      • 模块概览
      • 简单的 server+client 例子
      • 服务端 net.Server
        • server.address()
        • server.close(callback])
        • server.ref()/server.unref()
        • 事件 listening/connection/close/error
      • 客户端 net.Socket
      • API、属性归类
      • 事件简介
      • 相关链接
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

网络TCP net|Nodejs专题系列

# 网络TCP net

30 秒速记

  • net 模块 = Node 里的 TCP / IPC 编程接口,http 就搭在它上面
  • TCP 是可靠、有序的字节流,没有「消息」的概念 → 一次 data 不等于一条消息
  • 拆包、粘包要应用层自己定格式:按换行分隔,或者「长度头 + 内容」
  • 写数据要看背压(write() 返回 false 就等 drain),还要处理超时、ECONNRESET、半关闭
  • 解析器要限制单帧最大长度,否则对方发个超大长度头就能把内存吃满

net 是 Node 做 TCP 编程的底层模块,理解它的关键是记住 TCP 给你的是一条字节流,不是一条条消息。 打个比方,TCP 像一根水管,你分三次倒进去三杯水,对面接到的可能是一大桶,也可能是半杯半杯的,杯子的边界没了。所以对方一次 data 事件拿到的数据,可能是半条消息,也可能是两条半,必须自己约定分隔方式。另外写得太快对方收不过来时,数据会堆在内存里,这就是背压问题。真正写 TCP 服务,这两件事加上错误和超时处理才是主要工作量。

// 按换行分帧:处理粘包 / 拆包
let buf = '';
socket.setEncoding('utf8');
socket.on('data', chunk => {
  buf += chunk;
  let i;
  while ((i = buf.indexOf('\n')) !== -1) {
    const msg = buf.slice(0, i); // 一条完整消息
    buf = buf.slice(i + 1);
    console.log('收到消息:', msg);
  }
  if (buf.length > 64 * 1024) socket.destroy(); // 防恶意超长
});
@前端进阶之旅: 代码已经复制到剪贴板

理解「网络TCP net」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 network 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

TCP 编程最容易被忽视的是「消息边界」。TCP 在传输层只保证两件事:字节不丢、顺序不乱。至于你调用了几次 write,对面就按几次收到,这个 TCP 完全不管。发送端内核可能把几次小写入合并成一个包(Nagle 算法会主动这么做),一次大写入也可能被切成好几个包,接收端每次 data 拿到的就是这一刻缓冲区里有的字节。

所以几乎所有基于 TCP 的协议都自带分帧规则。常见的有两种。一种是分隔符,比如 Redis 协议和很多文本协议用 \r\n,实现简单,但内容里不能出现分隔符,或者要转义。另一种是长度前缀,先发固定字节数的长度,再发内容,二进制协议基本都这么做,解析时攒够长度头读出长度,再攒够这么多字节才算一条。

第二件事是背压。socket.write() 返回 true 表示数据已经交给内核或者缓冲还没满,返回 false 表示用户态缓冲已经超过 highWaterMark,这时候还接着写,数据只会越堆越多。正确做法是等 drain 事件再继续,或者用 stream.pipeline() 让它自动处理。

第三件事是异常路径。ECONNRESET、EPIPE、超时、对端半关闭在公网上都是常态,每个 socket 都要监听 error,不然一条连接出错就能让整个进程退出。

面试官追问

追问 1客户端连续 write 了两次 'hello',服务端一次 data 就收到了 'hellohello',是出错了吗?
参考回答

不是,这就是粘包。TCP 只保证字节顺序,不保证你的写入边界,内核会合并发送。应用层要自己分帧,比如每条消息后面加 \n,或者前 4 个字节写长度。

追问 2那「长度头」方案具体怎么做?
参考回答

发送方先写一个 4 字节的长度再写内容:buf.writeUInt32BE(body.length);接收方攒够 4 字节读出长度,再攒够这么多字节才算一条完整消息,剩下的留给下一轮。长度要设上限,超过直接断开。

追问 3给慢客户端狂 write,进程内存一路涨,怎么回事?
参考回答

write() 返回 false 时数据已经堆在用户态缓冲里了,你还在继续写。要停下来等 drain 事件再写,或者直接用 stream.pipeline(source, socket),它会自动处理背压。

追问 4线上偶尔出现 Error: read ECONNRESET 把进程搞崩了,怎么处理?
参考回答

对方粗暴断开(发了 RST)时 socket 会触发 error,没监听 error 的话会变成未捕获异常直接崩进程。每个 socket 都要 socket.on('error', ...),记日志后清理掉这条连接就行,这种错误在公网上很正常。

追问 5自己写 TCP 协议和直接用 WebSocket / HTTP,怎么选?
参考回答

能用 HTTP 或 WebSocket 就别自己造,分帧、心跳、代理穿透都有现成方案。自定义 TCP 协议适合对接已有设备协议、Redis 这类自带协议的服务,或者对延迟和包大小特别敏感的场景。

# 模块概览

30 秒速记

  • net 是核心模块,两个主角:net.Server(监听、接连接)和 net.Socket(一条连接)
  • http.Server 继承自 net.Server,每个 HTTP 请求底下都是一个 net.Socket
  • net.Socket 是双工流(Duplex),既能读也能写,pipe 那一套都能用
  • 同一套 API 既能走 TCP(host + port),也能走本地 IPC(Unix socket 或 Windows 命名管道)
  • 写 Node 服务端绕不开它:连接数、超时、keep-alive 这些问题最后都落在 socket 上

net 模块就两样东西:net.Server 负责在端口上等连接,net.Socket 代表一条已经建立的连接。 我们平时写的 http.createServer 其实就是在 net.Server 上面加了一层 HTTP 解析,req.socket 拿到的就是底下那个 net.Socket。Socket 是个双工流,一头读一头写,所以能直接 pipe。另外它不只支持 TCP,传一个路径进去就变成本机进程间通信,Docker 的 /var/run/docker.sock 就是这种。

const http = require('http');
const net = require('net');
const server = http.createServer((req, res) => {
  console.log(req.socket instanceof net.Socket); // true
  res.end('ok');
});
console.log(server instanceof net.Server); // true
@前端进阶之旅: 代码已经复制到剪贴板

net模块是同样是nodejs的核心模块。在http模块概览里提到,http.Server继承了net.Server,此外,http客户端与http服务端的通信均依赖于socket(net.Socket)。也就是说,做node服务端编程,net基本是绕不开的一个模块。

从组成来看,net模块主要包含两部分,了解socket编程的同学应该比较熟悉了:

  • net.Server:TCP server,内部通过socket来实现与客户端的通信。
  • net.Socket:tcp/本地 socket的node版实现,它实现了全双工的stream接口。

本文从一个简单的 tcp服务端/客户端 的例子开始讲解,好让读者有个概要的认识。接着再分别介绍 net.Server、net.Socket 比较重要的API、属性、事件。

对于初学者,建议把文中的例子本地跑一遍加深理解。

面试官追问

追问 1有人说 HTTP 服务和 TCP 服务是两回事,HTTP 不用管 socket,对吗?
参考回答

不对,http.Server 就是 net.Server 的子类。HTTP 只是跑在 TCP 连接上的一套文本格式,连接超时、keep-alive、连接数上限这些问题,最后都要在 socket 层面处理。

追问 2HTTP 服务连接数一直涨不下来,路由日志看着都正常,该看哪?
参考回答

看连接层。用 server.getConnections(cb) 看当前连接数,ss -tnp 看 ESTABLISHED 的连接是谁的。常见原因是客户端开着 keep-alive 一直不关,或者有请求没 res.end(),socket 一直挂着。

追问 3本机两个进程通信,用 TCP 端口还是 Unix socket?
参考回答

只在本机的话 Unix socket 更合适,不占端口、不经过网络协议栈、还能用文件权限控制谁能连。net.createServer().listen('/tmp/app.sock') 就行,代码和 TCP 版基本一样。

追问 4要做一个设备上报的纯 TCP 服务,能套 Express 吗?
参考回答

套不了,Express 只认 HTTP。设备发的是自定义二进制协议,就直接 net.createServer,在 socket 上自己做分帧和解析,这部分没框架能帮你。

# 简单的 server+client 例子

← 4.6 网络服务 https4.8 网络UDP dgram →

fe