为什么 MCP 大多用管道(stdio),很少用 socket
接着上一篇《用 30 行看懂 MCP》。很多有系统背景的人会问:
MCP 为什么用管道(stdio),而不是 socket?
先说结论:不是管道更高明,而是当前大多数 MCP 场景,恰好是管道的最优解——本机、单客户端、一次性、轻量无状态。一旦场景变成"多客户端 / 常驻 / 有状态 / 很重",就该换 socket。
MCP 是协议,传输可插拔
消息格式(JSON-RPC)和"用什么线传"是两回事:
| 传输 | 走什么 | 场景 |
|---|---|---|
| stdio | 管道(stdin/stdout) | 本地 server,客户端拉起它 |
| HTTP / SSE(Streamable HTTP) | TCP socket | 远程 / 多客户端 |
同一个协议,两种接线方式。
管道 vs socket:差在"语义重量"
| 维度 | 管道(stdio) | 本地 socket |
|---|---|---|
| 地址/名字 | ❌ 匿名 | ✅ 路径 / 127.0.0.1:port |
| 连接语义 | ❌ 就是两端 fd | ✅ listen/accept/connect |
| 客户端数 | 1:1 | 1:N |
| 生命周期 | 绑定父子进程 | 服务端可常驻 |
| 重连 / 鉴权 | ❌ / ❌ | ✅ / ✅ |
| 内核开销 | 极低 | 略高 |
注意:两者都"本地、零网络",数据路径都是"内核缓冲区 + 内存拷贝",性能非常接近——差别主要是语义,不是速度。
判据不是"本机 / 远程",而是这三问
- 几个客户端? 1 个 → stdio;多个 → socket。
- 要常驻 / 可重连吗? 要 → socket。
- 有状态吗?很重吗? 有 / 重 → socket。
有个常见误区:"服务应该无状态" → 所以第 3 条可以忽略? 不。无状态免掉的是"状态",免不掉"成本"。即使对外完全无状态,"加载一次模型 / 建一次贵连接"的物理开销依然在。若每个 stdio 客户端都 fork 一份,就是把这份成本重复付出——这是资源复用问题,跟"有没有状态"无关。
那为什么现状是 stdio 主导?
因为现在绝大多数 MCP 场景,恰好落在 stdio 的甜区:
- 桌面 AI(Claude Desktop、Cursor…)就是"拉一个本地工具"——天然 1:1;
- 工具多是无状态、轻量(读文件、查一下、跑个命令)——多份进程也无所谓;
- stdio 零网络面:不用端口、不用防火墙、免鉴权、免配置,跨平台最简单;
- 安全:进程隔离,攻击面最小。
一句话:场景匹配,不是技术优越。
什么时候就该上 socket?
- 多个 App / agent 想共用同一个工具服务;
- 服务要常驻(开机即起、崩溃重启、客户端退出不影响它);
- 工具很重(比如加载了本地模型的推理 server)——不想每个客户端重复加载;
- 需要会话状态(多轮、连着的设备/串口、流式长连接);
- 需要鉴权 / 监控 / 远程访问。
MCP 规范里没有原生的 UNIX socket 传输;想要"本地 socket 语义",就用 HTTP 传输绑
127.0.0.1(= loopback TCP,本地但不暴露),或者自己走 UNIX socket。
从边缘网关看(我的场景)
- 单一 agent 拉个轻工具 → stdio 足够,最省;
- 设备上一个常驻、贵的"设备能力服务"(agent、CLI、Web 都要用,尤其加载了模型那种)→ 别用 stdio,用本地 socket(HTTP 绑
127.0.0.1),避免"每个客户端一份进程、重复加载模型"。
一句话收尾
不是管道更好,是它正好匹配当下的大多数场景。 判断标准从来不是"本机 / 远程",而是:几个客户端、要不要常驻、有没有状态 / 成本。 场景一变,就换 socket。
📦 相关代码:github.com/zishuowang696/mcp-demo
💬 有问题或建议?在下方评论,或到 GitHub 提 Issue。
(本文中英双语;本系列记录从 0 构建 Agent 的过程。)
评论 0
还没有评论,来抢沙发~
登录 登录后即可参与评论