1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-12-22 01:57:12 -05:00

reusable connection

This commit is contained in:
v2ray 2016-05-31 00:21:41 +02:00
parent 3a6bf38686
commit 0fac2084c7
12 changed files with 206 additions and 76 deletions

View File

@ -48,8 +48,8 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
_, err := io.ReadFull(reader, buffer.Value[:protocol.IDBytesLen]) _, err := io.ReadFull(reader, buffer.Value[:protocol.IDBytesLen])
if err != nil { if err != nil {
log.Error("Raw: Failed to read request header: ", err) log.Info("Raw: Failed to read request header: ", err)
return nil, err return nil, io.EOF
} }
user, timestamp, valid := this.userValidator.Get(buffer.Value[:protocol.IDBytesLen]) user, timestamp, valid := this.userValidator.Get(buffer.Value[:protocol.IDBytesLen])
@ -77,7 +77,7 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
} }
if request.Version != Version { if request.Version != Version {
log.Warning("Raw: Invalid protocol version ", request.Version) log.Info("Raw: Invalid protocol version ", request.Version)
return nil, protocol.ErrorInvalidVersion return nil, protocol.ErrorInvalidVersion
} }

View File

@ -15,7 +15,7 @@ import (
"github.com/v2ray/v2ray-core/common/retry" "github.com/v2ray/v2ray-core/common/retry"
"github.com/v2ray/v2ray-core/proxy" "github.com/v2ray/v2ray-core/proxy"
"github.com/v2ray/v2ray-core/proxy/internal" "github.com/v2ray/v2ray-core/proxy/internal"
"github.com/v2ray/v2ray-core/transport/dialer" "github.com/v2ray/v2ray-core/transport/hub"
"github.com/v2ray/v2ray-core/transport/ray" "github.com/v2ray/v2ray-core/transport/ray"
) )
@ -77,7 +77,7 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *
destination = this.ResolveIP(destination) destination = this.ResolveIP(destination)
} }
err := retry.Timed(5, 100).On(func() error { err := retry.Timed(5, 100).On(func() error {
rawConn, err := dialer.Dial(destination) rawConn, err := hub.DialWithoutCache(destination)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,6 +1,7 @@
package inbound package inbound
import ( import (
"io"
"sync" "sync"
"github.com/v2ray/v2ray-core/app" "github.com/v2ray/v2ray-core/app"
@ -124,7 +125,7 @@ func (this *VMessInboundHandler) Listen(address v2net.Address, port v2net.Port)
func (this *VMessInboundHandler) HandleConnection(connection *hub.Connection) { func (this *VMessInboundHandler) HandleConnection(connection *hub.Connection) {
defer connection.Close() defer connection.Close()
connReader := v2net.NewTimeOutReader(16, connection) connReader := v2net.NewTimeOutReader(8, connection)
defer connReader.Release() defer connReader.Release()
reader := v2io.NewBufferedReader(connReader) reader := v2io.NewBufferedReader(connReader)
@ -135,13 +136,19 @@ func (this *VMessInboundHandler) HandleConnection(connection *hub.Connection) {
request, err := session.DecodeRequestHeader(reader) request, err := session.DecodeRequestHeader(reader)
if err != nil { if err != nil {
if err != io.EOF {
log.Access(connection.RemoteAddr(), "", log.AccessRejected, err) log.Access(connection.RemoteAddr(), "", log.AccessRejected, err)
log.Warning("VMessIn: Invalid request from ", connection.RemoteAddr(), ": ", err) log.Warning("VMessIn: Invalid request from ", connection.RemoteAddr(), ": ", err)
}
return return
} }
log.Access(connection.RemoteAddr(), request.Destination(), log.AccessAccepted, "") log.Access(connection.RemoteAddr(), request.Destination(), log.AccessAccepted, "")
log.Debug("VMessIn: Received request for ", request.Destination()) log.Debug("VMessIn: Received request for ", request.Destination())
if request.Option.IsChunkStream() {
connection.SetReusable(true)
}
ray := this.packetDispatcher.DispatchToOutbound(request.Destination()) ray := this.packetDispatcher.DispatchToOutbound(request.Destination())
input := ray.InboundInput() input := ray.InboundInput()
output := ray.InboundOutput() output := ray.InboundOutput()

View File

@ -14,7 +14,7 @@ import (
"github.com/v2ray/v2ray-core/proxy" "github.com/v2ray/v2ray-core/proxy"
"github.com/v2ray/v2ray-core/proxy/internal" "github.com/v2ray/v2ray-core/proxy/internal"
vmessio "github.com/v2ray/v2ray-core/proxy/vmess/io" vmessio "github.com/v2ray/v2ray-core/proxy/vmess/io"
"github.com/v2ray/v2ray-core/transport/dialer" "github.com/v2ray/v2ray-core/transport/hub"
"github.com/v2ray/v2ray-core/transport/ray" "github.com/v2ray/v2ray-core/transport/ray"
) )
@ -41,7 +41,7 @@ func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *al
Option: protocol.RequestOptionChunkStream, Option: protocol.RequestOptionChunkStream,
} }
conn, err := dialer.Dial(destination) conn, err := hub.Dial(destination)
if err != nil { if err != nil {
log.Error("Failed to open ", destination, ": ", err) log.Error("Failed to open ", destination, ": ", err)
return err return err
@ -49,6 +49,9 @@ func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *al
log.Info("VMessOut: Tunneling request to ", request.Address, " via ", destination) log.Info("VMessOut: Tunneling request to ", request.Address, " via ", destination)
defer conn.Close() defer conn.Close()
if request.Option.IsChunkStream() {
conn.SetReusable(true)
}
input := ray.OutboundInput() input := ray.OutboundInput()
output := ray.OutboundOutput() output := ray.OutboundOutput()

View File

@ -1,6 +1,9 @@
{ {
"port": 50030, "port": 50030,
"listen": "127.0.0.1", "listen": "127.0.0.1",
"log": {
"loglevel": "debug"
},
"inbound": { "inbound": {
"protocol": "dokodemo-door", "protocol": "dokodemo-door",
"settings": { "settings": {

View File

@ -2,7 +2,7 @@
"port": 50031, "port": 50031,
"listen": "127.0.0.1", "listen": "127.0.0.1",
"log": { "log": {
"loglevel": "warning" "loglevel": "debug"
}, },
"inbound": { "inbound": {
"protocol": "vmess", "protocol": "vmess",

View File

@ -1,40 +0,0 @@
package dialer
import (
"errors"
"net"
"time"
v2net "github.com/v2ray/v2ray-core/common/net"
)
var (
ErrorInvalidHost = errors.New("Invalid Host.")
)
func Dial(dest v2net.Destination) (net.Conn, error) {
if dest.Address().IsDomain() {
dialer := &net.Dialer{
Timeout: time.Second * 60,
DualStack: true,
}
network := "tcp"
if dest.IsUDP() {
network = "udp"
}
return dialer.Dial(network, dest.NetAddr())
}
ip := dest.Address().IP()
if dest.IsTCP() {
return net.DialTCP("tcp", nil, &net.TCPAddr{
IP: ip,
Port: int(dest.Port()),
})
} else {
return net.DialUDP("udp", nil, &net.UDPAddr{
IP: ip,
Port: int(dest.Port()),
})
}
}

View File

@ -7,9 +7,14 @@ import (
type ConnectionHandler func(*Connection) type ConnectionHandler func(*Connection)
type ConnectionManager interface {
Recycle(string, net.Conn)
}
type Connection struct { type Connection struct {
dest string
conn net.Conn conn net.Conn
listener *TCPHub listener ConnectionManager
reusable bool reusable bool
} }
@ -33,22 +38,12 @@ func (this *Connection) Close() error {
return ErrorClosedConnection return ErrorClosedConnection
} }
if this.Reusable() { if this.Reusable() {
this.listener.Recycle(this.conn) this.listener.Recycle(this.dest, this.conn)
return nil return nil
} }
return this.conn.Close() return this.conn.Close()
} }
func (this *Connection) Release() {
if this == nil || this.listener == nil {
return
}
this.Close()
this.conn = nil
this.listener = nil
}
func (this *Connection) LocalAddr() net.Addr { func (this *Connection) LocalAddr() net.Addr {
return this.conn.LocalAddr() return this.conn.LocalAddr()
} }

View File

@ -0,0 +1,107 @@
package hub
import (
"net"
"sync"
"time"
)
type AwaitingConnection struct {
conn net.Conn
expire time.Time
}
func (this *AwaitingConnection) Expired() bool {
return this.expire.Before(time.Now())
}
type ConnectionCache struct {
sync.Mutex
cache map[string][]*AwaitingConnection
}
func NewConnectionCache() *ConnectionCache {
c := &ConnectionCache{
cache: make(map[string][]*AwaitingConnection),
}
go c.Cleanup()
return c
}
func (this *ConnectionCache) Cleanup() {
for {
time.Sleep(time.Second * 4)
this.Lock()
for key, value := range this.cache {
size := len(value)
changed := false
for i := 0; i < size; {
if value[i].Expired() {
value[i].conn.Close()
value[i] = value[size-1]
size--
changed = true
} else {
i++
}
}
if changed {
for i := size; i < len(value); i++ {
value[i] = nil
}
value = value[:size]
this.cache[key] = value
}
}
this.Unlock()
}
}
func (this *ConnectionCache) Recycle(dest string, conn net.Conn) {
this.Lock()
defer this.Unlock()
aconn := &AwaitingConnection{
conn: conn,
expire: time.Now().Add(time.Second * 4),
}
var list []*AwaitingConnection
if v, found := this.cache[dest]; found {
v = append(v, aconn)
list = v
} else {
list = []*AwaitingConnection{aconn}
}
this.cache[dest] = list
}
func FindFirstValid(list []*AwaitingConnection) int {
for idx, conn := range list {
if !conn.Expired() {
return idx
}
conn.conn.Close()
}
return -1
}
func (this *ConnectionCache) Get(dest string) net.Conn {
this.Lock()
defer this.Unlock()
list, found := this.cache[dest]
if !found {
return nil
}
firstValid := FindFirstValid(list)
if firstValid == -1 {
delete(this.cache, dest)
return nil
}
res := list[firstValid].conn
list = list[firstValid+1:]
this.cache[dest] = list
return res
}

63
transport/hub/dialer.go Normal file
View File

@ -0,0 +1,63 @@
package hub
import (
"errors"
"net"
"time"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
)
var (
ErrorInvalidHost = errors.New("Invalid Host.")
globalCache = NewConnectionCache()
)
func Dial(dest v2net.Destination) (*Connection, error) {
destStr := dest.String()
conn := globalCache.Get(destStr)
if conn == nil {
var err error
log.Debug("Hub: Dialling new connection to ", dest)
conn, err = DialWithoutCache(dest)
if err != nil {
return nil, err
}
} else {
log.Debug("Hub: Reusing connection to ", dest)
}
return &Connection{
dest: destStr,
conn: conn,
listener: globalCache,
}, nil
}
func DialWithoutCache(dest v2net.Destination) (net.Conn, error) {
if dest.Address().IsDomain() {
dialer := &net.Dialer{
Timeout: time.Second * 60,
DualStack: true,
}
network := "tcp"
if dest.IsUDP() {
network = "udp"
}
return dialer.Dial(network, dest.NetAddr())
}
ip := dest.Address().IP()
if dest.IsTCP() {
return net.DialTCP("tcp", nil, &net.TCPAddr{
IP: ip,
Port: int(dest.Port()),
})
}
return net.DialUDP("udp", nil, &net.UDPAddr{
IP: ip,
Port: int(dest.Port()),
})
}

View File

@ -1,4 +1,4 @@
package dialer_test package hub_test
import ( import (
"testing" "testing"
@ -7,7 +7,7 @@ import (
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing" v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
"github.com/v2ray/v2ray-core/testing/assert" "github.com/v2ray/v2ray-core/testing/assert"
"github.com/v2ray/v2ray-core/testing/servers/tcp" "github.com/v2ray/v2ray-core/testing/servers/tcp"
. "github.com/v2ray/v2ray-core/transport/dialer" . "github.com/v2ray/v2ray-core/transport/hub"
) )
func TestDialDomain(t *testing.T) { func TestDialDomain(t *testing.T) {

View File

@ -49,24 +49,14 @@ func ListenTCP(address v2net.Address, port v2net.Port, callback ConnectionHandle
} }
func (this *TCPHub) Close() { func (this *TCPHub) Close() {
this.Lock()
defer this.Unlock()
this.accepting = false this.accepting = false
this.listener.Close() this.listener.Close()
this.listener = nil
} }
func (this *TCPHub) start() { func (this *TCPHub) start() {
this.accepting = true this.accepting = true
for this.accepting { for this.accepting {
this.Lock()
if !this.accepting {
this.Unlock()
break
}
conn, err := this.listener.Accept() conn, err := this.listener.Accept()
this.Unlock()
if err != nil { if err != nil {
if this.accepting { if this.accepting {
@ -75,6 +65,7 @@ func (this *TCPHub) start() {
continue continue
} }
go this.connCallback(&Connection{ go this.connCallback(&Connection{
dest: conn.RemoteAddr().String(),
conn: conn, conn: conn,
listener: this, listener: this,
}) })
@ -82,9 +73,10 @@ func (this *TCPHub) start() {
} }
// @Private // @Private
func (this *TCPHub) Recycle(conn net.Conn) { func (this *TCPHub) Recycle(dest string, conn net.Conn) {
if this.accepting { if this.accepting {
go this.connCallback(&Connection{ go this.connCallback(&Connection{
dest: dest,
conn: conn, conn: conn,
listener: this, listener: this,
}) })