mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-08 02:00:33 +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"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/hahahrfool/v2ray_simple/httpLayer"
|
"github.com/hahahrfool/v2ray_simple/httpLayer"
|
||||||
|
"github.com/hahahrfool/v2ray_simple/netLayer"
|
||||||
"github.com/hahahrfool/v2ray_simple/proxy"
|
"github.com/hahahrfool/v2ray_simple/proxy"
|
||||||
"github.com/hahahrfool/v2ray_simple/utils"
|
"github.com/hahahrfool/v2ray_simple/utils"
|
||||||
)
|
)
|
||||||
@@ -43,6 +44,9 @@ func loadConfig() {
|
|||||||
if appConf := standardConf.App; appConf != nil {
|
if appConf := standardConf.App; appConf != nil {
|
||||||
utils.LogLevel = appConf.LogLevel
|
utils.LogLevel = appConf.LogLevel
|
||||||
default_uuid = appConf.DefaultUUID
|
default_uuid = appConf.DefaultUUID
|
||||||
|
if appConf.NoReadV {
|
||||||
|
netLayer.UseReadv = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
21
main.go
21
main.go
@@ -694,24 +694,35 @@ afterLocalServerHandshake:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if theFallbackFirstBuffer != nil {
|
if theFallbackFirstBuffer != nil {
|
||||||
//这里注意,因为是吧tls解密了之后的数据发送到目标地址,所以这种方式只支持转发到本机纯http服务器
|
//这里注意,因为是把 tls解密了之后的数据发送到目标地址,所以这种方式只支持转发到本机纯http服务器
|
||||||
wrc.Write(theFallbackFirstBuffer.Bytes())
|
wrc.Write(theFallbackFirstBuffer.Bytes())
|
||||||
utils.PutBytes(theFallbackFirstBuffer.Bytes()) //这个Buf不是从utils.GetBuf创建的,而是从一个 GetBytes的[]byte 包装 的,所以我们要PutBytes,而不是PutBuf
|
utils.PutBytes(theFallbackFirstBuffer.Bytes()) //这个Buf不是从utils.GetBuf创建的,而是从一个 GetBytes的[]byte 包装 的,所以我们要PutBytes,而不是PutBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
if utils.CanLogDebug() {
|
if utils.CanLogDebug() {
|
||||||
|
/*
|
||||||
|
go func() {
|
||||||
|
n, e := io.Copy(wrc, wlc)
|
||||||
|
log.Println("本地->远程 转发结束", realTargetAddr.String(), n, e)
|
||||||
|
}()
|
||||||
|
n, e := io.Copy(wlc, wrc)
|
||||||
|
|
||||||
|
log.Println("远程->本地 转发结束", realTargetAddr.String(), n, e)
|
||||||
|
*/
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
n, e := io.Copy(wrc, wlc)
|
n, e := netLayer.TryCopy(wrc, wlc)
|
||||||
log.Println("本地->远程 转发结束", realTargetAddr.String(), n, e)
|
log.Println("本地->远程 转发结束", realTargetAddr.String(), n, e)
|
||||||
}()
|
}()
|
||||||
n, e := io.Copy(wlc, wrc)
|
|
||||||
|
|
||||||
|
n, e := netLayer.TryCopy(wlc, wrc)
|
||||||
log.Println("远程->本地 转发结束", realTargetAddr.String(), n, e)
|
log.Println("远程->本地 转发结束", realTargetAddr.String(), n, e)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
//如果两个都是 *net.TCPConn或uds, 则Copy会自动进行splice/sendfile,无需额外处理
|
//如果两个都是 *net.TCPConn或uds, 则Copy会自动进行splice/sendfile,无需额外处理
|
||||||
go io.Copy(wrc, wlc)
|
//go io.Copy(wrc, wlc)
|
||||||
io.Copy(wlc, wrc)
|
//io.Copy(wlc, wrc)
|
||||||
|
netLayer.Relay(wlc, wrc)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package netLayer
|
package netLayer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -27,6 +28,10 @@ type Addr struct {
|
|||||||
Network string
|
Network string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetRandLocalAddr() string {
|
||||||
|
return "0.0.0.0:" + strconv.Itoa(rand.Intn(60000)+4096)
|
||||||
|
}
|
||||||
|
|
||||||
func NewAddrFromUDPAddr(addr *net.UDPAddr) *Addr {
|
func NewAddrFromUDPAddr(addr *net.UDPAddr) *Addr {
|
||||||
return &Addr{
|
return &Addr{
|
||||||
IP: addr.IP,
|
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
|
package netLayer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"net"
|
"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) {
|
// 返回从 conn1读取 写入到 conn2的数据
|
||||||
go io.Copy(conn2, conn1)
|
// UseReadv==true 时 内部使用 TryCopy 进行拷贝
|
||||||
io.Copy(conn1, conn2)
|
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"`
|
DefaultUUID string `toml:"default_uuid"`
|
||||||
MyCountryISO_3166 string `toml:"mycountry" json:"mycountry"` //加了mycountry后,就会自动按照geoip分流,也会对顶级域名进行国别分流
|
MyCountryISO_3166 string `toml:"mycountry" json:"mycountry"` //加了mycountry后,就会自动按照geoip分流,也会对顶级域名进行国别分流
|
||||||
|
|
||||||
|
NoReadV bool `toml:"noreadv"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleConf struct {
|
type RuleConf struct {
|
||||||
|
@@ -89,5 +89,3 @@ func (s *Server) Handshake(underlay net.Conn) (io.ReadWriter, *netLayer.Addr, er
|
|||||||
func (s *Server) CanFallback() bool {
|
func (s *Server) CanFallback() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
func (s *Server) Stop() {
|
|
||||||
}
|
|
||||||
|
@@ -53,9 +53,7 @@ func (_ Server) CanFallback() bool {
|
|||||||
func (_ Server) Name() string {
|
func (_ Server) Name() string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
func (_ Server) Stop() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
func (s *Server) Handshake(underlay net.Conn) (newconn io.ReadWriter, targetAddr *netLayer.Addr, err error) {
|
func (s *Server) Handshake(underlay net.Conn) (newconn io.ReadWriter, targetAddr *netLayer.Addr, err error) {
|
||||||
var b = utils.GetBytes(300) //一般要获取请求信息,不需要那么长; 就算是http,加了path,path也不会上千字节吧
|
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 之外的其他方法
|
// ProxyCommonStruct 实现 ProxyCommon中除了Name 之外的其他方法
|
||||||
|
// 规定,所有的proxy都要内嵌本struct
|
||||||
|
// 这是verysimple的架构所要求的。verysimple规定,在加载完配置文件后,一个listen和一个dial所使用的全部层级都是确定了的
|
||||||
|
// 因为所有使用的层级都是确定的,就可以进行针对性优化
|
||||||
type ProxyCommonStruct struct {
|
type ProxyCommonStruct struct {
|
||||||
Addr string
|
Addr string
|
||||||
TLS bool
|
TLS bool
|
||||||
@@ -230,6 +233,10 @@ func (pcs *ProxyCommonStruct) AdvancedLayer() string {
|
|||||||
return pcs.AdvancedL
|
return pcs.AdvancedL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//do nothing. As a placeholder.
|
||||||
|
func (s *ProxyCommonStruct) Stop() {
|
||||||
|
}
|
||||||
|
|
||||||
// 从 url 初始化一些通用的配置,目前只有 u.Host
|
// 从 url 初始化一些通用的配置,目前只有 u.Host
|
||||||
func (pcs *ProxyCommonStruct) InitFromUrl(u *url.URL) {
|
func (pcs *ProxyCommonStruct) InitFromUrl(u *url.URL) {
|
||||||
pcs.Addr = u.Host
|
pcs.Addr = u.Host
|
||||||
|
@@ -59,9 +59,6 @@ func (s *Server) Name() string { return Name }
|
|||||||
func (s *Server) CanFallback() bool {
|
func (s *Server) CanFallback() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
func (s *Server) Stop() {
|
|
||||||
// Nothing to stop or close
|
|
||||||
}
|
|
||||||
|
|
||||||
//English: https://www.ietf.org/rfc/rfc1928.txt
|
//English: https://www.ietf.org/rfc/rfc1928.txt
|
||||||
|
|
||||||
|
@@ -384,17 +384,15 @@ realPart:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &UserConn{
|
return &UserConn{
|
||||||
Conn: underlay,
|
Conn: underlay,
|
||||||
optionalReader: io.MultiReader(readbuf, underlay),
|
optionalReader: io.MultiReader(readbuf, underlay),
|
||||||
uuid: thisUUIDBytes,
|
remainFirstBufLen: readbuf.Len(),
|
||||||
version: int(version),
|
uuid: thisUUIDBytes,
|
||||||
isUDP: addr.Network == "udp",
|
version: int(version),
|
||||||
isServerEnd: true,
|
isUDP: addr.Network == "udp",
|
||||||
|
isServerEnd: true,
|
||||||
}, addr, nil
|
}, addr, nil
|
||||||
|
|
||||||
}
|
|
||||||
func (s *Server) Stop() {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Get_CRUMFURS(id string) *CRUMFURS {
|
func (s *Server) Get_CRUMFURS(id string) *CRUMFURS {
|
||||||
|
@@ -32,12 +32,15 @@ const (
|
|||||||
|
|
||||||
type UserConn struct {
|
type UserConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
optionalReader io.Reader //在使用了缓存读取包头后,就产生了buffer中有剩余数据的可能性,此时就要使用MultiReader
|
optionalReader io.Reader //在使用了缓存读取握手包头后,就产生了buffer中有剩余数据的可能性,此时就要使用MultiReader
|
||||||
uuid [16]byte
|
|
||||||
convertedStr string
|
remainFirstBufLen int //记录读取握手包头时读到的buf的长度. 如果我们读超过了这个部分的话,实际上我们就可以不再使用 optionalReader 读取, 而是直接从Conn读取
|
||||||
version int
|
|
||||||
isUDP bool
|
uuid [16]byte
|
||||||
isServerEnd bool //for v0
|
convertedStr string
|
||||||
|
version int
|
||||||
|
isUDP bool
|
||||||
|
isServerEnd bool //for v0
|
||||||
|
|
||||||
// udpUnreadPart 不为空,则表示上一次读取没读完整个包(给Read传入的buf太小),须接着读
|
// udpUnreadPart 不为空,则表示上一次读取没读完整个包(给Read传入的buf太小),须接着读
|
||||||
udpUnreadPart []byte //for udp
|
udpUnreadPart []byte //for udp
|
||||||
@@ -170,6 +173,7 @@ func (uc *UserConn) Read(p []byte) (int, error) {
|
|||||||
if !uc.isUDP {
|
if !uc.isUDP {
|
||||||
|
|
||||||
if !uc.isServerEnd && !uc.isntFirstPacket {
|
if !uc.isServerEnd && !uc.isntFirstPacket {
|
||||||
|
//先读取响应头
|
||||||
|
|
||||||
uc.isntFirstPacket = true
|
uc.isntFirstPacket = true
|
||||||
|
|
||||||
|
@@ -6,20 +6,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
standardBytesPool sync.Pool //1500, 最大MTU的长度
|
standardBytesPool sync.Pool //专门储存 长度为 StandardBytesLength 的 []byte
|
||||||
|
|
||||||
// 作为参考对比,tcp默认是 16384, 16k,实际上范围是1k~128k之间
|
// 作为参考对比,tcp默认是 16384, 16k,实际上范围是1k~128k之间
|
||||||
// 而 udp则最大还不到 64k。(65535-20-8)
|
// 而 udp则最大还不到 64k。(65535-20-8)
|
||||||
// io.Copy 内部默认buffer大小为 32k
|
// io.Copy 内部默认buffer大小为 32k
|
||||||
// 总之 我们64k已经够了
|
// 总之 我们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
|
const StandardBytesLength int = 1500
|
||||||
|
|
||||||
|
//注意wifi信号MTU是 2304,我们并未考虑wifi,主要是因为就算用wifi传, 早晚还是要经过以太网,除非两个wifi设备互传
|
||||||
|
// https://en.wikipedia.org/wiki/Maximum_transmission_unit
|
||||||
|
|
||||||
//本作设定的最大buf大小,64k
|
//本作设定的最大buf大小,64k
|
||||||
const MaxBufLen = 64 * 1024
|
const MaxBufLen = 64 * 1024
|
||||||
|
|
||||||
@@ -52,16 +55,16 @@ func PutBuf(buf *bytes.Buffer) {
|
|||||||
bufPool.Put(buf)
|
bufPool.Put(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
//建议在 Read net.Conn 时, 使用 GetPacket函数 获取到足够大的 []byte(MaxBufLen, 64*1024字节)
|
//建议在 Read net.Conn 时, 使用 GetPacket函数 获取到足够大的 []byte(MaxBufLen)
|
||||||
func GetPacket() []byte {
|
func GetPacket() []byte {
|
||||||
return standardPacketPool.Get().([]byte)
|
return standardPacketPool.Get().([]byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 放回用 GetPacket 获取的 []byte, 或者长度够长的可回收的 []byte
|
// 放回用 GetPacket 获取的 []byte
|
||||||
func PutPacket(bs []byte) {
|
func PutPacket(bs []byte) {
|
||||||
c := cap(bs)
|
c := cap(bs)
|
||||||
if c < MaxBufLen { //如果不够大,考虑放到更小的 pool里
|
if c < MaxBufLen {
|
||||||
if c > StandardBytesLength {
|
if c >= StandardBytesLength {
|
||||||
standardBytesPool.Put(bs[:c])
|
standardBytesPool.Put(bs[:c])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -70,21 +73,19 @@ func PutPacket(bs []byte) {
|
|||||||
standardPacketPool.Put(bs[:c])
|
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 {
|
func GetBytes(size int) []byte {
|
||||||
if size < StandardBytesLength {
|
if size <= StandardBytesLength {
|
||||||
bs := standardBytesPool.Get().([]byte)
|
bs := standardBytesPool.Get().([]byte)
|
||||||
return bs[:size]
|
return bs[:size]
|
||||||
}
|
}
|
||||||
|
|
||||||
randomBytes1 := standardBytesPool.Get().([]byte)
|
return GetPacket()[:size]
|
||||||
|
|
||||||
if len(randomBytes1) >= size {
|
|
||||||
return randomBytes1[:size]
|
|
||||||
} else {
|
|
||||||
standardBytesPool.Put(randomBytes1)
|
|
||||||
return make([]byte, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,2 +1,9 @@
|
|||||||
// Package utils provides utils that needed by all packages of verysimle
|
// Package utils provides utils that needed by all packages of verysimle
|
||||||
package utils
|
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)
|
//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))
|
//log.Println("Read next frame header ok,", h.Length, c.r.State.Fragmented(), "givenbuf len", len(p))
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ws基本读写功能测试.
|
||||||
// 分别测试写入短数据和长数据
|
// 分别测试写入短数据和长数据
|
||||||
func TestWs(t *testing.T) {
|
func TestWs(t *testing.T) {
|
||||||
listenAddr := "127.0.0.1:7777"
|
listenAddr := "127.0.0.1:7777"
|
||||||
|
Reference in New Issue
Block a user