以下分析过程比较冗长,我应xxx要求分享下过程和经验,安卓开发和测试人员如果了解到这个问题的原理和解决方案应该会比较有用,各位大佬如觉得必要可以扩散给他们.不感兴趣的请自行忽略~

  首先,这个问题涉及到Linux系统内核和TCP三次握手时携带的时间戳的工作方式.

  先说Linux系统内核,这涉及到2个内核参数:tcp_tw_recycle和tcp_timestamps

  第一个参数主要是用于tcp time_wait状态的快速回收(注1),tcp_timestamps主要作用是序列号回绕,乱序的时间判断依据,PAWS,计算RTT.

   Linux/Android系统默认情况下对这2个参数的默认设置为前者off,后者on.

   但是高并发的网站下会生产time_wait过多的问题, time_wait过多会导致占用过多端口的一个影响是会占用我们需要使用到的端口,比如例如我们有个服务监听的端口为30007,重启的时候经常会提示端口被占用.所以基于这个问题的处理方案,大都会把tcp time_wait的值更改为on(注2).

   OK,通过以上描述,我们可以了解到,我们应用网关上这2个内核参数现在都是on的状态(注3).

   在这2个参数都是on的情况下,会发生什么问题呢?纯技术层面上引发的问题过程如下:

   60s内同一源IP主机(NAT环境)(注4)的socket connect请求中的时间戳必须是递增的,NAT环境下,当客户端发起TCP握手请求,服务器端会缓存并验证其时间戳序列号,后续请求中如果时间戳小于缓存的时间戳,即视为无效,相应的数据包会被丢弃.

   技术层面说完,来说下实际情况,由于多个客户通过NAT以同一IP请求服务器,服务器端把他认为重复的,或者不符合规范的请求drop掉.也就产生了APP访问异常的问题.由于iOS个别安卓机型和无此问题(注5),大部分情况下大家会认为是手机,本地网络,甚至是APP的问题.

   解决方案就是将tcp_tw_recycle改为默认的off即可.如果以上理论依据让大家难以信服的话,我也问过各大互联网手游公司的大佬们,采取的方案和采用该方案的需求点是一致的.所以这个问题的解决有理论和事实依据同时支撑.

   我们的APP虽然没有受这个问题的太多影响(注6),但是由于该问题是普适性问题,并且问题表现形式涉及到多个层面.提前解决了会比较安心.再一次感谢xx!

 

   题外话1:移动的cmwap网络发来的包的时间戳是乱跳的,所以这个问题的解决应该会对在该网络下使用的用户起到一些帮助.

   题外话2:我们在解决问题的时候大多善于搜索解决方案,但对解决方案中使用到的技术原理疏于理解.我也是如此....自我检讨下先...

   题外话3:该问题涉及到很多TCP/IP原理,为了不让这份邮件看起来太冗长,我没有把更多的技术细节包含进来,欢迎有兴趣了解的同学们来找我交流,其实我自己对这方面的理解也不是很深入.


  注1:对C和TCP有了解的请看:http://lxr.free-electrons.com/ident?i=tcp_timewait_state_process

  注2:随便google/百度下,80%的搜索结果对该问题的处理方法都是time_wait设置为on,这种方式虽然有效直接,但是会引发其他的问题,比如引发我们现在讨论的这个问题.

  注3:历史遗留问题,石宁在的时候该参数就是ON,不过即使不是历史遗留问题,大多数人也不会把这个问题和安卓客户端联系到一起.尤其是在移动互联网之前,这个问题是根本存在,或者是可以忽略的.

  注4:因为NAT环境下不论客户端多少,都是通过一个公网IP出去的,对于服务器来说,接收到的请求是来自于同一个用户的.

  注5:iOS和Mac下,对于tcp_tw_recycle的默认参数是off的,也就是说,这个问题在客户端处理一样可以解决,修改下安卓的内核参数就行了,但是这种问题总不能让用户来买单,所以只能服务器端来处理了.至于Apple公司为什么把自家产品的这个参数设为off,我没找到相关资料,我只能表示,Apple是一家非常NB和有远见的公司....

  注6:我问过晓峰,表示只有个别安卓机出现这个问题,并且之前也没什么人和我反应过类似问题.