☰
macOS telnet被移除:nc、ncat、socat替代与端口调试
2026/10/1 1:49:40 网站建设 项目流程

1. macOS 上的 telnet 去哪了:先把这件事说透

如果你在两台机器之间调试过 TCP 服务,或者要确认某台网络设备的 23 端口是不是通的,你大概率敲过telnet。但在 macOS 上敲这个命令,十有八九会得到一句zsh: command not found: telnet。很多人第一反应是"系统坏了"或者"路径没配好",其实都不是——macOS 从 High Sierra 开始就不再预装 telnet 客户端了,这是有意为之,不是 bug。

这个变化影响的其实是三类人:一类是做后端和运维的,平时用 telnet 探端口、手工发 HTTP 或 SMTP 报文;一类是搞嵌入式、做网络设备调试的,需要和串口服务器、打印服务器、交换机这类设备做文本交互;还有一类就是纯好奇、想复现教程里命令的普通用户。这篇内容就是给这三类人写的,我会把 macOS 上 telnet 的可用替代方案一条条拆开,讲清楚每个工具适合什么场景、参数怎么填、有哪些坑,最后给出可以直接抄的脚本。

先建立一个认知:telnet 这个命令本身能做的事情,其实被拆到了好几个工具里。它不只是"连 23 端口"这么简单,老一代工程师用它做了很多事——探测任意 TCP 端口是否开放、手工构造应用层协议报文、做收发测试、给设备做半交互式调试。理解了这一点,你才能明白为什么替代方案不是一个工具,而是一组工具。

2. 为什么会被移除:理解背后的取舍

2.1 明文协议这件事,绕不过去

telnet 从设计之初就是明文传输。你输入的账号、口令、敲下的每一个字符,在网络链路上都是公开可见的,中间任何一跳的抓包都能还原出完整会话。它诞生的年代,网络还局限在实验室内,安全问题不是优先项。但当这套协议被用在跨网络环境里,风险就变得不可接受。

相比之下,SSH 在同一场景下提供了加密通道、主机指纹校验、密钥认证。所以整个行业的方向很明确:能上 SSH 就上 SSH,telnet 只在隔离的、可信的本地网络里,用于那些根本没有 SSH 支持的哑设备。

2.2 苹果的做法:不删协议,只是不预装

需要注意一个细节:macOS 并没有从系统里"拔掉" telnet 的能力,它只是不再把客户端二进制放进/usr/bin。telnet 所依赖的 TCP 栈、解析库都还在系统里。这意味着你完全可以通过包管理器把客户端装回来,只是苹果把这个选择权交还给了用户——想要就自己装,装了之后风险自负。

这个思路其实挺合理。系统预装一个默认就会发明文口令的工具,对普通用户来说是不必要的风险面;而对确实需要的专业用户,一条安装命令的成本可以忽略不计。

2.3 你真正需要的,其实不是 telnet

这是我想强调的核心观点。大多数人喊"我要 telnet",实际需求可以归成下面几类:

  • 确认某个 IP 的某个 TCP 端口是不是通的;
  • 手工往一个 TCP 服务里发几行文本,看看它回什么;
  • 做 HTTP、SMTP、Redis 这类文本协议的手工调试;
  • 和一台老设备保持一个半交互式的命令行会话。

这四类需求,对应的是不同的最佳工具。硬要用一个 telnet 全包,反而是效率最低的做法。下面我把替代方案按"接近 telnet 的程度"从高到低排开。

3. 替代方案全景:五个工具各自的位置

3.1 nc(netcat):手感最接近的那个

netcat 通常被称为"TCP/IP 的瑞士军刀",它最基础的能力就是建立一条到目标 IP 和端口的 TCP 连接,然后把标准输入送过去、把收到的数据打出来。这跟 telnet 的核心行为几乎一样。

macOS 自带的nc是 Apple 定制版本,位于/usr/bin/nc,不需要额外安装,开箱即用。这一点非常关键——当你只想快速验证一下端口,不需要装任何东西。

最简单的用法:

nc -v 192.168.1.1 80

-v打开详细输出,会告诉你连接成功还是失败。连上之后你敲的字会被发到对端,对端返回的内容会直接显示在终端里。

如果只想判断端口通不通、不想进入交互,用-z:

nc -vz 192.168.1.1 443

-z是"零 I/O 模式",只做连接建立和关闭,不发送任何数据。这个用法是我日常用得最多的,比 telnet 那种还要手动退出的方式干净得多。

3.2 用 Apple nc 的正确姿势:-G 和 -w 别搞混

这里有个真实的坑,很多教程写错。Apple 版本的 nc 里,连接超时用的是-G,会话空闲超时用的是-w。

  • -G timeout:控制 TCP 连接建立的超时秒数。目标主机不可达、被防火墙丢包时,如果不设这个,你可能要等系统默认的几十秒才返回。
  • -w timeout:连接建立之后,空闲多少秒自动断开。

所以在做批量扫描时,写法应该是:

nc -vz -G 2 192.168.1.1 22

而不是只写-w 2。我见过太多次有人只加-w,结果遇到不通的 IP 时脚本卡住不动,就是因为-w在 Apple nc 里对"正在建立连接"这个阶段不生效。

注意:如果你用的是通过 Homebrew 安装的 GNU netcat(包名netcat),超时参数就变成了-w,语义跟 Apple 版不同。两个 nc 混用是排查问题时最容易被绕进去的地方,先用which nc和nc -h确认版本。

3.3 ncat:Nmap 家的增强版

ncat 是 Nmap 项目维护的 netcat 重写版,特点是支持 SSL/TLS、支持代理、支持连接代理链,行为比传统 nc 更规范。安装方式:

brew install nmap

装完之后 ncat 是一个独立命令。它最实用的两个场景:

一是带 TLS 的连接。传统 nc 无法直接对 TLS 服务做手工交互,ncat 可以:

ncat --ssl example.com 443

它会自动完成 TLS 握手,你之后输入的内容就是加密通道里的明文,极大方便调试 HTTPS 服务。

二是稳定的监听与转发行为。nc 各家实现对于"监听端断开后是否继续等待"处理不一致,ncat 默认行为更可预测,做临时中转服务时更省心。

3.4 socat:把任意两种数据流对接起来

socat 的定位比 nc 更进一步,它把"两个数据端点"抽象出来,然后做双向转发。用它替代 telnet 的最简写法:

socat - TCP:192.168.1.1:23

前面的-表示标准输入输出。连上之后,你的键盘输入进 TCP,TCP 返回的数据打到屏幕上,体验和 telnet 基本一致。

socat 真正的价值在于复杂场景,比如:把本地某个端口转发到远端设备、把串口数据转到 TCP、给一条连接加上日志记录。举个日志的例子:

socat -v - TCP:192.168.1.1:23 2> session.log

-v会把双向数据流都打上方向标记写进日志,事后回看会话过程非常方便。这是 telnet 完全给不了的能力。

3.5 真要把 telnet 装回来:两种方式

如果你就是习惯 telnet 的交互,或者要复现某篇文档里的命令,装回来是最省事的。

第一种,Homebrew 单独装:

brew install telnet

第二种,装一整套传统网络工具:

brew install inetutils

inetutils会把 telnet、ftp、tftp、rsh 等一批经典工具一起装上。装完后命令在 Homebrew 的bin目录里,直接在终端调用即可。

注意:装回来的 telnet 依然是明文协议版。如果你的使用场景涉及账号口令,请在受控的、隔离的本地网络里使用,或者改用 SSH。这一点没有折中余地。

3.6 场景专用工具:别忘了几位"专科医生"

有些调试任务,其实根本不需要通用 TCP 工具:

  • HTTP 调试:curl -v http://host:port/path比手工敲报文高效得多;curl -v telnet://host:port甚至可以直接用 telnet 协议连一个端口,只用来看握手和响应。
  • TLS 服务:openssl s_client -connect host:443 -servername host,能看到证书链、协商的加密套件,这是任何 nc 都做不到的。
  • SSH 服务:直接ssh -p 2222 user@host,比先 telnet 试探再切 SSH 少一步。

把这些工具和 nc 组合起来,日常九成以上的调试需求都能覆盖。

4. 实操:把常见 telnet 用法逐个换掉

4.1 端口连通性检测:三种写法对比

这是最高频的需求。给一个直观的对比:

需求推荐命令说明
单端口快速探测nc -vz -G 2 host 443自带,无需安装,输出清晰
不想看详细输出nc -z host 443; echo $?返回码 0 表示通,非 0 表示不通
带 TLS 的端口探测ncat --ssl host 443需要 nmap,能验证 TLS 层

用返回码判断这一点特别值得展开。nc -z在连接成功时退出码是 0,失败是非 0,这意味着你可以把它直接塞进 shell 的条件判断:

if nc -z -G 2 192.168.1.1 6379; then echo "redis 端口可达" else echo "redis 端口不可达" fi

这段逻辑在写巡检脚本时几乎是必备的。相比 telnet 那种"连上要看输出、不通要等超时"的体验,nc -z配合-G把整个过程压缩到了秒级。

4.2 手工发送应用层报文:以 Redis 为例

Redis 用的是文本协议,非常适合手工敲。假设你要确认一个 Redis 实例能不能正常响应:

nc 192.168.1.1 6379

连上之后输入:

PING

如果服务正常,会返回:

+PONG

再试一条:

INFO server

会返回一大段服务器信息。这一整套流程和当年用 telnet 完全一样,换成 nc 之后没有任何学习成本。

需要注意一点:Redis 6 之后可能需要先认证,输入AUTH yourpassword即可。但如果你只是想确认端口和基本响应,PING通常就够。

4.3 手工发 HTTP 请求:用 nc 走一遍原始报文

虽然 curl 更方便,但用 nc 手工发 HTTP 请求能帮你真正理解协议长什么样,排查一些诡异的响应问题时特别有用。

nc -v example.com 80

连上后输入(注意 HTTP/1.1 必须带 Host 头,否则很多服务器会直接返回 400):

GET / HTTP/1.1 Host: example.com Connection: close

最后那个空行不能少,它是请求头和请求体的分隔符。输入完成后按回车,服务器就会返回响应头加响应体。Connection: close的作用是让服务器在响应结束后主动关闭连接,这样 nc 会自动退出,不用你手动断。

提示:在 nc 里输入时,如果终端没有回显,是正常的——nc 的默认行为是把输入直接送走,不回显。如果你需要看到自己敲的内容,可以配合rlwrap来包一层,后面会讲。

4.4 邮件服务调试:SMTP 手工会话

SMTP 交互是 telnet 的经典用途之一。用 nc 替换:

nc -v mail.example.com 25

连接成功后依次输入:

EHLO myhost.local

服务器返回支持的扩展列表。然后:

MAIL FROM:<sender@example.com> RCPT TO:<recipient@example.com> DATA

输入DATA之后可以开始写正文,以单独一行的一个点.结束。整个过程中每一步服务器都会回一个三位数字的状态码,看到250就是成功,5xx就是被拒绝。

这个流程在排查"为什么邮件发不出去"时非常有用,因为你绕过了所有客户端封装,看到的是服务器最原始的反应。

4.5 与网络设备做文本交互的注意事项

有一类设备只提供基于文本的命令行接口,比如某些打印服务器、串口服务器、老的交换设备。这类交互用 nc 或 socat 都能做:

socat -,raw,echo=0 TCP:192.168.1.50:23

raw关掉了终端的行缓冲和特殊字符处理,echo=0关掉本地回显,让设备的回显成为唯一的显示来源。这两个参数是关键——不加的话,你敲一个字符可能被本地终端和远端设备各回显一次,看起来就是一片乱码。

需要明确的是:只应对你有管理权限的设备做这类连接。生产环境里的设备调试应该走厂商提供的正规管理通道和凭据体系,而不是试图绕过认证。这一点不是技术建议,是职业底线。

4.6 批量端口巡检脚本

把前面的东西拼起来,就是一个能直接用的巡检脚本。它会遍历一份主机列表和端口列表,输出哪些通、哪些不通,并且用颜色区分。脚本用 bash 写,兼容 macOS 自带版本:

#!/bin/bash # 用法: ./portcheck.sh hosts.txt ports.txt HOST_FILE=${1:-hosts.txt} PORT_FILE=${2:-ports.txt} while read -r host; do [ -z "$host" ] && continue while read -r port; do [ -z "$port" ] && continue if nc -z -G 2 "$host" "$port" 2>/dev/null; then printf "[OK] %s:%s\n" "$host" "$port" else printf "[FAIL] %s:%s\n" "$host" "$port" fi done < "$PORT_FILE" done < "$HOST_FILE"

-G 2保证单个不可达目标最多等 2 秒,避免整批任务被一个坏 IP 拖死。如果要并发跑,可以把内层循环改成后台执行,但那样输出顺序会乱,需要重定向到各自的文件再汇总。对于几十个目标的量级,串行跑完全够用,没必要为了快一点引入复杂度。

5. 踩坑实录:这些坑我都替你踩过了

5.1 自带 nc 和 GNU netcat 的参数不通用

这是最高频的困惑。macOS 自带的是 Apple 版 nc,它不支持-c、-e这类 GNU 扩展参数,超时语义也和 GNU 版不同。表现就是:你从网上抄了一条命令,在别人机器上跑得好好的,在你这里报invalid option。

排查手段很简单:

which -a nc nc -h 2>&1 | head -20

which -a会把 PATH 里所有的 nc 都列出来,你能看到到底是/usr/bin/nc还是 Homebrew 装的版本生效了。nc -h输出的选项列表就是最权威的参考,比任何博客都准。

提示:如果你确实需要 GNU 语义,可以装完后用绝对路径调用,或者把 Homebrew 的版本重命名为gnc做别名,避免和系统版本打架。

5.2-z加-w在 Apple nc 上可能不生效

前面提过,Apple nc 的连接超时是-G。但还有一个更隐蔽的问题:-z模式和-w组合时,某些 macOS 版本上-w对连接建立阶段没有任何作用。结果就是脚本遇到一个不存在的主机会卡住十几秒。

解决办法只有两个:一是始终用-G控制连接超时;二是在脚本外层加timeout命令兜底。但 macOS 默认没有timeout,需要brew install coreutils之后用gtimeout,或者在脚本里自己实现一个基于后台进程和kill的超时函数。我现在的习惯是统一用-G,简单可靠。

5.3 连上之后退不出来

用 nc 连上一个服务,如果对面不主动断开,你会发现自己敲什么都没反应,Ctrl+C会直接杀掉整个终端里的当前进程,Ctrl+D在某些情况下也不管用。

标准做法是:Ctrl+C其实是可以的,它发 SIGINT 给 nc 进程,进程退出。如果你希望更礼貌地关闭连接,可以先按Ctrl+D关闭标准输入,很多服务检测到对端关闭后会自动断开。真正的坑在于:如果你在socat里没有加-t参数,关闭时可能等待一段时间才退出,加个-t 1能让它一秒内退出。

判断当前是不是卡住了,看终端光标是否还在动。如果连输入都不回显,基本就是连接已经断开但进程还没回收,Ctrl+C一次即可。

5.4 输入没有回显,打错字也看不见

这是新用户最不适应的地方。nc 默认不回显你输入的内容,因为它的设计假设是"你输入的就是要发送的字节"。

解决方法是用rlwrap包一层,它给任意命令加上行编辑能力:

brew install rlwrap rlwrap nc 192.168.1.1 6379

这样你就有上下键翻历史、左右键改错字、Ctrl+A/E跳行首行尾的能力。对经常做手工协议调试的人来说,这个包装几乎是刚需。

5.5 常见问题速查表

现象可能原因处理方式
command not found: telnet系统不再预装用nc或brew install telnet
nc卡住不返回连接超时未设置加-G 2
输入不回显、无法编辑nc 默认行为用rlwrap nc包装
中文显示乱码终端编码或 raw 模式未开检查 locale,socat 加raw
invalid option参数与 nc 版本不匹配nc -h确认,区分 Apple/GNU
连上后无响应服务要求先发数据或认证手工发PING、EHLO等探测

6. 效率层面的一些个人做法

6.1 别名和小函数

我终端里长期挂着一个函数,叫port,用来快速探端口:

port() { nc -vz -G 2 "$1" "$2"; }

用的时候port 192.168.1.1 443,比每次敲完整命令快得多。这种小函数的价值在于把常用参数固化下来,避免每次都要回忆-G还是-w。

另一个常用的是tcp函数,用来做半交互连接:

tcp() { rlwrap -q " " nc "$1" "$2"; }

rlwrap的-q参数用来指定引号字符,避免它对某些输入做特殊处理,实测下来这个细节能省掉一些奇怪的转义问题。

6.2 zsh 下的 /dev/tcp 等价物

网上流传的bash写法echo > /dev/tcp/host/port在 macOS 上不可用,因为系统自带的 bash 3.2 编译时没有开启网络重定向特性。这是一个非常容易被误导的点,很多教程直接把 Linux 的写法搬过来,在 macOS 上必然失败。

zsh 提供了自己的实现,需要先加载模块:

zmodload zsh/net/tcp ztcp example.com 80

成功会返回一个文件描述符编号,然后用print -u $fd "GET / HTTP/1.0"往这个 fd 写数据,用sysread读响应。这套东西适合写脚本时用,日常调试还是 nc 更顺手。

6.3 把会话记录下来

调试时最怕的是"刚才那个响应到底是啥样",所以我现在默认会开启记录。用script命令可以记录整个终端会话:

script -q session.log

之后的所有终端输出都会同步写入session.log,包括你敲的命令和程序的响应。调试完exit退出,日志就留在那里了。对需要事后复盘或者提交问题报告的场景,这个比截图专业得多。

6.4 关于交互式体验的一点取舍

有人会问,既然 ssh 这么好用,为什么还要折腾这些文本协议工具。我的答案是:两者的适用面根本不同。ssh 解决的是"有一台能登录的机器",而 nc/socat 解决的是"有一个能说话的端口"。一台 Redis、一个 MQTT broker、一台老打印服务器,它们没有 ssh 服务,只有裸的 TCP 端口。这时候你能依靠的,就是这类原始工具。

理解了这一点,你就不会再纠结"为什么不用 ssh 代替一切"这种问题——它们本来是两条平行线,各管一段。我个人的习惯是:只要对方有 ssh,绝不碰文本协议;一旦对方只有 TCP 端口,立刻切到 nc 或 socat,先把端口和基本响应确认清楚,再决定下一步怎么深入。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询