问题描述:在这几年的Android开发中,遇到了一个困扰我好久的问题,有时候在公司的wifi下,请求我们的公司自己的服务器很慢,甚至经常请求失败,切换成移动网络3G或者4G,就明显变快。but在相同的wifi环境下,用iphone和电脑请求就很快
刚开始发现手机wifi很慢的时候,以为是公司网络的问题,所以找运维去解决,运维的解释是我们公司用的北京鹏博士的宽带,公司机房是用的北京联通的宽带,公司的网络连接公司的服务器要经过武汉的转接点才能连到公司的服务器,绕了一个大圈,导致请求变慢,解决方法是接入多个运营商,但是由于公司预算的问题,这个没法解决。
后来发现只有Android手机连接服务器比较慢,iPhone和windows电脑请求都正常,我猜想可能是应用的网络请求框架有问题,因此专门写了一个demo,用Android提供的HttpUrlConnection直接请求,然后又用系统浏览器直接请求接口,也有同样的问题,排除了框架问题的可能。
难道是Android手机的wifi模块质量比较次,所以我把手头所有Android手机都测试了一遍,不管是贵的,还是便宜的,可能爆炸的还是工匠精神的都有此问题,并且还发现在家里的wifi下速度嗖嗖的。所以wifi模块的问题也排除了。
后来又想难道是发送出去的数据有问题,所以我就开始用Fiddler抓包,然后发现只要通过代理,将App代理到公司电脑上,在公司wifi下,连接很顺畅,后来又特意用手机做了一个热点,共享wifi到另一台手机,也很顺畅/
至此,因为水平有限,所有能试的方法都用了,没有定位到原因,这个问题也就搁置了。后来有一天研究okhttp,看到复用连接池,突然想到会不会跟这个原因有关呢,想要看连接有没有被复用,就要开始对Android手机进行抓包,并且不能通过代理的方式抓包,要直接抓包,抓包方法:http://blog.csdn.net/jk38687587/article/details/48467329
安装好抓包工具后,开始对手机进行抓包,通过和电脑直接请求的包进行对比,发现手机wifi下,Tcp Retransmission的情况很多,如图所示
这是什么鬼,SYN是什么玩意儿,ACK是啥(黑人懵逼脸.jpg),好吧,先恶补一下TCP/IP协议,然后谷歌一下,说是在第一次握手(SYN)的时候网络请求超时重发,但是为什么会超时呢,如果网络不好为什么windows下的请求就不超时呢?再仔细对比两边发出去数据包,发现Android发出去的包比windows发出去的包多了个timestamps字段,查查这个是什么玩意儿,查到了http://blog.csdn.net/jueshengtianya/article/details/50440696,有可能时间戳可能会导致客户端访问不了服务器,有两种解决方案,一种是将服务器的时间戳net.ipv4.tcp_timestamps设置为0,另一种是该客户端,将时间戳关闭,服务器咱改不了,先改改客户端吧,客户端怎么改呀,在寻找答案的过程中还发现了有人跟我遇到过同样的问题Android之网络丢包事件,我先试着用Root Explorer修改/proc/sys/net/ipv4文件夹下的tcp_timestamps文件(在这里遇到一个坑,以为可以直接编辑修改,废了好大劲也没修改成功,以为是root不彻底,鼓捣了半天root,也没成功,后来发现必须要通过命令才能修改,详见:http://blog.csdn.net/apn172/article/details/8034240)
先将timestamps设置为0,
抓包显示
timestamps没有了,效果立显,所有的重发都没有了
再设置回去:抓包显示
问题又出现了,来回试了几十遍,确认就是这个参数的问题。好有成就感呀。
但是这个问题最终的解决方案还得服务器改,因为客户端改这个参数要root,所以经过跟运维的同学沟通,将服务器的net.ipv4.tcp_timestamps设置为0和1,分别验证效果,结果很明显,就是这个参数的问题,最终服务器的同学将这个参数设置为了0,至此,困扰了我好久的问题终于得到了解决!!!
事后,查了一下tcp_timestamps的相关资料,了解了一下RFC1323协议,在linux中如果是tcp_tw_recycle被打开了话,会假设对端开启了 tcp_timestamps,然后会去比较时间戳,如果时间戳变大了,就可以重用。tcp_timestamps记录的是从开机到现在所经过的秒数,tcp_tw_recycle在开启后会比较同一公网ip下的tcp_timestamps,所以在同一wifi下,在第一次SYN的时候,tcp_timestamps如果比其他设备的tcp_timestamps小的话,包就直接被丢弃。