有一段非常非常非常简单的网络编程代码,C语言,C/S模式,相信每一个学习C语言网络编程的人都写过,正因为特别简单,所以发生错误时第一反应并不是我的代码错了,而是系统的BUG! 事实证明,这种想法真是 too young, too simple, sometime naive !

无论是系统调用还是库函数代码,全世界那么多人在用,经过这么多年的千锤百炼,发生错误的可能性是很小的,大多时候都是自己的粗心大意。本次乌龙事件最后查明的原因是传给recvfrom函数的最后一个参数 addrlen没有被初始化。

事情是这样的,一个非常简单的客户/服务器程序。
第一次运行 ./server 和 ./client 127.0.0.1 , 当client发送数据的时候,server收到了,但是显示client的IP地址是错误的,因为是错误的IP地址,所以server把回送的数据报发给了错误的地址,client当然就收不到了。

奇怪的是,关闭client,再打开一次(server不必关闭再打开),然后做同样的事,这回server能显示正确的client IP地址了,而且client也能收到server回送的报文了。
如图。

这种情况可以复现,更奇怪的是这种错误发生在我的一台笔记本上(内核是Linux 3.5),而在另一台笔记本上(Linux 2.6.38内核),这个程序正常运行。一开始我还很兴奋,以为找到了一个内核的Bug,还幻想能不能发一个牛逼的patch给内核社区。(蛤蟆说:你们呐,too young, too simple)

其实这个问题是另一位同学遇到的,然后他问我,代码也是他提供的,因为代码很简单所以不贴出了,只给出下载链接吧。
udp.zip

熟悉网络编程的人一看就明白,这是一个基础的不能再基础的 “hello,world” 程序。一开始发生错误的时候,我各种调试,又是用wireshark捕捉发出的数据包,又是用gdb跟踪调试,忙活了一下午还是没搞定,然后求助同事某牛,同事只是瞄了一眼代码,然后man了一下手册,告诉我这个参数应该要初始化。。。。试了一下,果然。。。

手册上这样说:

The argument addrlen is a value-result argument, which the caller should initialize before the call to the size of the buffer associated with src_addr, and modified on return to indicate the actual size of the source address. The returned address is truncated if the buffer provided is too small; in this case, addrlen will return a value greater than was supplied to the call.

既然如此,那么给addrlen赋初值 addrlen=sizeof(their_addr); 就行了。

之后想想,为什么第一次运行client保证出错,关闭以后在开启,就会正确呢?

因为recvfrom函数在server内,server开启的时候没有初始化addrlen的值,因此会出现莫名其妙的错误(表现返回一个莫名其妙的IP),然而,正是因为有了第一次的错误,歪打正着,相当于给addrlen赋了一个初值,当关闭client并再次运行时,这次addrlen有了初值,一切都正常了。

其实写这篇文章,并没有打算详细的写如何解决错误,而是想记录一下避免再犯类似的错误,或者说,从这件事里面可以学到什么。
1. 当然是编程的基本功,自己必须要清楚他调用的每一个函数的参数,类型神马的要正确,该初始化的要初始化。
2. man手册是个好老师。
3. 暂时没想到。

至于文中还提到的一个问题:为什么3.x内核上出错,而2.6内核上正常呢? 这个问题暂时没有想明白,留着以后回答。

42,616 views
Home

15 Comments so far

Trackbacks/Pingbacks

Leave a comment

Name(required)
Mail (required),(will not be published)
Website(recommended)

Fields in bold are required. Email addresses are never published or distributed.

Some HTML code is allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
URLs must be fully qualified (eg: http://blog.nlogn.cn),and all tags must be properly closed.

Line breaks and paragraphs are automatically converted.

Please keep comments relevant. Off-topic, offensive or inappropriate comments may be edited or removed.