公司分布式系统的通信组件实在有点看不下去了,有自己的想法,动动手吧,有个原型也好与人PK。
想当然地希望使用TCP紧急数据处理作为“消息”的异常中止标志,心想这个简单,加了EPOLLPRI后,一直表现正常的epoll_wait()突然陷入了看似万劫不复的等待当中。修改一下代码后,发现了另一个问题,明明是用send(MSG_OOB)发送的带外数据,却出现在正常的字节流中,可是没有设置SO_OOBINLINE啊,神秘事件。
费几个周折,最终确定我的代码是OK的,稍微修改一下UNP的例子也可以重现这个问题:让sender只发送MSG_OOB数据,而接收者不读归紧急数据,只读取正常的字节流。不一会儿,接收程序就从正常字节流中读出东西了。按预想的情况,接收者应该永远阻塞才是。
开始以为是TCP receiver side的内核代码出了问题,检查一下更新tcp_sock->urg_data/urg_seq的代码没有什么问题啊,并且2.6.32和2.6.38-rc7都有这个问题!抓包看看吧,有些数据包有16384字节长。看来发送消息时就有“问题”,看tcp_sendmsg()吧。果然这里是有可能出现情况的。心想,这是个bug吧!心中窃喜,让俺抓到TCP stack的小辫子了?
怎么fix呢?查查以前下载RFC全集,根本就没要求带外数据一定不能出现字节流中。想了想,改这个真是有些复杂的,发送端需要修改不说,连滑动窗口的行为恐怕都得动,不然,接收端可能还得缓冲一堆紧急指针。仔细想了想,这个劳什子的TCP紧急数据其实就是个不中用的货色:
1、最直观的缺点:名为带外数据,其实不带外,这也就罢了。数据长度只有一个字节,作用实在有限。
2、带外数据在TCP头内的偏移只有16bit长,而现代TCP连接大量使用的window scaling选项,这样滑动窗口的容量大大超出了16 bits能表示的范围。因此很可能在接收端还没有知晓前一个带外数据的时候,后一个段已经把紧急指针写掉了。这样,前一个紧急数据就被丢掉了。接收端的应用程序可能根本就不知晓。
3、Linux的TCP实现,如果两个紧急数据挨的太近,就引起上面的我提到的bug。
后来发邮件问了问netdev邮件列表上的达人,John回复说,参考一下RFC6093吧,一看,这个RFC还挺新鲜,2011-01, TCP的紧急数据已经是废弃的功能了。不建议新的应用程序使用,只为旧程序兼容而存在。即使是使用,也建议使用打开SO_OOBINLINE。可是,我要说,打开这个选项也没有什么帮助,因为这个选项只对接收端有作用,tcp_sendmsg()还是可能出现上述(3)的问题。TCP的RFC和实现代码都好复杂啊,对于它,很有些积重难返的感觉。也许在许多场合,SCTP是个更好的选择吧。
一句话,别用的TCP的紧急数据。
阅读(4119) | 评论(1) | 转发(2) |