超越 “Hello World”

偶尔学习一种新的编程语言是件好事,但不能仅止步于 “Hello World"。

时常学习一种新的编程语言对你有好处,即使这种语言不会流行起来或者已经过时。使用新的语言处理旧的问题会促使你重新思考你当前处理问题的视角、方法和习惯。

我喜欢尝试新鲜的事物,特别是编程语言。但是,当你用新的语言实现了“你好,世界!”或者斐波那契序列之后,通常你会感到基本上再没什么可做的,没有任何新奇的地方。你可以试着实现埃拉托斯特尼筛法,借此探索一点数据结构和算法性能。但是我想要一些实际的东西,可能以后还会被复用。因此,不久前我自己创造了一个问题,这个问题可以帮助我仅用几百行代码去熟悉一种语言。

问题涉及了一种语言几个非常重要的方面:字符串,文件和网络输入输出,当然还有并行。称这个问题为 TCP/IP 代理(或者你可以称它 网络调试器)。问题的想法:你有一个TCP/IP侦听器(单线程或多线程)在给定的端口接受连接,当它接受到连接(调用者)时,它必须连接另外一台主机(远程主机),并且在调用者和远程主机间全双工传输数据。另外,代理还可以使用各种格式的日志记录通信内容,用来帮助分析数据。

我不再计算需要使用这种工具的场合。任何时候,只要涉及到网络编程,这种工具必不可少。我已经用不同的语言实现了这种工具很多次:C,C++,Perl,PHP。最近的两个实现用的是Python和Erlang。这种工具代表了我正在寻找的那种实际问题。

我可以指定更具体的需求。应用必须能够同时服务多个连接。对于每个连接,它需要以三种方式记录数据:一个以十六进制格式导出的表示双向顺序数据的导出日志文件;两个二进制日志文件分别记录输入和输出数据流。

这篇文章我将用Go语言来实现这个程序。Go语言的作者声称Go语言血脉中就支持并行和多线程。我打算让它们名符其实。

如果借助boost库使用C++开发这个程序的话,我很可能选择主侦听器线程加上用于服务每个连接的线程。由此,一个单独的连接将完全占用一个线程。

下面是用Go语言实现的程序中用来服务每个连接的线程:

1. 一个双向十六进制导出器线程

2. 两个线程以二进制格式记录输入和输出数据流

3. 两个线程用来在本地和远端主机间双向传输数据

总共5个线程。

再强调一遍,五个线程用来服务每一个单独的连接。我实现所有这些线程不是因为多线程本身的缘故,而是因为Go语言鼓励多线程,C++恰恰相反。Go语言本身就支持多线程,使用起来非常简单。我用Go语言实现TCP/IP代理时没有使用互斥信号量和条件变量。使用Go语言的管道(channels)可以优雅地实现线程同步。

好吧,下面是源代码,带有解释。如果你不熟悉Go编程语言,注释应该会有帮助。我的目的不只关注程序功能,同时还关注Go语言本身。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/901c8d2d20942b7d88e3cd2a380cd535.html