Files
v2ray_simple/netLayer/relay.go
hahahrfool f8ef685bdd 解决readv内存泄漏问题;解决转发时断连后的悬垂链接问题
在四点链接的情况下,我们只终端中间两点是不够的,要切三刀;
总之实践很简单,就是copy完成之后,要Close所有的链接

readv的话,系统readv数组和buffer不要在put进pool后相互引用

添加-bl 选项,可以自定义buf大小;注意越小可能越慢,建议buf大小保持在4k以上

添加-pp选项,可以生成cpu.pprof文件

修复其它小问题.
2022-03-31 01:32:58 +08:00

220 lines
4.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package netLayer
import (
"io"
"log"
"net"
"reflect"
"syscall"
"github.com/hahahrfool/v2ray_simple/utils"
)
// TryCopy 尝试 循环 从 readConn 读取数据并写入 writeConn, 直到错误发生。
//会接连尝试 splice、循环readv 以及 原始Copy方法
func TryCopy(writeConn io.Writer, readConn io.Reader) (allnum int64, err error) {
var multiWriter utils.MultiWriter
var rawConn syscall.RawConn
var isWriteConn_a_MultiWriter bool
var isWriteConnBasic bool
if utils.CanLogDebug() {
log.Println("TryCopy", reflect.TypeOf(readConn), "->", reflect.TypeOf(writeConn))
}
if SystemCanSplice {
rCanSplice := CanSpliceDirectly(readConn)
if rCanSplice {
var wCanSplice bool
wCanSpliceDirectly := CanSpliceDirectly(writeConn)
if wCanSpliceDirectly {
wCanSplice = true
} else {
if CanSpliceEventually(writeConn) {
wCanSplice = true
}
}
if rCanSplice && wCanSplice {
if utils.CanLogDebug() {
log.Println("copying with splice")
}
goto copy
}
}
}
// 不全 支持splice的话我们就考虑 read端 可 readv 的情况
// 连readv都不让 那就直接 经典拷贝
if !UseReadv {
goto classic
}
rawConn = GetRawConn(readConn)
if rawConn == nil {
goto classic
}
if utils.CanLogDebug() {
log.Println("copying with readv")
}
isWriteConnBasic = IsBasicConn(writeConn)
if !isWriteConnBasic {
multiWriter, isWriteConn_a_MultiWriter = writeConn.(utils.MultiWriter)
}
for {
readv_mem := get_readvMem()
var buffers net.Buffers
buffers, err = readvFrom(rawConn, readv_mem)
if err != nil {
put_readvMem(readv_mem)
return
}
var thisWriteNum int64
var writeErr error
// vless.UserConn 和 ws.Conn 实现了 utils.MultiWriter
if isWriteConn_a_MultiWriter {
thisWriteNum, writeErr = multiWriter.WriteBuffers(buffers)
} else {
// 这里不能直接使用 buffers.WriteTo, 因为它会修改buffer本身
// 而我们为了缓存,是不能允许篡改的
// 所以我们在确保 writeConn 不是 基本连接后, 要 自行write
if isWriteConnBasic {
//在basic时之所以可以 WriteTo是因为它并不会用循环读取方式, 而是用底层的writev
// 而writev时是不会篡改 buffers的
thisWriteNum, writeErr = buffers.WriteTo(writeConn)
} else {
thisWriteNum, writeErr = utils.BuffersWriteTo(buffers, writeConn)
}
}
allnum += thisWriteNum
if writeErr != nil {
err = writeErr
put_readvMem(readv_mem)
return
}
//buffers = utils.RecoverBuffers(buffers, readv_buffer_allocLen, ReadvSingleBufLen)
put_readvMem(readv_mem)
}
classic:
if utils.CanLogDebug() {
log.Println("copying with classic method")
}
copy:
//Copy内部实现 会自动进行splice, 若无splice实现则直接使用原始方法 “循环读取 并 写入”
// 我们的 vless和 ws 的Conn均实现了ReadFrom方法可以最终splice
return io.Copy(writeConn, readConn)
}
// 类似TryCopy但是只会读写一次; 因为只读写一次所以没办法splice
func TryCopyOnce(writeConn io.Writer, readConn io.Reader) (allnum int64, err error) {
var buffers net.Buffers
var rawConn syscall.RawConn
var rm *readvMem
if utils.CanLogDebug() {
log.Println("TryCopy", reflect.TypeOf(readConn), "->", reflect.TypeOf(writeConn))
}
// 不全 支持splice的话我们就考虑 read端 可 readv 的情况
// 连readv都不让 那就直接 经典拷贝
if !UseReadv {
goto classic
}
rawConn = GetRawConn(readConn)
if rawConn == nil {
goto classic
}
if utils.CanLogDebug() {
log.Println("copying with readv")
}
rm = get_readvMem()
defer put_readvMem(rm)
buffers, err = readvFrom(rawConn, rm)
if err != nil {
return 0, err
}
allnum, err = utils.BuffersWriteTo(buffers, writeConn) //buffers.WriteTo(writeConn)
return
classic:
if utils.CanLogDebug() {
log.Println("copying with classic method")
}
bs := utils.GetPacket()
n, e := readConn.Read(bs)
if e != nil {
utils.PutPacket(bs)
return 0, e
}
n, e = writeConn.Write(bs[:n])
utils.PutPacket(bs)
return int64(n), e
}
// 从conn1读取 写入到 conn2并同时从 conn2读取写入conn1
// 阻塞
// 返回从 conn1读取 写入到 conn2的数据
// UseReadv==true 时 内部使用 TryCopy 进行拷贝
// 会自动优选 splicereadv不行则使用经典拷贝
func Relay(wrc, wlc io.ReadWriteCloser) {
go func() {
TryCopy(wrc, wlc)
wlc.Close()
wrc.Close()
}()
TryCopy(wlc, wrc)
wlc.Close()
wrc.Close()
}
func DebugRelay(realTargetAddr *Addr, wrc, wlc io.ReadWriteCloser) {
go func() {
n, e := TryCopy(wrc, wlc)
log.Println("本地->远程 转发结束", realTargetAddr.String(), n, e)
wlc.Close()
wrc.Close()
}()
n, e := TryCopy(wlc, wrc)
log.Println("远程->本地 转发结束", realTargetAddr.String(), n, e)
wlc.Close()
wrc.Close()
}