近期在工作中碰到一个问题,需要建立一个TCP(或者UDP也可以)连接到另一台机器,只需要连接某个自定义的端口一下即可,连接后马上断开,不用发送任何数据,以此作为一种“心跳”的实现。实现这个需要编写一个TCP小程序放到心跳发起的机器上然后循环执行它。当然这个小程序越简单越好,于是就想到用python,然而由于机器上的软件已经被裁减了,根本就没有对python的支持,如果为此“安装”一个python,能预见的就是遇到一系列的库依赖问题,最终陷入一个泥潭。如果真这样,我还不如直接用C写一个心跳发送程序呢...最终的方案是使用telnet:
echo ^]quit |telnet 192.168.2.2 1234
这样就可以连接上之后立马断开了,可以使用,于是就采用了。很显然对端需要有一个接收程序,使用nc再简单不过了:
nc -l -p 1234
完事后,就这个问题又进一步思考了一下。万一连telnet也没有该怎么办?这次之所以用了telnet,那是凑巧了!于是就想如果能用shell内建的命令建立TCP连接该多好。google之,man之,终于发现了一个bash内建的文件:/dev/tcp[or UDP]/[IP or HostName]/port
只要打开这个文件,就能建立一个TCP或者UDP连接到[IP or HostName]的port端口,现在的问题是如何打开这个文件。想要打开这个文件,你一定要用bash内建命令打开,因为只有bash可以识别/dev/tcp...否则如果你用cat打开的话,由于cat不能识别它,就要真的去/dev路径下去寻找了,结果可想而知:no such file...
使用exec这个内建命令可以完成这个工作,exec不附带任何command以替换当前bash,而仅仅是为了打开上述的tcp文件,然后将之定向到一个空闲的文件描述符,这样我们就可以操作该文件描述符了:
1.首先重定向,建立连接:
exec 6>/dev/tcp/192.168.2.2/1234
2.然后关闭:
exec 6>&-
3.其它
在关闭之前,你可以使用下面的命令来向该连接发送数据:
echo -e "$data" >&6
在关闭之前,你可以使用下面的命令来读取该连接返回的数据:
cat <&6
以上就是使用bash内建命令exec以及内建文件名/dev/$proto/$address/$port进行网络通信的方法。美中不足的地方在于bash没有对应的文件产生TCP/UDP服务器,如果万一系统中没有nc,没有python,你就不得不动用C了,编译,复制到系统,万一出错或者库不匹配,就要重新编译,链接。但是由于bash的源码可获得,因此修改它就可以了。在动手之前首先要设计好接口。以下仅仅以TCP为例。对于客户端而言,bash的/dev/tcp/接口设计的很好,于是我们可以仿造该接口设计一个新的TCP服务器的接口用于侦听某一个端口:
/dev/tcp-server/$IP/$PORT
在bash中,只要重定向这个文件就打开一个侦听TCP,现在问题是如何接受客户端的连接并且提供服务,而且这一切还必须内置于bash,我们可以采用“读取”该TCP服务文件描述符的方式,其内容是一个工作套接字的文件描述符,或者用tail也可以:
1.创建一个侦听套接字:
exec 6>/dev/tcp-server/192.168.2.2/1234
2.等待客户端连接:
cat <&6
返回8
3.提供服务:
cat <&8
...
echo -e "response" >&8
4.主动断开连接:
exec 8>&-
5.停止服务:
exec 6>&-
echo ^]quit |telnet 192.168.2.2 1234
这样就可以连接上之后立马断开了,可以使用,于是就采用了。很显然对端需要有一个接收程序,使用nc再简单不过了:
nc -l -p 1234
完事后,就这个问题又进一步思考了一下。万一连telnet也没有该怎么办?这次之所以用了telnet,那是凑巧了!于是就想如果能用shell内建的命令建立TCP连接该多好。google之,man之,终于发现了一个bash内建的文件:/dev/tcp[or UDP]/[IP or HostName]/port
只要打开这个文件,就能建立一个TCP或者UDP连接到[IP or HostName]的port端口,现在的问题是如何打开这个文件。想要打开这个文件,你一定要用bash内建命令打开,因为只有bash可以识别/dev/tcp...否则如果你用cat打开的话,由于cat不能识别它,就要真的去/dev路径下去寻找了,结果可想而知:no such file...
使用exec这个内建命令可以完成这个工作,exec不附带任何command以替换当前bash,而仅仅是为了打开上述的tcp文件,然后将之定向到一个空闲的文件描述符,这样我们就可以操作该文件描述符了:
1.首先重定向,建立连接:
exec 6>/dev/tcp/192.168.2.2/1234
2.然后关闭:
exec 6>&-
3.其它
在关闭之前,你可以使用下面的命令来向该连接发送数据:
echo -e "$data" >&6
在关闭之前,你可以使用下面的命令来读取该连接返回的数据:
cat <&6
以上就是使用bash内建命令exec以及内建文件名/dev/$proto/$address/$port进行网络通信的方法。美中不足的地方在于bash没有对应的文件产生TCP/UDP服务器,如果万一系统中没有nc,没有python,你就不得不动用C了,编译,复制到系统,万一出错或者库不匹配,就要重新编译,链接。但是由于bash的源码可获得,因此修改它就可以了。在动手之前首先要设计好接口。以下仅仅以TCP为例。对于客户端而言,bash的/dev/tcp/接口设计的很好,于是我们可以仿造该接口设计一个新的TCP服务器的接口用于侦听某一个端口:
/dev/tcp-server/$IP/$PORT
在bash中,只要重定向这个文件就打开一个侦听TCP,现在问题是如何接受客户端的连接并且提供服务,而且这一切还必须内置于bash,我们可以采用“读取”该TCP服务文件描述符的方式,其内容是一个工作套接字的文件描述符,或者用tail也可以:
1.创建一个侦听套接字:
exec 6>/dev/tcp-server/192.168.2.2/1234
2.等待客户端连接:
cat <&6
返回8
3.提供服务:
cat <&8
...
echo -e "response" >&8
4.主动断开连接:
exec 8>&-
5.停止服务:
exec 6>&-
至于实现,对着已有的客户端实现比葫芦画瓢即可,这个不重要,重要的是如何使得接口更容易被使用,如果还没有nc方便,那么做这个就没有意义了。实际上你不能指望该方式能提供多么复杂的服务,它基本上就是一个调试网络的工具而已,这也许就是bash为何只实现了client而没有实现server的原因吧。
本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1268984