V2ray-core: Improve proxy performance on lossy connection

Created on 1 Jun 2016  ·  24Comments  ·  Source: v2ray/v2ray-core

当网络环境不稳定时, 基于TCP的VMess协议连接会变得不稳定,导致无法正常使用。

能否考虑通过添加一个基于UDP的,能够在较高丢包,较高延迟下正常工作的协议来解决这个问题?

Most helpful comment

这个功能正在被实现中。
https://github.com/xiaokangwang/v2ray-core/tree/kcptun

All 24 comments

目前可能的思路:

在高延迟环境下,重传将有较高的时间和速度成本,因此,单纯使用ARQ协议的效果可能比较一般。

但是再加上Error correction code, 可以在不请求重传的情况下,对抗高丢包环境。

有想过使用类似 kcp 之类的协议,但是工程量非常大,一时半会搞不定。

补充: 已经有比较成形的项目如 https://github.com/xtaci/kcptun

如果直接整合的话,可以减少很大的工程量。

这个功能正在被实现中。
https://github.com/xiaokangwang/v2ray-core/tree/kcptun

请等一下,你对 V2Ray 和 KCP 的理解是不对的。

KCP 是一个传输方式,它不是一个 proxy。如果你把它实现成 proxy 会带来以下问题:

  1. 你需要实现一整套新的加密解密机制(目前在你的 fork 中还没有看到相应的代码);
  2. 其它协议无法使用 KCP;

合理的做法是把 KCP 实现在 transport 下面,大致要改的地方是:

  1. 修改 hub.TCPHub,在 Listen 的时候使用 KCP 来监听;
  2. 修改 hub.Dial,使用 KCP 来发起连接;
  3. 修改 hub.Connection 兼容 KCP;

这样做的好处是:

  1. 所有的 proxy 都可以在 KCP 和传统 TCP 之间无缝切换;
  2. proxy 可以不用关心传输层的配置,比如 MTU,window size 之类;

V2Ray 的架构请参考:https://www.v2ray.com/zh_cn/chapter_04/02_design.html

如果你要实现一个新的 proxy,请先给出协议的定义,类似这样:https://www.v2ray.com/zh_cn/chapter_04/03_vmess.html

对于一个 proxy 来说,它的数据格式直接关系到传输过程的稳定性,即能否被识别。所以对于格式的讨论要比具体实现重要得多。

KCP不是和TCP,UDP同一层的协议: 它是一个ARQ协议。

你可能理解成我用类似于SCTP的协议实现了一个proxy,不过实际情况 _不是_ 这样的。。。。

我实际上使用了KCP包里面的UDPSession(aka github.com/xtaci/kcp-go.UDPSession)实现了一个proxy。

UDPSession会监听一个UDP端口,然后利用KCP协议将数据包组合成流,因此并不是独立的一个传输层协议。 不应该和TCP类的协议混淆。

至于加密的问题, github.com/xtaci/kcp-go.DialWithOptions 里面第3个参数就是把密钥提交进去的地方,这个是KCP包自带的加密,参考kcptun的实现,有了这个加密之后就不用再加密一次了。

关于数据格式, 由于数据流的稳定性已经被KCP保证了,我这个实现的就是直接把要连接的地址用json编码后发送出去,远程接收到就直接把整个当前的UDP数据流和与Dispatch的连接双向管道了。

关于把KCP实现在transport下的问题: 和其他程序交互时,其他程序都不支持KCP,如果要支持KCP(Over UDP)的话,那么也只有VMess一个协议能够使用KCP。 其他不是v2ray-v2ray的in/out bound都不会支持KCP。 如果以后有了VMess over Websocket, over TLS, 也都不会支持。 不独立出来一个proxy,可能会导致困扰。

至于能否被识别,我觉得还是需要进一步考虑的,我现在做的时候没有考虑这点。
screenshot from 2016-06-09 21-29-22

我现在制作的这个版本已经能用了,但是有内存泄漏,先继续讨论,再进行进一步的完善。

内存泄漏原因: KCP连接不是TCP连接,实际上没有状态,不会自己关闭,不能像TCP连接一样的使用。(可能需要set deadline。。。)

ARQ 指的是一种类型的协议,TCP 就是一种 ARQ 协议。参见:https://en.wikipedia.org/wiki/Automatic_repeat_request

从 OSI 的角度来说,V2Ray 中的 transport 对应 OSI 的第四、第五层,proxy 对应第六层。错层的话会带来非常复杂的设计问题。

你说的其它程序不能用的问题无效,在 proxy 交互的时候,有很多因素会使交互不成功,比如协议格式、传输方式等等。使用 KCP 或者 TCP 来传输只是这些因素中的一种,为了避免这个因素而单独做一个协议是不合理的,也没有任何持续性可言。为了让不同的 VMess 程序可以互通,加个参数就可以,就像 v1.15 中的连接重用一样,只要两边都打开了这个参数,就可以进行连接重用。使用 KCP 同理。

如果把 KCP 做在传输层,之后的 VMess Over TLS 当然也可以用的。只要客户端服务器双方都支持且配置了 KCP 就可以交互,不会有任何问题。

我大概理解你的意思了,那我实现一下hub里面的相关功能,同时把目前的这些参数移动到transport区域里面?

我现在比较担心的是KCP连接和TCP连接在一些细节上有不同,如果要使用TCP方式使用的话可能会有一些问题。

参数大致先写成这样吧:

"transport": {
  "connectionReuse": false, // 已有的

  "streamingType": "tcp" / "kcp",
  "kcpOption1": "xxx",
  "kcpOption2": "yyy",
  .....
}

如果 kcp 在默认参数的情况可以正常工作,那除了 streamingType 外先不必开放过多的参数。参数多了可能会导致误解和滥用。

我看过 kcp 的介绍,如果 kcptun 没有加入额外内容的话,应该可以无缝替换 tcp 的。

至于对 proxy 的判断,你可以 pull 到最新的代码,里面我加了 proxy.InboundMeta 和 proxy.OutboundMeta,你可以在里面加一个 flag 表示当前 proxy 能否使用 kcp。然后把 meta 传入 hub.Dial 和 hub.ListenTCP 来根据不同的 flag 创建不同类型的连接。

我近期实现一下,如果遇到问题继续讨论

现在的想法:
proxy.InboundMeta,proxy.OutboundMeta 中添加flag,由proxy声明是否生成KCP。

在 transport/hub/tcp.go 创建新函数 func ListenTCP6(address v2net.Address, port v2net.Port, callback ConnectionHandler,proxyMeta proxy.InboundHandlerMeta ,tlsConfig _tls.Config) (_TCPHub, error)
如果proxyMeta中声明了kcp支持,就按照配置监听,否则就调用ListenTCP

在 transport/hub/dialer.go 创建新函数 Dial3(src v2net.Address, dest v2net.Destination,proxyMeta proxy.OutboundHandlerMeta) (*Connection, error) DialWithoutCache3(src v2net.Address, dest v2net.Destination,proxyMeta proxy.OutboundHandlerMeta) (net.Conn, error) 如果proxyMeta中声明了kcp支持同理

注:KCP UDPSession必须设置Timeout(无状态连接,不会EOF) (写给自己的备忘。。)

在中国大陆北京市的速度测试结果

实验组 使用VMess over KCP(UDPSess) 参数随便输入了一个:
screenshot from 2016-06-12 15-46-48

对比组 使用VMess over TCP (/proc/sys/net/ipv4/tcp_congestion_control=htcp):
screenshot from 2016-06-12 15-49-13

看上去不错,是否打算提交pr到主分支呢?

近期会PR。

目前要进一步测试,补充文档,之后PR。

Closing as PR is merged.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

supersonic600 picture supersonic600  ·  3Comments

choicky picture choicky  ·  4Comments

ghost picture ghost  ·  4Comments

limaofu picture limaofu  ·  3Comments

supersndqd picture supersndqd  ·  3Comments