mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-07 01:32:57 +08:00
添加readv功能;给出测试数据;修订代码,注释;
toml 新增 app.noreadv项,命令行参数新增 -readv 默认readv是打开状态,可以选择性关闭。
This commit is contained in:
@@ -246,7 +246,7 @@ verysimple -c server.toml
|
||||
|
||||
## 验证方式
|
||||
|
||||
对于功能的golang test,请使用 `go test ./...` 命令。如果要详细的打印出test的过程,可以添加 -v 参数
|
||||
对于功能的golang test,请使用 `go test ./... -count=1` 命令。如果要详细的打印出test的过程,可以添加 -v 参数
|
||||
|
||||
## 关于证书
|
||||
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hahahrfool/v2ray_simple/httpLayer"
|
||||
"github.com/hahahrfool/v2ray_simple/netLayer"
|
||||
"github.com/hahahrfool/v2ray_simple/proxy"
|
||||
"github.com/hahahrfool/v2ray_simple/utils"
|
||||
)
|
||||
@@ -43,6 +44,9 @@ func loadConfig() {
|
||||
if appConf := standardConf.App; appConf != nil {
|
||||
utils.LogLevel = appConf.LogLevel
|
||||
default_uuid = appConf.DefaultUUID
|
||||
if appConf.NoReadV {
|
||||
netLayer.UseReadv = false
|
||||
}
|
||||
}
|
||||
return
|
||||
} else {
|
||||
|
17
main.go
17
main.go
@@ -694,12 +694,13 @@ afterLocalServerHandshake:
|
||||
}
|
||||
|
||||
if theFallbackFirstBuffer != nil {
|
||||
//这里注意,因为是吧tls解密了之后的数据发送到目标地址,所以这种方式只支持转发到本机纯http服务器
|
||||
//这里注意,因为是把 tls解密了之后的数据发送到目标地址,所以这种方式只支持转发到本机纯http服务器
|
||||
wrc.Write(theFallbackFirstBuffer.Bytes())
|
||||
utils.PutBytes(theFallbackFirstBuffer.Bytes()) //这个Buf不是从utils.GetBuf创建的,而是从一个 GetBytes的[]byte 包装 的,所以我们要PutBytes,而不是PutBuf
|
||||
}
|
||||
|
||||
if utils.CanLogDebug() {
|
||||
/*
|
||||
go func() {
|
||||
n, e := io.Copy(wrc, wlc)
|
||||
log.Println("本地->远程 转发结束", realTargetAddr.String(), n, e)
|
||||
@@ -707,11 +708,21 @@ afterLocalServerHandshake:
|
||||
n, e := io.Copy(wlc, wrc)
|
||||
|
||||
log.Println("远程->本地 转发结束", realTargetAddr.String(), n, e)
|
||||
*/
|
||||
|
||||
go func() {
|
||||
n, e := netLayer.TryCopy(wrc, wlc)
|
||||
log.Println("本地->远程 转发结束", realTargetAddr.String(), n, e)
|
||||
}()
|
||||
|
||||
n, e := netLayer.TryCopy(wlc, wrc)
|
||||
log.Println("远程->本地 转发结束", realTargetAddr.String(), n, e)
|
||||
|
||||
} else {
|
||||
//如果两个都是 *net.TCPConn或uds, 则Copy会自动进行splice/sendfile,无需额外处理
|
||||
go io.Copy(wrc, wlc)
|
||||
io.Copy(wlc, wrc)
|
||||
//go io.Copy(wrc, wlc)
|
||||
//io.Copy(wlc, wrc)
|
||||
netLayer.Relay(wlc, wrc)
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package netLayer
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -27,6 +28,10 @@ type Addr struct {
|
||||
Network string
|
||||
}
|
||||
|
||||
func GetRandLocalAddr() string {
|
||||
return "0.0.0.0:" + strconv.Itoa(rand.Intn(60000)+4096)
|
||||
}
|
||||
|
||||
func NewAddrFromUDPAddr(addr *net.UDPAddr) *Addr {
|
||||
return &Addr{
|
||||
IP: addr.IP,
|
||||
|
108
netLayer/readv.go
Normal file
108
netLayer/readv.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package netLayer
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/hahahrfool/v2ray_simple/utils"
|
||||
)
|
||||
|
||||
func GetRawConn(reader io.Reader) syscall.RawConn {
|
||||
if sc, ok := reader.(syscall.Conn); ok {
|
||||
rawConn, err := sc.SyscallConn()
|
||||
if err != nil {
|
||||
if utils.CanLogDebug() {
|
||||
log.Println("can't convert syscall.Conn to syscall.RawConn", reader, err)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return rawConn
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 用于读端实现了 readv但是写端的情况,比如 从socks5读取 数据, 等裸协议的情况
|
||||
// 小贴士:将该 net.Buffers 写入io.Writer的话,只需使用 其WriteTo方法。
|
||||
/*
|
||||
使用方式
|
||||
|
||||
var readConn, writeConn net.Conn
|
||||
//想办法初始化这两个Conn
|
||||
|
||||
rawConn:= netLayer.GetRawConn(readConn)
|
||||
mr:=utils.GetReadVReader()
|
||||
buffers,err:= netLayer.ReadFromMultiReader(rawConn,mr)
|
||||
if err!=nil{
|
||||
log.Fatal("sfdfdaf")
|
||||
}
|
||||
buffers.WriteTo(writeConn)
|
||||
|
||||
包装的代码见 TryCopy 函数; Relay函数也用到了
|
||||
|
||||
*/
|
||||
func ReadFromMultiReader(rawReadConn syscall.RawConn, mr utils.MultiReader) (net.Buffers, error) {
|
||||
//v2ray里还使用了动态分配的方式,我们为了简便先啥也不做
|
||||
bs := make([][]byte, 16)
|
||||
// 实测16个buf已经完全够用,平时也就偶尔遇到5个buf的情况, 极速测速时会占用更多;
|
||||
// 16个1500那就是 24000, 23.4375 KB, 不算小了;
|
||||
for i := range bs {
|
||||
bs[i] = utils.GetMTU()
|
||||
}
|
||||
mr.Init(bs)
|
||||
|
||||
var nBytes int32
|
||||
err := rawReadConn.Read(func(fd uintptr) bool {
|
||||
n := mr.Read(fd)
|
||||
if n < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
nBytes = n
|
||||
return true
|
||||
})
|
||||
mr.Clear()
|
||||
if err != nil {
|
||||
ReleaseNetBuffers(bs)
|
||||
return nil, err
|
||||
}
|
||||
if nBytes == 0 {
|
||||
ReleaseNetBuffers(bs)
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
//删减buffer 到合适的长度,并释放没用到的buf
|
||||
|
||||
nBuf := 0
|
||||
for nBuf < len(bs) {
|
||||
if nBytes <= 0 {
|
||||
break
|
||||
}
|
||||
end := nBytes
|
||||
if end > int32(utils.StandardBytesLength) {
|
||||
end = int32(utils.StandardBytesLength)
|
||||
}
|
||||
bs[nBuf] = bs[nBuf][:end]
|
||||
nBytes -= end
|
||||
nBuf++
|
||||
}
|
||||
/*
|
||||
if utils.CanLogDebug() {
|
||||
// 可用于查看到底用了几个buf, 便于我们调整buf最大长度
|
||||
log.Println("release buf", len(bs)-nBuf)
|
||||
}
|
||||
*/
|
||||
ReleaseNetBuffers(bs[nBuf:])
|
||||
|
||||
return bs[:nBuf], nil
|
||||
}
|
||||
|
||||
func ReleaseNetBuffers(mb [][]byte) {
|
||||
for i := range mb {
|
||||
utils.PutBytes(mb[i])
|
||||
|
||||
}
|
||||
}
|
377
netLayer/readv_test.go
Normal file
377
netLayer/readv_test.go
Normal file
@@ -0,0 +1,377 @@
|
||||
package netLayer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hahahrfool/v2ray_simple/utils"
|
||||
)
|
||||
|
||||
/*
|
||||
我们本地benchmark,实际benchmark readv 是比 经典拷贝慢的
|
||||
|
||||
BenchmarkReadVCopy-8 426525 2934 ns/op
|
||||
BenchmarkClassicCopy-8 531406 2185 ns/op
|
||||
BenchmarkClassicCopy_SimulateRealWorld-8 60873 19631 ns/op
|
||||
BenchmarkClassicCopy_SimulateRealWorld_ReadV-8 66138 17907 ns/op
|
||||
|
||||
|
||||
我们添加一种情况 SimulateRealWorld, 分10次写入, 每次写入长度均小于MTU,此时即可发现readv更快
|
||||
|
||||
总之这种本地benchmark 对于 readv来说意义不大,因为本地回环太快了, readv只能徒增各种附加操作.
|
||||
|
||||
理论上来说,在非本地测试环境下,只要每次传输的包超过了MTU,那么readv就应该是有优势的,包长度越大越有优势,因为越大越容易被割包,那么readv就越好用
|
||||
*/
|
||||
|
||||
//我们不断向一个net.Conn 发送大数据
|
||||
func TestReadVCopy(t *testing.T) {
|
||||
|
||||
listenAddr := GetRandLocalAddr()
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
bigBytes := make([]byte, 10240) //10k
|
||||
|
||||
n, err := rand.Reader.Read(bigBytes)
|
||||
if err != nil || n != 10240 {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
transmitCount := 10
|
||||
|
||||
go func() {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
//t.Log(err)
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < transmitCount; i++ {
|
||||
buf := &bytes.Buffer{}
|
||||
_, err := TryCopyOnce(buf, conn)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "close") {
|
||||
t.Fail()
|
||||
|
||||
}
|
||||
//t.Log(err)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
tcpConn, err := net.Dial("tcp", listenAddr)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
for i := 0; i < transmitCount; i++ {
|
||||
_, e := tcpConn.Write(bigBytes)
|
||||
if e != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
tcpConn.Close()
|
||||
}
|
||||
|
||||
func BenchmarkReadVCopy(b *testing.B) {
|
||||
transmitCount := b.N
|
||||
|
||||
listenAddr := GetRandLocalAddr()
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
bigBytes := make([]byte, 10240) //10k
|
||||
|
||||
n, err := rand.Reader.Read(bigBytes)
|
||||
if err != nil || n != 10240 {
|
||||
log.Fatalln(n, err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
//b.Log(err)
|
||||
b.Fail()
|
||||
}
|
||||
//buf := &bytes.Buffer{}
|
||||
|
||||
for i := 0; i < transmitCount; i++ {
|
||||
buf := utils.GetBuf()
|
||||
_, err := TryCopyOnce(buf, conn)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "close") {
|
||||
b.Fail()
|
||||
|
||||
}
|
||||
}
|
||||
utils.PutBuf(buf)
|
||||
//buf.Reset()
|
||||
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
tcpConn, err := net.Dial("tcp", listenAddr)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
for i := 0; i < transmitCount; i++ {
|
||||
_, e := tcpConn.Write(bigBytes)
|
||||
if e != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
tcpConn.Close()
|
||||
}
|
||||
|
||||
func BenchmarkClassicCopy(b *testing.B) {
|
||||
|
||||
b.StopTimer()
|
||||
b.ResetTimer()
|
||||
|
||||
transmitCount := b.N
|
||||
|
||||
listenAddr := GetRandLocalAddr()
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
const bigBytesLen = 10240
|
||||
|
||||
bigBytes := make([]byte, bigBytesLen) //10k
|
||||
|
||||
n, err := rand.Reader.Read(bigBytes)
|
||||
if err != nil || n != bigBytesLen {
|
||||
log.Fatalln(n, err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
//b.Log(err)
|
||||
b.Fail()
|
||||
}
|
||||
|
||||
//bs := make([]byte, bigBytesLen)
|
||||
//buf := &bytes.Buffer{}
|
||||
// 不能直接使用单一buf,否则对readv来说不公平,必须同样从pool中存取
|
||||
|
||||
for {
|
||||
|
||||
buf := utils.GetBuf()
|
||||
//bs := utils.GetMTU()
|
||||
bs := utils.GetPacket()
|
||||
|
||||
n, err := conn.Read(bs)
|
||||
|
||||
if err != nil {
|
||||
if err != io.EOF && !strings.Contains(err.Error(), "close") {
|
||||
b.Fail()
|
||||
}
|
||||
return
|
||||
}
|
||||
buf.Write(bs[:n])
|
||||
utils.PutBuf(buf)
|
||||
|
||||
utils.PutPacket(bs)
|
||||
//utils.PutBytes(bs)
|
||||
|
||||
//buf.Reset()
|
||||
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
tcpConn, err := net.Dial("tcp", listenAddr)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < transmitCount; i++ {
|
||||
_, e := tcpConn.Write(bigBytes)
|
||||
if e != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
tcpConn.Close()
|
||||
}
|
||||
|
||||
func BenchmarkClassicCopy_SimulateRealWorld(b *testing.B) {
|
||||
b.StopTimer()
|
||||
b.ResetTimer()
|
||||
|
||||
transmitCount := b.N
|
||||
|
||||
listenAddr := GetRandLocalAddr()
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
b.Log(err)
|
||||
b.FailNow()
|
||||
}
|
||||
|
||||
const bigBytesLen = 10240
|
||||
|
||||
bigBytes := make([]byte, bigBytesLen) //10k
|
||||
|
||||
n, err := rand.Reader.Read(bigBytes)
|
||||
if err != nil || n != bigBytesLen {
|
||||
b.Log(err)
|
||||
b.FailNow()
|
||||
}
|
||||
|
||||
go func() {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
b.Log(err)
|
||||
b.Fail()
|
||||
return
|
||||
}
|
||||
|
||||
//bs := make([]byte, bigBytesLen)
|
||||
|
||||
for {
|
||||
//bs := utils.GetMTU()
|
||||
bs := utils.GetPacket()
|
||||
|
||||
n, err := conn.Read(bs)
|
||||
|
||||
if err != nil {
|
||||
if err != io.EOF && !strings.Contains(err.Error(), "close") {
|
||||
b.Log(err)
|
||||
b.Fail()
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
buf := utils.GetBuf()
|
||||
buf.Write(bs[:n])
|
||||
|
||||
utils.PutBuf(buf)
|
||||
|
||||
utils.PutPacket(bs)
|
||||
|
||||
//utils.PutBytes(bs)
|
||||
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
tcpConn, err := net.Dial("tcp", listenAddr)
|
||||
if err != nil {
|
||||
b.Log(err)
|
||||
b.FailNow()
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < transmitCount; i++ {
|
||||
unit := bigBytesLen / 10
|
||||
|
||||
for cursor := 0; cursor < bigBytesLen; cursor += unit {
|
||||
_, e := tcpConn.Write(bigBytes[cursor : cursor+unit])
|
||||
if e != nil {
|
||||
//log.Fatalln(err)
|
||||
b.Log(err)
|
||||
b.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
tcpConn.Close()
|
||||
}
|
||||
|
||||
func BenchmarkClassicCopy_SimulateRealWorld_ReadV(b *testing.B) {
|
||||
b.StopTimer()
|
||||
b.ResetTimer()
|
||||
|
||||
transmitCount := b.N
|
||||
|
||||
listenAddr := GetRandLocalAddr()
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
b.Log(err)
|
||||
b.FailNow()
|
||||
}
|
||||
|
||||
const bigBytesLen = 10240
|
||||
|
||||
bigBytes := make([]byte, bigBytesLen) //10k
|
||||
|
||||
n, err := rand.Reader.Read(bigBytes)
|
||||
if err != nil || n != bigBytesLen {
|
||||
b.Log(err)
|
||||
b.FailNow()
|
||||
}
|
||||
|
||||
go func() {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
b.Log(err)
|
||||
b.Fail()
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
|
||||
//buf := &bytes.Buffer{}
|
||||
buf := utils.GetBuf()
|
||||
_, err := TryCopyOnce(buf, conn)
|
||||
|
||||
if err != nil {
|
||||
if err != io.EOF && strings.Contains(err.Error(), "close") {
|
||||
b.Fail()
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
utils.PutBuf(buf)
|
||||
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
tcpConn, err := net.Dial("tcp", listenAddr)
|
||||
if err != nil {
|
||||
b.Log(err)
|
||||
b.FailNow()
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < transmitCount; i++ {
|
||||
unit := bigBytesLen / 10
|
||||
|
||||
for cursor := 0; cursor < bigBytesLen; cursor += unit {
|
||||
_, e := tcpConn.Write(bigBytes[cursor : cursor+unit])
|
||||
if e != nil {
|
||||
//log.Fatalln(err)
|
||||
b.Log(err)
|
||||
b.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
tcpConn.Close()
|
||||
}
|
@@ -1,14 +1,161 @@
|
||||
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的 都算
|
||||
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,但是只会读写一次
|
||||
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))
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
copy:
|
||||
|
||||
//Copy内部实现 会自动进行splice, 若无splice实现则直接使用原始方法 “循环读取 并 写入”
|
||||
return io.Copy(writeConn, readConn)
|
||||
}
|
||||
|
||||
// 从conn1读取 写入到 conn2,并同时从 conn2读取写入conn1
|
||||
// 阻塞
|
||||
func RelayTCP(conn1, conn2 net.Conn) {
|
||||
go io.Copy(conn2, conn1)
|
||||
io.Copy(conn1, conn2)
|
||||
// 返回从 conn1读取 写入到 conn2的数据
|
||||
// UseReadv==true 时 内部使用 TryCopy 进行拷贝
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 阻塞.
|
||||
|
@@ -76,6 +76,7 @@ type AppConf struct {
|
||||
DefaultUUID string `toml:"default_uuid"`
|
||||
MyCountryISO_3166 string `toml:"mycountry" json:"mycountry"` //加了mycountry后,就会自动按照geoip分流,也会对顶级域名进行国别分流
|
||||
|
||||
NoReadV bool `toml:"noreadv"`
|
||||
}
|
||||
|
||||
type RuleConf struct {
|
||||
|
@@ -89,5 +89,3 @@ func (s *Server) Handshake(underlay net.Conn) (io.ReadWriter, *netLayer.Addr, er
|
||||
func (s *Server) CanFallback() bool {
|
||||
return false
|
||||
}
|
||||
func (s *Server) Stop() {
|
||||
}
|
||||
|
@@ -53,9 +53,7 @@ func (_ Server) CanFallback() bool {
|
||||
func (_ Server) Name() string {
|
||||
return name
|
||||
}
|
||||
func (_ Server) Stop() {
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Server) Handshake(underlay net.Conn) (newconn io.ReadWriter, targetAddr *netLayer.Addr, err error) {
|
||||
var b = utils.GetBytes(300) //一般要获取请求信息,不需要那么长; 就算是http,加了path,path也不会上千字节吧
|
||||
|
||||
|
@@ -161,6 +161,9 @@ func prepareTLS_forProxyCommon_withURL(u *url.URL, isclient bool, com ProxyCommo
|
||||
}
|
||||
|
||||
// ProxyCommonStruct 实现 ProxyCommon中除了Name 之外的其他方法
|
||||
// 规定,所有的proxy都要内嵌本struct
|
||||
// 这是verysimple的架构所要求的。verysimple规定,在加载完配置文件后,一个listen和一个dial所使用的全部层级都是确定了的
|
||||
// 因为所有使用的层级都是确定的,就可以进行针对性优化
|
||||
type ProxyCommonStruct struct {
|
||||
Addr string
|
||||
TLS bool
|
||||
@@ -230,6 +233,10 @@ func (pcs *ProxyCommonStruct) AdvancedLayer() string {
|
||||
return pcs.AdvancedL
|
||||
}
|
||||
|
||||
//do nothing. As a placeholder.
|
||||
func (s *ProxyCommonStruct) Stop() {
|
||||
}
|
||||
|
||||
// 从 url 初始化一些通用的配置,目前只有 u.Host
|
||||
func (pcs *ProxyCommonStruct) InitFromUrl(u *url.URL) {
|
||||
pcs.Addr = u.Host
|
||||
|
@@ -59,9 +59,6 @@ func (s *Server) Name() string { return Name }
|
||||
func (s *Server) CanFallback() bool {
|
||||
return false
|
||||
}
|
||||
func (s *Server) Stop() {
|
||||
// Nothing to stop or close
|
||||
}
|
||||
|
||||
//English: https://www.ietf.org/rfc/rfc1928.txt
|
||||
|
||||
|
@@ -386,15 +386,13 @@ realPart:
|
||||
return &UserConn{
|
||||
Conn: underlay,
|
||||
optionalReader: io.MultiReader(readbuf, underlay),
|
||||
remainFirstBufLen: readbuf.Len(),
|
||||
uuid: thisUUIDBytes,
|
||||
version: int(version),
|
||||
isUDP: addr.Network == "udp",
|
||||
isServerEnd: true,
|
||||
}, addr, nil
|
||||
|
||||
}
|
||||
func (s *Server) Stop() {
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) Get_CRUMFURS(id string) *CRUMFURS {
|
||||
|
@@ -32,7 +32,10 @@ const (
|
||||
|
||||
type UserConn struct {
|
||||
net.Conn
|
||||
optionalReader io.Reader //在使用了缓存读取包头后,就产生了buffer中有剩余数据的可能性,此时就要使用MultiReader
|
||||
optionalReader io.Reader //在使用了缓存读取握手包头后,就产生了buffer中有剩余数据的可能性,此时就要使用MultiReader
|
||||
|
||||
remainFirstBufLen int //记录读取握手包头时读到的buf的长度. 如果我们读超过了这个部分的话,实际上我们就可以不再使用 optionalReader 读取, 而是直接从Conn读取
|
||||
|
||||
uuid [16]byte
|
||||
convertedStr string
|
||||
version int
|
||||
@@ -170,6 +173,7 @@ func (uc *UserConn) Read(p []byte) (int, error) {
|
||||
if !uc.isUDP {
|
||||
|
||||
if !uc.isServerEnd && !uc.isntFirstPacket {
|
||||
//先读取响应头
|
||||
|
||||
uc.isntFirstPacket = true
|
||||
|
||||
|
@@ -6,20 +6,23 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
standardBytesPool sync.Pool //1500, 最大MTU的长度
|
||||
standardBytesPool sync.Pool //专门储存 长度为 StandardBytesLength 的 []byte
|
||||
|
||||
// 作为参考对比,tcp默认是 16384, 16k,实际上范围是1k~128k之间
|
||||
// 而 udp则最大还不到 64k。(65535-20-8)
|
||||
// io.Copy 内部默认buffer大小为 32k
|
||||
// 总之 我们64k已经够了
|
||||
standardPacketPool sync.Pool //64*1024
|
||||
standardPacketPool sync.Pool // 专门储存 长度为 MaxBufLen 的 []byte
|
||||
|
||||
bufPool sync.Pool
|
||||
bufPool sync.Pool //储存 *bytes.Buffer
|
||||
)
|
||||
|
||||
//即MTU
|
||||
//即MTU, Maximum transmission unit, 参照的是 Ethernet v2 的MTU;
|
||||
const StandardBytesLength int = 1500
|
||||
|
||||
//注意wifi信号MTU是 2304,我们并未考虑wifi,主要是因为就算用wifi传, 早晚还是要经过以太网,除非两个wifi设备互传
|
||||
// https://en.wikipedia.org/wiki/Maximum_transmission_unit
|
||||
|
||||
//本作设定的最大buf大小,64k
|
||||
const MaxBufLen = 64 * 1024
|
||||
|
||||
@@ -52,16 +55,16 @@ func PutBuf(buf *bytes.Buffer) {
|
||||
bufPool.Put(buf)
|
||||
}
|
||||
|
||||
//建议在 Read net.Conn 时, 使用 GetPacket函数 获取到足够大的 []byte(MaxBufLen, 64*1024字节)
|
||||
//建议在 Read net.Conn 时, 使用 GetPacket函数 获取到足够大的 []byte(MaxBufLen)
|
||||
func GetPacket() []byte {
|
||||
return standardPacketPool.Get().([]byte)
|
||||
}
|
||||
|
||||
// 放回用 GetPacket 获取的 []byte, 或者长度够长的可回收的 []byte
|
||||
// 放回用 GetPacket 获取的 []byte
|
||||
func PutPacket(bs []byte) {
|
||||
c := cap(bs)
|
||||
if c < MaxBufLen { //如果不够大,考虑放到更小的 pool里
|
||||
if c > StandardBytesLength {
|
||||
if c < MaxBufLen {
|
||||
if c >= StandardBytesLength {
|
||||
standardBytesPool.Put(bs[:c])
|
||||
}
|
||||
return
|
||||
@@ -70,21 +73,19 @@ func PutPacket(bs []byte) {
|
||||
standardPacketPool.Put(bs[:c])
|
||||
}
|
||||
|
||||
// 从pool中获取 []byte, 在 size <= 1500时有最佳性能
|
||||
func GetMTU() []byte {
|
||||
return standardBytesPool.Get().([]byte)[:StandardBytesLength]
|
||||
}
|
||||
|
||||
// 从pool中获取 []byte, 在 size <= StandardBytesLength 时有最佳性能
|
||||
// 否则会直接调用 GetPacket
|
||||
func GetBytes(size int) []byte {
|
||||
if size < StandardBytesLength {
|
||||
if size <= StandardBytesLength {
|
||||
bs := standardBytesPool.Get().([]byte)
|
||||
return bs[:size]
|
||||
}
|
||||
|
||||
randomBytes1 := standardBytesPool.Get().([]byte)
|
||||
|
||||
if len(randomBytes1) >= size {
|
||||
return randomBytes1[:size]
|
||||
} else {
|
||||
standardBytesPool.Put(randomBytes1)
|
||||
return make([]byte, size)
|
||||
}
|
||||
return GetPacket()[:size]
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1,2 +1,9 @@
|
||||
// Package utils provides utils that needed by all packages of verysimle
|
||||
package utils
|
||||
|
||||
//具体实现见 readv_*.go
|
||||
type MultiReader interface {
|
||||
Init([][]byte)
|
||||
Read(fd uintptr) int32
|
||||
Clear()
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ func (c *Conn) Read(p []byte) (int, error) {
|
||||
*/
|
||||
|
||||
//log.Println("OpCode not Binary", h.OpCode)
|
||||
return 0, utils.NewDataErr("ws first OpCode not Binary", nil, h.OpCode)
|
||||
return 0, utils.NewDataErr("ws OpCode not Binary", nil, h.OpCode)
|
||||
}
|
||||
//log.Println("Read next frame header ok,", h.Length, c.r.State.Fragmented(), "givenbuf len", len(p))
|
||||
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// ws基本读写功能测试.
|
||||
// 分别测试写入短数据和长数据
|
||||
func TestWs(t *testing.T) {
|
||||
listenAddr := "127.0.0.1:7777"
|
||||
|
Reference in New Issue
Block a user