mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-16 13:50:51 +08:00
150 lines
4.5 KiB
Go
150 lines
4.5 KiB
Go
package utils
|
||
|
||
import (
|
||
"io"
|
||
"log"
|
||
)
|
||
|
||
//一种简单的组合
|
||
// 在ws包中被用到.
|
||
type RW struct {
|
||
io.Reader
|
||
io.Writer
|
||
}
|
||
|
||
// SystemReadver 是平台相关的 用于 调用readv的 工具.
|
||
// 该 SystemReadver 的用例请参照 netLayer.readvFrom , 在 netLayer/readv.go中;
|
||
// SystemReadver的具体平台相关的实现见 readv_*.go; 用 GetReadVReader() 函数来获取本平台的对应实现。
|
||
type SystemReadver interface {
|
||
Init(bs [][]byte, singleBufLen int) //将 给出的buffer 放入内部实际数据中
|
||
Read(fd uintptr) (uint32, error) //读取一次文件,并放入 buffer中
|
||
Clear() //清理内部buffer
|
||
Recover(bsLen int, bs [][]byte) //恢复内部buffer
|
||
}
|
||
|
||
// 因为 net.Buffers 的 WriteTo方法只会查看其是否实现了net包私有的 writeBuffers 接口
|
||
// 我们无法用WriteTo来给其它 代码提升性能;因此我们重新定义一个新的借口, 实现了 MultiWriter
|
||
// 接口的结构 我们就认为它会提升性能,比直接用 net.Buffers.WriteTo 要更强.
|
||
/*
|
||
本接口 在代理中的用途,基本上只适合 加密层 能够做到分组加密 的情况; 因为如果不加密的话就是裸协议,直接就splice或者writev了,也不需要这么麻烦;
|
||
|
||
如果是tls的话,可能涉及自己魔改tls把私有函数暴露出来然后分组加密;
|
||
|
||
如果是vmess的话,倒是有可能的,不过我还没研究vmess的 加密细节;
|
||
|
||
而如果是ss 那种简单混淆 异或加密的话,则是完全可以的
|
||
|
||
分组加密然后 一起用 writev 发送出去,可以降低网络延迟, 不过writev性能的提升可能是非常细微的, 也不必纠结这里.
|
||
|
||
如果考虑另一种情况,即需要加包头和包尾,则区别就很大了;
|
||
|
||
WriteTo会调用N次Write,如果包装的话,会包装N 个包头和 包尾;而如果我们实现WriteBuffers方法,
|
||
只需先写入包头,而在 最后一个 []byte 后加 包尾,那么就可以获得性能提升,
|
||
我们只需增添两个新的 []byte 放在其前后即可, 然后再用 writev 一起发送出去
|
||
|
||
那么实际上 websocket 的gobwas/ws 包在不开启缓存时,就是 每次Write都写一次包头的情况;
|
||
|
||
所以websocket很有必要实现 WriteBuffers 方法.
|
||
|
||
目前实现 的有 vless.UserConn 和 ws.Conn
|
||
*/
|
||
type MultiWriter interface {
|
||
WriteBuffers([][]byte) (int64, error)
|
||
}
|
||
|
||
//获取所有子[]byte 长度总和
|
||
func BuffersLen(bs [][]byte) (allnum int) {
|
||
if len(bs) < 1 {
|
||
return 0
|
||
}
|
||
for _, b := range bs {
|
||
allnum += len(b)
|
||
}
|
||
return allnum
|
||
}
|
||
|
||
func PrintBuffers(bs [][]byte) {
|
||
for i, b := range bs {
|
||
log.Println(i, b)
|
||
}
|
||
}
|
||
|
||
//削减buffer内部的子[]byte 到合适的长度;返回削减后 bs应有的长度.
|
||
func ShrinkBuffers(bs [][]byte, all_len int, SingleBufLen int) int {
|
||
curIndex := 0
|
||
for curIndex < len(bs) {
|
||
if all_len <= 0 {
|
||
break
|
||
}
|
||
end := all_len
|
||
if end > SingleBufLen {
|
||
end = SingleBufLen
|
||
}
|
||
bs[curIndex] = bs[curIndex][:end]
|
||
all_len -= end
|
||
curIndex++
|
||
}
|
||
return curIndex
|
||
}
|
||
|
||
//通过reslice 方式将 bs的长度以及 子 []byte 的长度 恢复至指定长度
|
||
func RecoverBuffers(bs [][]byte, oldLen, old_sub_len int) [][]byte {
|
||
bs = bs[:oldLen]
|
||
for i, v := range bs {
|
||
bs[i] = v[:old_sub_len]
|
||
}
|
||
return bs
|
||
}
|
||
|
||
func BuffersWriteTo(bs [][]byte, writeConn io.Writer) (num int64, err2 error) {
|
||
for _, b := range bs {
|
||
nb, err := writeConn.Write(b)
|
||
num += int64(nb)
|
||
if err != nil {
|
||
|
||
err2 = err
|
||
break
|
||
}
|
||
}
|
||
return
|
||
}
|
||
|
||
// 如果 分配了新内存来 包含数据,则 duplicate ==true; 如果利用了原有的第一个[]byte, 则 duplicate==false
|
||
// 如果 duplicate==false, 不要 使用 PutPacket等方法放入Pool;
|
||
// 因为 在更上级的调用会试图去把 整个bs 放入pool;
|
||
func MergeBuffers(bs [][]byte) (result []byte, duplicate bool) {
|
||
if len(bs) < 1 {
|
||
return
|
||
}
|
||
b0 := bs[0]
|
||
if len(bs) == 1 {
|
||
return b0, false
|
||
}
|
||
allLen := BuffersLen(bs)
|
||
|
||
if allLen <= cap(b0) { //所有的长度 小于第一个的cap,那么可以全放入第一个中;实际readv不会出现这种情况
|
||
b0 = b0[:allLen]
|
||
cursor := len(b0)
|
||
for i := 1; i < len(bs); i++ {
|
||
cursor += copy(b0[cursor:], bs[i])
|
||
}
|
||
return b0, false
|
||
}
|
||
|
||
if allLen <= MaxBufLen {
|
||
result = GetPacket()
|
||
|
||
} else {
|
||
result = make([]byte, allLen) //实际目前的readv实现也很难出现这种情况
|
||
// 一定要尽量避免这种情况,如果 MaxBufLen小于readv buf总长度,会导致严重的内存泄漏问题,
|
||
// 见github issue #24
|
||
}
|
||
|
||
cursor := 0
|
||
for i := 0; i < len(bs); i++ {
|
||
cursor += copy(result[cursor:], bs[i])
|
||
}
|
||
|
||
return result[:allLen], true
|
||
}
|