mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-08 10:10:27 +08:00
160 lines
3.5 KiB
Go
160 lines
3.5 KiB
Go
package netLayer
|
||
|
||
import (
|
||
"flag"
|
||
"io"
|
||
"log"
|
||
"net"
|
||
"reflect"
|
||
"runtime"
|
||
"syscall"
|
||
|
||
"github.com/hahahrfool/v2ray_simple/utils"
|
||
)
|
||
|
||
const SystemCanSplice = runtime.GOARCH != "wasm" && runtime.GOOS != "windows"
|
||
|
||
var UseReadv bool
|
||
|
||
func init() {
|
||
flag.BoolVar(&UseReadv, "readv", true, "toggle the use of 'readv' syscall")
|
||
|
||
}
|
||
|
||
//这里认为能 splice 或 sendfile的 都算,具体可参考go标准代码的实现, 总之就是tcp和uds可以
|
||
func CanSplice(r interface{}) bool {
|
||
|
||
if _, ok := r.(*net.TCPConn); ok {
|
||
return true
|
||
} else if _, ok := r.(*net.UnixConn); ok {
|
||
|
||
return true
|
||
}
|
||
return false
|
||
|
||
}
|
||
|
||
// TryCopy 尝试 循环 从 readConn 读取数据并写入 writeConn, 直到错误发生。
|
||
//会接连尝试 splice、循环readv 以及 原始Copy方法
|
||
func TryCopy(writeConn io.Writer, readConn io.Reader) (allnum int64, err error) {
|
||
var mr utils.MultiReader
|
||
var buffers net.Buffers
|
||
var rawConn syscall.RawConn
|
||
|
||
if utils.CanLogDebug() {
|
||
log.Println("TryCopy", reflect.TypeOf(readConn), "->", reflect.TypeOf(writeConn))
|
||
}
|
||
|
||
if SystemCanSplice && CanSplice(readConn) && CanSplice(writeConn) {
|
||
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
|
||
}
|
||
|
||
mr = utils.GetReadVReader()
|
||
if utils.CanLogDebug() {
|
||
log.Println("copying with readv")
|
||
}
|
||
for {
|
||
buffers, err = ReadFromMultiReader(rawConn, mr)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
num, err2 := buffers.WriteTo(writeConn)
|
||
allnum += num
|
||
if err2 != nil {
|
||
err = err2
|
||
return
|
||
}
|
||
ReleaseNetBuffers(buffers)
|
||
}
|
||
classic:
|
||
if utils.CanLogDebug() {
|
||
log.Println("copying with classic method")
|
||
}
|
||
copy:
|
||
|
||
//Copy内部实现 会自动进行splice, 若无splice实现则直接使用原始方法 “循环读取 并 写入”
|
||
return io.Copy(writeConn, readConn)
|
||
}
|
||
|
||
// 类似TryCopy,但是只会读写一次; 因为只读写一次,所以没办法splice
|
||
func TryCopyOnce(writeConn io.Writer, readConn io.Reader) (allnum int64, err error) {
|
||
var mr utils.MultiReader
|
||
var buffers net.Buffers
|
||
var rawConn syscall.RawConn
|
||
|
||
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
|
||
}
|
||
|
||
mr = utils.GetReadVReader()
|
||
if utils.CanLogDebug() {
|
||
log.Println("copying with readv")
|
||
}
|
||
|
||
buffers, err = ReadFromMultiReader(rawConn, mr)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
allnum, err = buffers.WriteTo(writeConn)
|
||
|
||
ReleaseNetBuffers(buffers)
|
||
return
|
||
|
||
classic:
|
||
if utils.CanLogDebug() {
|
||
log.Println("copying with classic method")
|
||
}
|
||
|
||
bs := utils.GetPacket()
|
||
n, e := readConn.Read(bs)
|
||
if e != nil {
|
||
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 进行拷贝
|
||
// 会自动优选 splice,readv,不行则使用经典拷贝
|
||
func Relay(conn1, conn2 io.ReadWriter) (int64, error) {
|
||
|
||
if UseReadv {
|
||
go TryCopy(conn1, conn2)
|
||
return TryCopy(conn2, conn1)
|
||
|
||
} else {
|
||
go io.Copy(conn1, conn2)
|
||
return io.Copy(conn2, conn1)
|
||
}
|
||
}
|