本文很短,目的在于confirm一下凌乱的《 Open×××莫名其妙断线的问题及其解决》,如果看觉得我比较啰嗦,那么一定要看看最后一个小节,好在CSDN为每篇文章都自动添加了目录,可以直接跳转到最后一节。

1.控制通道

控制通道主宰Open×××的SSL握手,密钥协商以及重协商。因此其健壮性直接影响到隧道是否能够建立成功。因此优化后burst retransmit直接影响恶劣网络环境下的隧道建立过程,使之更容易建立。一旦窗口由于ACK乱序/丢失而爆满,马上重传ID最小的包,期待收到ACK延展窗口!
原则:你丢包我就以多次重发来稀释掉丢包率,虽然这种方式有点自私,但是恶劣环境中求生是需要自私的。
效果:由于控制通道的数据量有限,因此需要比较极端的方式来展现这个修改是有效的,那就是设置以下几个参数:
ping:设置为5秒,尽可能短,但不要太短
ping-restart:设置成120秒,尽可能和ping拉开距离,这两个参数保证不会因为ping-restart导致断开,这样就将问题全部集中在控制通道了
reneg-sec:将它设置成2,即2秒钟进行一次密钥重协商
hand-window:将它设置成15,即15秒内如果SSL通道上的握手,密钥重协商没有成功,则算断开
tran-window:将它设置成5,即hand-window内失败的话,持续5秒钟隧道断开,该参数是可选的

用retry版本和标准版本测试,发现极端情下,标准版本几乎会瞬间失败,但是retry版本明显好很多。

2.数据通道

平时隧道有数据通过的时候,timer总是会reset,没有数据的时候,就依靠在数据通道发送PING来reset对端的timer。如果ping-restart到期timer都没有reset,则断开隧道。因此没有隧道数据时,更容易断开!注意,只要隧道建立,密钥协商好,除了重协商或者推送,控制通道基本空闲。因此影响隧道接通后断开的因素在ping,ping-restart参数。

3.高丢包率环境二者关系

一旦高丢包率且没有隧道数据过境,数据通道就会完全依靠PING,如果ping-restart过短,PING在恶劣环境下丢包,则隧道就会断开,此时会尝试重新建立隧道,因为此时已经是恶劣环境了,丢包率很高,ACK很容易乱序/丢失,加上Open×××在窗口满时不会马上重传,故而隧道久久不能建立。所以是,数据通道断开反映了网络环境已经恶化,这种恶化进而影响接下来的控制通道的握手和协商,所以针对Open×××断开的问题要双管齐下,第一拉长ping和ping-restart的距离(但是不能泛PING),第二,如《Open×××莫名其妙断线的问题及其解决》一样修改Open×××的重传调度逻辑。

4.程序极限

程序员往往希望一厢情愿地彻底解决问题!极端的情况就是,因为TCP是保证传输的,所以即使网线被剪断了,程序员还是希望(也仅仅是希望而已)能写出什么神秘的代码来接通网络,也还是希望发现是由于自己代码写的不够好才导致丢包,断线。但是对于网络工程师,就反过来了,他丝毫不管应用程序如何,上去就是show这show那,检查网线...有时候还真的就是socket阻塞了。事实上,对于Open×××断线的问题,也有一种处理极限,那就是网络真的不通了,丢包率真的太大了。那怎么办,那就只能断线了,这是我们所解决不了的,我们能做的就是保证损失降低到最小而已,并且告知用户发生了什么,让用户知道,虽然网络不通了,但是Open×××依然在不断努力中。

5.不修改Open×××的防断方案

不管怎么说,Open×××也是经过很多测试的成熟代码,它的重传调度虽然不及时,单是毕竟会重传!如果有重大问题,早就在社区被修改了。另外要注意的是,大多数时候,控制通道的数据包以及ACK并不是丢了,而是延迟到达,这样的话,仅仅通过延长超时时间就可以解决,幸运的是,Open×××本身给出了很多超时时间的配置:
a.增加ping,ping-restart的差值,可以确保不会轻易由于收不到ping而断开;
b.延长hand-window可以给控制通道的密钥协商预留足够的时间;
c.延长tran-window可以进一步在已经发觉要断和隧道真正断开之前的这期间,做reneg的最后尝试;此参数要大于reneg-sec!
...

6.我做的一切都是画蛇添足,但是...

实际上,当我回归看代码的时候,发现我所做的针对Open×××的修改本来就已经实现了,只是我研究其参数配置还没有细化到一定程度,所以我就自行进行了修改,对于我能找到点子上,并且设计出了一个schedule接口:
void reliable_schedule_id (struct reliable *rel, time_t timeout, packet_id_type id);
我很欣慰,但是我还是重新实现了已经实现的东西,重造了轮子!这是大多数初中级程序员的典型行为!
        我们来看一下以下的这个配置参数:
--tls-timeout n
              Packet retransmit timeout on TLS control channel if no acknowledgment from remote within  n  seconds  (default=2).
              When  Open××× sends a control packet to its peer, it will expect to receive an acknowledgement within n seconds or
              it will retransmit the packet, subject to a TCP-like exponential backoff algorithm.  This parameter  only  applies
              to  control  channel  packets.   Data  channel packets (which carry encrypted tunnel data) are never acknowledged,
              sequenced, or retransmitted by Open××× because the higher level network protocols running on  top  of  the  tunnel
              such as TCP expect this role to be left to them.

默认2秒钟,如果想让重传密集化,那么设置为1好了,虽然依靠这个参数带来的是全局意义的重传超时配置,但是我相信网络会解决好拥塞问题的。比较下来,我的修改只针对ID最小的包,即仅仅重传阻碍窗口推进的包,可能更加合理,但是Open×××自带的tls-timeout足以解决已经存在的ACK乱序丢失问题了,要拒绝完美,容忍不完美,这才是成事的关键!如果要完美,那么代码中就会充斥很多的所谓小技巧。
        花了大量的时间考虑如何修改Open×××的代码,到头来却是代码回退!