楔子
今天朋友找我解决一个问题,他的 java 服务在阿里服务器里监听了某端口(使用的 TCP),但通过公网无法连接。我尝试在本地 telnet 那个端口(跨公网),不通。在他服务器里 telnet 127.0.0.1 端口,是通的。说明服务正常监听了端口。但由于我不熟悉他的 java 服务,无法判断该 java 服务是否只监听了本地回环地址。
因此公网无法访问大概有两种可能,一,java 服务只监听了本地回环地址; 二、防火墙限制了端口
排除第二种情况对我来说比较简单,我通过 netcat 在服务器监听端口,然后在本地 telnet,不通!说明问题的确出在防火墙身上。该服务器为 Ubuntu,使用 ufw status
查看防火墙状态,显示为 inactive
,也就是关闭状态。再查看阿里云安全组规则,已经开放了 tcp/udp 的所有端口 (-1/-1)。后面我再次执行 ufw disable
,端口就通了。所以这是 ufw 的 bug?
引子有点长了。主要是为了说明,如果想知道与服务器之间的 tcp 连接是否通,可以使用 netcat 工具去判断。下面介绍下 netcat ~
netcat 是什么
是一种计算机网络实用程序,用于使用 TCP 或 UDP 读写网络连接,号称 TCP/IP 的瑞士军刀。大家一般简写为 nc
。总之,涉及到网络的事儿,它几乎都能做。为什么叫瑞士军刀,因为它体积很小,不同版本的 nc 可执行文件只有几十 k 到 300 多 k,而且大部分 linux 发行版都有预安装。
各种版本
netcat 有许多版本,能力从弱到强为:GUN、BSD、Ncat。基础功能三者都一样,不同的是一些版本额外内置了些高级 / 有趣的功能(例如 Ncat 有个参数 --chat
,可以创建匿名聊天室)。
大部分 centos6 内置的是 BSD 版本的 nc,我的 Centos7 里边内置的 nc 是 Ncat(属于 nmap 工具集里边)
使用 man nc
,拉到最下边可以看到你的 nc 版本。(使用 man 你基本能够查阅所有的用法了,其它软件也是如此,只要是标准安装的)
ps. socat 号称是更为强大的 nc,有兴趣的小伙伴可以自己去看看。
用途
检查端口通不通
以往我一直使用 telnet 来判断某个端口是否通,但 telnet 是基于 tcp 协议的,如果要判断 udp 端口就无能为力了。这时可以使用 nc 来判断。为了方便实验,本文统一使用同一台机器模拟服务端和客户端。
# 咱们使用nc创建一个tcp协议监听的端口。-l为listen,默认协议为tcp nc -l 8088 # 使用nc连上该端口。-v(verbose)用于显示更多信息,-z(zero I/O)表示不发送数据包,仅显示连接状态。 # 下面succeed表示端口是通的 nc -vz 127.0.0.1 8088 Connection to 127.0.0.1 8088 port [tcp/radan-http] succeeded! # 创建一个udp端口。-u指使用udp协议 nc -lu 8088 # 同样,加一个u表示使用udp连接该端口,同时你会看到服务端打印出若干个X。 # 不是说加了z参数就不会发数据包吗,为什么服务端收到了报文?我认为udp是无连接状态的,通过系统调用write # 往该套接字发数据,通过返回值才知道包发出去没有,从而判断udp端口是否通 nc -vzu 127.0.0.1 8088 Connection to 127.0.0.1 8088 port [udp/radan-http] succeeded!
端口扫描
# 扫描tcp端口。-w(wait)指定连接超时时间,单位秒。 # 连接成功的端口将作为标准输出 nc -v -w3 -z 127.0.0.1 1-65535 | tee succeed_port.log # 下面摘抄部分连接成功的端口 Connection to 127.0.0.1 22 port [tcp/ssh] succeeded! Connection to 127.0.0.1 25 port [tcp/smtp] succeeded! Connection to 127.0.0.1 80 port [tcp/http] succeeded! Connection to 127.0.0.1 443 port [tcp/https] succeeded! Connection to 127.0.0.1 1100 port [tcp/mctp] succeeded! Connection to 127.0.0.1 1111 port [tcp/lmsocialserver] succeeded! Connection to 127.0.0.1 2255 port [tcp/vrtp] succeeded! Connection to 127.0.0.1 3001 port [tcp/*] succeeded! Connection to 127.0.0.1 3306 port [tcp/mysql] succeeded! Connection to 127.0.0.1 4300 port [tcp/corelccam] succeeded! Connection to 127.0.0.1 4369 port [tcp/epmd] succeeded! Connection to 127.0.0.1 5672 port [tcp/amqp] succeeded! Connection to 127.0.0.1 6553 port [tcp/*] succeeded! # 扫描udp端口。nc -v -zu 127.0.0.1 1-65535 # 以下为我本地扫描结果 Connection to 127.0.0.1 636 port [udp/ldaps] succeeded! Connection to 127.0.0.1 35157 port [udp/*] succeeded! Connection to 127.0.0.1 54890 port [udp/*] succeeded!
文件传输
归根到底都是创建一个 tcp/udp 连接,通过连接收发数据而已。所以,无论数据是 ascii 字符串,二进制文件都可以发送 / 接收。
为了加快传输速率 / 加密,你甚至还可以在发送之前对数据进行压缩 / 加密。
对于大文件,个人认为还是 scp、rsync 这类专业的工具靠谱。小文件可以试试这种方法,毕竟简单,不需要 ssh 认证。
# 接收端监听端口,把数据包重定向到指定文件 nc -l 8085 > file.ext # 发送方往指定ip:端口发送数据 nc 127.0.0.1 8085 < file.ext
ssh 代理
是的,可以用作 ssh 代理!!如果某台内网机器需要通过代理才能连接,那么给 ssh 配置个代理是个不错的选择。这样 ssh 和 scp 都可以正常访问那台机器。
主要是利用了 ssh 的 ProxyCommand 选项,例如你要访问的机器 ip 为 1.2.3.4,代理 ip: 端口为 5.6.7.8:1988,那么编辑~/.ssh/config
Host 1.2.3.4 ProxyCommand nc -X 5 -x 5.6.7.8:1988 %h %p
-X 指定代理协议,默认为 5 (socks5),还有其他选项:4 (socks4)、connect (https)
-x 指定代理地址 [端口]
网络测速
接收端:
nc -l port > /dev/null
发送端:
dd if=/dev/zero bs=9100004096 count=1 | nc 接收端ip port
最后你会看到结果:
2147479552 bytes (2.1 GB) copied, 12.7978 s, 168 MB/s
其它
另外还可以做以下事情,但我觉得一般来说用处不大。ps. 有些版本的 nc 可能参数有所差异或没有对应的特性
- 远程执行 shell/bash。例子:服务端执行
nc -l 8083 -e /bin/bash(或shell)
,客户端nc 127.0.0.1 8083
,然后客户端就可以执行 bash/shell 了。但这种用法不要在公网用,否则很危险呐 - 网速测试 (已补充到本文)。
- 啊,编不出来了~
ps. 有啥实用 / 有趣的用途,欢迎留言。