当前位置:C++技术网 > 资讯 > Tcp服务器程序移植开发的经历(Libuv的连续回发数据的坑)总结

Tcp服务器程序移植开发的经历(Libuv的连续回发数据的坑)总结

更新时间:2017-02-24 00:41:32浏览次数:1+次

    现在正在开发TCP服务器程序,从我之前开发的Windows平台的TCP服务器客户端移植过来。而在移植Linux之前,考虑过将服务器做成Windows服务的形式运行在Windows的云服务器上。Windows服务器的服务程序已经做好了,从客户端项目提炼出来了一套代码,并且测试通过。不过后来在选择Web接口时,经过仔细的比较,我认为Windows在做服务器前景不如Linux,而且Web接口并不方便做。Linux作为服务器本来就是主流,客户主要是购买Linux服务器。所以最终决定将服务器移植到Linux平台。不管对Linux熟不熟悉(不太熟悉),该做的事情就要毫不犹豫的做。拖泥带水的犹豫和留恋,只会造成今后更多的不便和麻烦。

    就这样开始了移植Linux之旅。

Tcp服务器程序移植开发的经历(Libuv的连续回发数据的坑)总结

    Libuv本身就是跨平台的网络库,由node.js提炼出来开源的,所以移植到Linux上,问题不大。经过一番艰苦的移植,碰到各路妖怪(编译链接错误),一路打怪升级,最终移植成功了。在做完基本的数据通信测试后,开始测试业务协议的数据。刚好协议的逻辑要求在收到客户端一个命令后要回复两个命令。然后问题就来了。当客户端发送了一个命令,服务端回复两次数据后,服务器程序CPU占用率达到99.x%。后来经过跟踪调试,发现是Libuv陷入了死循环了。
    看似成功移植的表面却隐藏着一个大坑,那就是连续回发数据会导致服务器程序挂掉。这就是第一关的终极大boss,libuv在linux下的一个大坑。当然一开始我是不知道的。libuv作为知名度不错的开源库,我充分相信libuv没有问题。开始反复检查我的代码,没有逻辑问题,然后再换gcc编译器,换了3个版本(4.4.7,4.8.5,5.4.0)。还是不行,然后试了3个版本的centos(6.5 64位,7 64位,6.8 32位),问题依然如此,系统都重装了好多次,不过因为我都写成脚本自动部署环境,所以部署系统还是很快的。后来尝试不同的libuv版本,问题依旧。也就是到今天,确诊为libuv在centos的一个小bug。
    反复配置不同的环境,在于相信libuv应该不会出现这样的问题的。因为同样的代码在Windows上工作的很好,到了Linux却工作的不正常。自然让人有点怀疑哪些地方出问题了。
    本着踏实做技术的态度,我没有轻易的断言是libuv库的问题,否则显得浮躁不踏实。但是事到如今,不同的环境都是同样的问题。后来直接在Windows拿同样的测试代码测试,结果Windows表现一切正常。这样才确诊了libuv的bug。
    有部分代码是用C++11标准写的,所以才尝试了不同的编译器,结果问题没有变化。也就排除了编译器的问题。后来怀疑是不是系统的位数的问题。通过测试计算数据类型的字节长度,显示一切正常,数据包长度和协议一致。然后安装了32位和64位系统来测试,问题依然如故。排除系统位数的问题。在排除我自己的代码的问题时,我显示逐步的注释自己的代码,最后把所有代码都注释了,写了简单的测试代码都有问题。然后用这个简单的代码测试Windows版的程序,一切正常。后来质疑发送的数据缓冲的问题,换用了两个自己分配的不同的缓冲区,依然还是有问题。
     到此,基本能够造成问题的地方都做了测试,确实是Libuv的问题。确诊之后,也就好解决问题了。其实要说解决问题,早就可以,但是解决问题的方法不规范,会对今后维护不利。但是事到如今,也是没办法。不过也不是没有选择的余地。通过仔细思考,最终想到了一个不错的数据发送机制,成功绕过libuv的这个bug,同时也兼顾了数据发送效率,而且代码改动非常小。测试后完全通过,代表linux版的站级监控初步移植开发完成。这个过程真的是痛并快乐着,遇到的问题的都是对我的一种锻炼。libuv的bug让我对linux环境的部署有了深入的了解。而移植过程也对跨平台加深了理解。
    因为单次回发数据是没有问题的。既然libuv已经存在这个问题,那就绕过去吧。在解决这个问题之前,已经通过邮件将问题情况反馈给了libuv开发者,希望能够完善问题。绕过去bug的方法很简单,即把需要回发的多个数据包合并到一个数据包里,这样只需要发送一次。而在收到叠包的数据包时,在拆包之后,再将所有的包解析做处理之后,将所有的返回给客户端的数据包打包到一个缓冲中,然后一次性发送。通过巧妙的发送机制的改造,成功的绕开了libuv不能连续回发数据的bug。
    到此,整个移植开发过程也就告一段落了。移植后的程序测试通过。
    这一路走的真是辛苦。不过走完之后,收获挺多的。