当网络环境不稳定时, 基于TCP的VMess协议连接会变得不稳定,导致无法正常使用。
能否考虑通过添加一个基于UDP的,能够在较高丢包,较高延迟下正常工作的协议来解决这个问题?
目前可能的思路:
在高延迟环境下,重传将有较高的时间和速度成本,因此,单纯使用ARQ协议的效果可能比较一般。
但是再加上Error correction code, 可以在不请求重传的情况下,对抗高丢包环境。
有想过使用类似 kcp 之类的协议,但是工程量非常大,一时半会搞不定。
补充: 已经有比较成形的项目如 https://github.com/xtaci/kcptun 了
如果直接整合的话,可以减少很大的工程量。
请等一下,你对 V2Ray 和 KCP 的理解是不对的。
KCP 是一个传输方式,它不是一个 proxy。如果你把它实现成 proxy 会带来以下问题:
合理的做法是把 KCP 实现在 transport 下面,大致要改的地方是:
这样做的好处是:
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,可能会导致困扰。
至于能否被识别,我觉得还是需要进一步考虑的,我现在做的时候没有考虑这点。

我现在制作的这个版本已经能用了,但是有内存泄漏,先继续讨论,再进行进一步的完善。
内存泄漏原因: 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) 参数随便输入了一个:

对比组 使用VMess over TCP (/proc/sys/net/ipv4/tcp_congestion_control=htcp):

看上去不错,是否打算提交pr到主分支呢?
近期会PR。
目前要进一步测试,补充文档,之后PR。
Closing as PR is merged.
Most helpful comment
这个功能正在被实现中。
https://github.com/xiaokangwang/v2ray-core/tree/kcptun