使用nc命令发送网络请求的一个问题
一个众所周知的用来测试TCP连接的nc命令,大概有以下两种简单用法:
- 手动
nc host port,然后输入内容并发送 - 通过管道写入:
cat a.txt | nc host port
方法1的问题:你有可能不知道按下回车的时候程序内部究竟得到的是什么字符。
有可能是“\r(0x0D)”、有可能是“\n(0x0A)”、还有可能是……“\r\n(0x0D 0x0A)”?如果是简单地测试发送HTTP请求,大概率会成功,因为几乎所有主流Web服务器都同时支持以“\r\n”或“\n”作为换行符。但是如果你不知道是什么字符、且又在测试严格二进制格式的协议,这就非常模棱两可。
先说结论:最终得到的字符要看终端当前的stty设置。
在绝大多数Linux/MacOS终端默认配置下,按下一次回车时键盘发送的是“\r(0x0D)”。但是,终端驱动开启了“ICRNL”特性,会把收到的“\r”换成“\n(0x0A)”。因此,程序从标准输入读到的通常是“\n”。这一点可以用cat命令简单验证(按下回车后再按Ctrl-D):
1 2 3 4 5 6 |
|
改终端设置通常不是一个好的做法,所以这时候大家开始用方法2。
方法2的问题:nc在标准输入结束后可能会不等待网络另一端是否有数据就直接退出。
听起来非常离谱,但是实际上的行为也如此。比如当你执行下来的命令时会发现,nc立马就退出了:
1
|
|
这个现象在MacOS、AlpineLinux(BusyBox)上测试都是这样。前者是类OpenBSD的实现版本;后者是精简版实现,不支持任何参数。
有些地方可能告诉你应该加上-w timeout参数,但神奇的是,在我这里毫无作用。(也没有-N参数可用)
1
|
|
每当这时候,我就怀疑到底是服务器真的没有任何返回、还是我的协议输入错了然后服务器直接关了。
所以,我就只能献出睡眠大法了,怪恶心的写法:
1
|
|
结语
真是个奇怪的命令,不仅有多种实现版本不说,就连基本功能都难以满足。我已经难得去翻规范为什么要这样定义了。
以前在公司看到的运维多喜欢用telnet命令(系统一般不再自带),而不是nc。也许是这个原因?