Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

模拟高丢包率环境下时出现的问题 #4

Closed
vzex opened this issue Feb 24, 2015 · 7 comments
Closed

模拟高丢包率环境下时出现的问题 #4

vzex opened this issue Feb 24, 2015 · 7 comments

Comments

@vzex
Copy link

vzex commented Feb 24, 2015

A B通讯,丢包率A:50%,B:75%
用脚本连续1000次请求发送一定数据

然后 kcp.snd_queue开始堆积
经过调试,发现是kcp.snd_buf队列中,segment.resendts延迟几乎每次都在100s后左右,导致snd_buf队列中的包一直很难发出,致使snd_una几乎不再更新
然后 _itimediff(kcp.snd_nxt, kcp.snd_una + cwnd) >= 0 这个条件基本上一直为true,最后snd_queue也开始堆积了。我用的版本是从c版本直接翻译成golang的,理论上行为全部一致,另外我看另外一个强制snd_buf发包的条件是包头的fastack大于0,但是这个也是依赖A发送B,B再回复ACK才会更新的,所以这个情况下基本上就是没有快速重试的途径,最后只能断开连接

我目前暂时的解决方案,snd_buff的第一个元素(影响snd_una更新的包)当xmit>2时强制重发,不知道有没有更合适的方法可以解决上面的问题?

@skywind3000
Copy link
Owner

你真细致呀,网络数据堆积是比较重要的一个问题。ARQ协议的信道理论可用带宽为:
最大可用窗口(min(远端剩余接收窗口,本地发送窗口,流控窗口)) x (1-丢包率) / RTT
你需要判断你发送的速率是否超过信道理论容量。如果超过了,就像tcp一样,你每秒只能发送10KB的数据了,而用户却每秒产生20KB的数据需要发送。

@skywind3000
Copy link
Owner

50%+75%的丢包率实在是太高了,信道几乎不可用了。因为高丢包,RTO会变的很大,这些行为和TCP也都是一致的,tcp在35%丢包时就断线了,没法工作了。其实处理“当前网络无法发送更多数据”这种事情,是上层传输逻辑的一个很重要的逻辑,不管下面是tcp还是kcp,再我们用kcp传送语音和视频,都会碰到网络震荡,这时候,语音或者视频一旦发现ikcp_waitsnd 的数据超过一个阀值就开始跳帧,不再传送新数据出去,直到网络恢复,或者超时不恢复就断线重连了。这是一个参考处理方法。还有一个参考处理方法就是给上层返回 EAGAIN,和TCP的方式一样,让用户去解决去。

@skywind3000
Copy link
Owner

当你需要发送“超过信道容量”的数据时,由易到烦,有三个处理方法。

  1. int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);
    扩大发送窗口和接收窗口,比如设置为64,相当于tcp的,SNDBUF, RCVBUF
  2. 连接管理层(即kcp的上一层管理连接,用于衔接用户和kcp的控制类),每次调用kcp_send前检查ikcp_waitsnd是否超过阀值,超过的话,不要调用kcp_send了,直接给用户 EAGAIN,和tcp行为保持一直。
  3. 数据传送层(处理kcp->output, 临近udp的那一层),发现丢包超过20%时,启动FEC,每发送三个包,紧跟一个冗余包(冗余包=P1 xor P2 xor P3),即3:1的冗余,如果丢包率上升,继续调整为2:1的冗余,发现一个包丢失的话,使用同组其他包xor后恢复出来,让传输层来负担一部分减少丢包率的任务。

理论上来讲,方法3在大多数情况下很有效果,但是如果你真的达到了信道的物理带宽上限,那么增加冗余包只会进一步增加丢包率。方法1和3都是缓解,归根结底是需要处理“每秒待发数据超过信道容量”这个问题,不管下层是tcp还是kcp,这个问题都必须要仔细处理。

@vzex
Copy link
Author

vzex commented Feb 24, 2015

谢谢耐心回复,这个问题我调了两天,所以代码读了不少,现在还在头疼,目前正常使用没啥问题,单子先关了

@vzex vzex closed this as completed Feb 24, 2015
@skywind3000
Copy link
Owner

嗯,我看一下你的项目,没理解错的话,应该是一个代理,一头接受用户的tcp连接,一头使用kcp出去。这种代理类的完美解决方案是,如果你kcp的wait_send超过一个阀值T(比如64),那么停止用户tcp连接的recv,直到你每秒检测一次wait_send低于T/2了,那么再允许那条tcp连接的recv。这样发生网络抖动时,你不recv,用户又持续发数据,你tcp的recv_buf就会变满,接着用户进程连过来的tcp那端的snd_buf就会变满,然后用户的tcp socket就阻塞了,该继续缓存等待还是该断线重连就让用户来抉择了。这样的表现是和用户不用代理直接tcp到目的地时的效果一模一样。

@vzex
Copy link
Author

vzex commented Feb 24, 2015

好的谢谢

@seven-eleven
Copy link

@vzex
怎么模拟网络丢包的?
在代理服务器上使用TC命令,针对出包进行的控制?
出包怎么控制一个方向的报文受控制策略影响?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants