mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-30 05:56:54 -05:00
Merge pull request #170 from xiaokangwang/kcptransport
Add KCP(UDPSession) as a transport for v2ray, reslove #162
This commit is contained in:
commit
22ce652a25
@ -18,11 +18,15 @@ type InboundHandlerMeta struct {
|
||||
Tag string
|
||||
Address v2net.Address
|
||||
Port v2net.Port
|
||||
//Whether this proxy support KCP connections
|
||||
KcpSupported bool
|
||||
}
|
||||
|
||||
type OutboundHandlerMeta struct {
|
||||
Tag string
|
||||
Address v2net.Address
|
||||
//Whether this proxy support KCP connections
|
||||
KcpSupported bool
|
||||
}
|
||||
|
||||
// An InboundHandler handles inbound network connections to V2Ray.
|
||||
|
@ -106,7 +106,7 @@ func (this *VMessInboundHandler) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
tcpListener, err := hub.ListenTCP(this.meta.Address, this.meta.Port, this.HandleConnection, nil)
|
||||
tcpListener, err := hub.ListenTCP6(this.meta.Address, this.meta.Port, this.HandleConnection, this.meta, nil)
|
||||
if err != nil {
|
||||
log.Error("Unable to listen tcp ", this.meta.Address, ":", this.meta.Port, ": ", err)
|
||||
return err
|
||||
@ -220,7 +220,9 @@ func (this *VMessInboundHandler) HandleConnection(connection *hub.Connection) {
|
||||
|
||||
readFinish.Lock()
|
||||
}
|
||||
|
||||
func (this *VMessInboundHandler) setProxyCap() {
|
||||
this.meta.KcpSupported = true
|
||||
}
|
||||
func init() {
|
||||
internal.MustRegisterInboundHandlerCreator("vmess",
|
||||
func(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {
|
||||
@ -246,6 +248,8 @@ func init() {
|
||||
handler.inboundHandlerManager = space.GetApp(proxyman.APP_ID_INBOUND_MANAGER).(proxyman.InboundHandlerManager)
|
||||
}
|
||||
|
||||
handler.setProxyCap()
|
||||
|
||||
return handler, nil
|
||||
})
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *al
|
||||
|
||||
err := retry.Timed(5, 100).On(func() error {
|
||||
rec = this.receiverManager.PickReceiver()
|
||||
rawConn, err := hub.Dial(this.meta.Address, rec.Destination)
|
||||
rawConn, err := hub.Dial3(this.meta.Address, rec.Destination, this.meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -154,14 +154,21 @@ func (this *VMessOutboundHandler) handleResponse(session *raw.ClientSession, con
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *VMessOutboundHandler) setProxyCap() {
|
||||
this.meta.KcpSupported = true
|
||||
}
|
||||
func init() {
|
||||
internal.MustRegisterOutboundHandlerCreator("vmess",
|
||||
func(space app.Space, rawConfig interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {
|
||||
vOutConfig := rawConfig.(*Config)
|
||||
return &VMessOutboundHandler{
|
||||
|
||||
handler := &VMessOutboundHandler{
|
||||
receiverManager: NewReceiverManager(vOutConfig.Receivers),
|
||||
meta: meta,
|
||||
}, nil
|
||||
}
|
||||
|
||||
handler.setProxyCap()
|
||||
|
||||
return handler, nil
|
||||
})
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
package transport
|
||||
|
||||
import "github.com/v2ray/v2ray-core/transport/hub/kcpv"
|
||||
|
||||
// Config for V2Ray transport layer.
|
||||
type Config struct {
|
||||
ConnectionReuse bool
|
||||
enableKcp bool
|
||||
kcpConfig *kcpv.Config
|
||||
}
|
||||
|
||||
// Apply applies this Config.
|
||||
@ -10,5 +14,17 @@ func (this *Config) Apply() error {
|
||||
if this.ConnectionReuse {
|
||||
connectionReuse = true
|
||||
}
|
||||
enableKcp = this.enableKcp
|
||||
if enableKcp {
|
||||
KcpConfig = this.kcpConfig
|
||||
/*
|
||||
KCP do not support connectionReuse,
|
||||
it is mandatory to set connectionReuse to false
|
||||
Since KCP have no handshake and
|
||||
does not SlowStart, there isn't benefit to
|
||||
use that anyway.
|
||||
*/
|
||||
connectionReuse = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -2,18 +2,38 @@
|
||||
|
||||
package transport
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
"github.com/v2ray/v2ray-core/transport/hub/kcpv"
|
||||
)
|
||||
|
||||
func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
type JsonConfig struct {
|
||||
ConnectionReuse bool `json:"connectionReuse"`
|
||||
ConnectionReuse bool `json:"connectionReuse"`
|
||||
EnableKcp bool `json:"EnableKCP,omitempty"`
|
||||
KcpConfig *kcpv.Config `json:"KcpConfig,omitempty"`
|
||||
}
|
||||
jsonConfig := &JsonConfig{
|
||||
ConnectionReuse: true,
|
||||
EnableKcp: false,
|
||||
}
|
||||
if err := json.Unmarshal(data, jsonConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
this.ConnectionReuse = jsonConfig.ConnectionReuse
|
||||
this.enableKcp = jsonConfig.EnableKcp
|
||||
if jsonConfig.KcpConfig != nil {
|
||||
this.kcpConfig = jsonConfig.KcpConfig
|
||||
if jsonConfig.KcpConfig.AdvancedConfigs == nil {
|
||||
jsonConfig.KcpConfig.AdvancedConfigs = kcpv.DefaultAdvancedConfigs
|
||||
}
|
||||
} else {
|
||||
if jsonConfig.EnableKcp {
|
||||
log.Error("transport: You have enabled KCP but no configure is given")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/transport"
|
||||
)
|
||||
|
||||
@ -62,3 +63,40 @@ func DialWithoutCache(src v2net.Address, dest v2net.Destination) (net.Conn, erro
|
||||
|
||||
return dialer.Dial(dest.Network().String(), dest.NetAddr())
|
||||
}
|
||||
|
||||
func Dial3(src v2net.Address, dest v2net.Destination, proxyMeta *proxy.OutboundHandlerMeta) (*Connection, error) {
|
||||
if proxyMeta.KcpSupported && transport.IsKcpEnabled() {
|
||||
return DialKCP3(src, dest, proxyMeta)
|
||||
}
|
||||
return Dial(src, dest)
|
||||
}
|
||||
func DialWithoutCache3(src v2net.Address, dest v2net.Destination, proxyMeta *proxy.OutboundHandlerMeta) (net.Conn, error) {
|
||||
if proxyMeta.KcpSupported && transport.IsKcpEnabled() {
|
||||
return DialKCPWithoutCache(src, dest)
|
||||
}
|
||||
return DialWithoutCache(src, dest)
|
||||
}
|
||||
|
||||
func DialKCP3(src v2net.Address, dest v2net.Destination, proxyMeta *proxy.OutboundHandlerMeta) (*Connection, error) {
|
||||
if src == nil {
|
||||
src = v2net.AnyIP
|
||||
}
|
||||
id := src.String() + "-" + dest.NetAddr()
|
||||
conn, err := DialWithoutCache3(src, dest, proxyMeta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Connection{
|
||||
dest: id,
|
||||
conn: conn,
|
||||
listener: globalCache,
|
||||
}, nil
|
||||
}
|
||||
|
||||
/*DialKCPWithoutCache Dial KCP connection
|
||||
This Dialer will ignore src this is a restriction
|
||||
due to github.com/xtaci/kcp-go.DialWithOptions
|
||||
*/
|
||||
func DialKCPWithoutCache(src v2net.Address, dest v2net.Destination) (net.Conn, error) {
|
||||
return DialKCP(dest)
|
||||
}
|
||||
|
193
transport/hub/kcp.go
Normal file
193
transport/hub/kcp.go
Normal file
@ -0,0 +1,193 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/transport"
|
||||
"github.com/v2ray/v2ray-core/transport/hub/kcpv"
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
type KCPVlistener struct {
|
||||
lst *kcp.Listener
|
||||
conf *kcpv.Config
|
||||
previousSocketid map[int]uint32
|
||||
previousSocketid_mapid int
|
||||
}
|
||||
|
||||
/*Accept Accept a KCP connection
|
||||
Since KCP is stateless, if package deliver after it was closed,
|
||||
It could be reconized as a new connection and call accept.
|
||||
If we can detect that the connection is of such a kind,
|
||||
we will discard that conn.
|
||||
*/
|
||||
func (kvl *KCPVlistener) Accept() (net.Conn, error) {
|
||||
conn, err := kvl.lst.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if kvl.previousSocketid == nil {
|
||||
kvl.previousSocketid = make(map[int]uint32)
|
||||
}
|
||||
|
||||
var badbit bool = false
|
||||
|
||||
for _, key := range kvl.previousSocketid {
|
||||
if key == conn.GetConv() {
|
||||
badbit = true
|
||||
}
|
||||
}
|
||||
if badbit {
|
||||
conn.Close()
|
||||
return nil, errors.New("KCP:ConnDup, Don't worry~")
|
||||
} else {
|
||||
kvl.previousSocketid_mapid++
|
||||
kvl.previousSocketid[kvl.previousSocketid_mapid] = conn.GetConv()
|
||||
/*
|
||||
Here we assume that count(connection) < 512
|
||||
This won't always true.
|
||||
More work might be necessary to deal with this in a better way.
|
||||
*/
|
||||
if kvl.previousSocketid_mapid >= 512 {
|
||||
delete(kvl.previousSocketid, kvl.previousSocketid_mapid-512)
|
||||
}
|
||||
}
|
||||
|
||||
kcv := &KCPVconn{hc: conn}
|
||||
kcv.conf = kvl.conf
|
||||
err = kcv.ApplyConf()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return kcv, nil
|
||||
}
|
||||
|
||||
func (kvl *KCPVlistener) Close() error {
|
||||
return kvl.lst.Close()
|
||||
}
|
||||
|
||||
func (kvl *KCPVlistener) Addr() net.Addr {
|
||||
return kvl.lst.Addr()
|
||||
}
|
||||
|
||||
type KCPVconn struct {
|
||||
hc *kcp.UDPSession
|
||||
conf *kcpv.Config
|
||||
conntokeep time.Time
|
||||
}
|
||||
|
||||
//var counter int
|
||||
|
||||
func (kcpvc *KCPVconn) Read(b []byte) (int, error) {
|
||||
ifb := time.Now().Add(time.Duration(kcpvc.conf.AdvancedConfigs.ReadTimeout) * time.Second)
|
||||
if ifb.After(kcpvc.conntokeep) {
|
||||
kcpvc.conntokeep = ifb
|
||||
}
|
||||
kcpvc.hc.SetDeadline(kcpvc.conntokeep)
|
||||
return kcpvc.hc.Read(b)
|
||||
}
|
||||
|
||||
func (kcpvc *KCPVconn) Write(b []byte) (int, error) {
|
||||
ifb := time.Now().Add(time.Duration(kcpvc.conf.AdvancedConfigs.WriteTimeout) * time.Second)
|
||||
if ifb.After(kcpvc.conntokeep) {
|
||||
kcpvc.conntokeep = ifb
|
||||
}
|
||||
kcpvc.hc.SetDeadline(kcpvc.conntokeep)
|
||||
return kcpvc.hc.Write(b)
|
||||
}
|
||||
|
||||
/*ApplyConf will apply kcpvc.conf to current Socket
|
||||
|
||||
It is recommmanded to call this func once and only once
|
||||
*/
|
||||
func (kcpvc *KCPVconn) ApplyConf() error {
|
||||
nodelay, interval, resend, nc := 0, 40, 0, 0
|
||||
if kcpvc.conf.Mode != "manual" {
|
||||
switch kcpvc.conf.Mode {
|
||||
case "normal":
|
||||
nodelay, interval, resend, nc = 0, 30, 2, 1
|
||||
case "fast":
|
||||
nodelay, interval, resend, nc = 0, 20, 2, 1
|
||||
case "fast2":
|
||||
nodelay, interval, resend, nc = 1, 20, 2, 1
|
||||
case "fast3":
|
||||
nodelay, interval, resend, nc = 1, 10, 2, 1
|
||||
}
|
||||
} else {
|
||||
log.Error("kcp: Failed to Apply configure: Manual mode is not supported.(yet!)")
|
||||
return errors.New("kcp: Manual Not Implemented")
|
||||
}
|
||||
|
||||
kcpvc.hc.SetNoDelay(nodelay, interval, resend, nc)
|
||||
kcpvc.hc.SetWindowSize(kcpvc.conf.AdvancedConfigs.Sndwnd, kcpvc.conf.AdvancedConfigs.Rcvwnd)
|
||||
kcpvc.hc.SetMtu(kcpvc.conf.AdvancedConfigs.Mtu)
|
||||
kcpvc.hc.SetACKNoDelay(kcpvc.conf.AdvancedConfigs.Acknodelay)
|
||||
kcpvc.hc.SetDSCP(kcpvc.conf.AdvancedConfigs.Dscp)
|
||||
//counter++
|
||||
//log.Info(counter)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*Close Close the current conn
|
||||
We have to delay the close of Socket for a few second
|
||||
or the VMess EOF can be too late to send.
|
||||
*/
|
||||
func (kcpvc *KCPVconn) Close() error {
|
||||
go func() {
|
||||
time.Sleep(2000 * time.Millisecond)
|
||||
//counter--
|
||||
//log.Info(counter)
|
||||
kcpvc.hc.Close()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kcpvc *KCPVconn) LocalAddr() net.Addr {
|
||||
return kcpvc.hc.LocalAddr()
|
||||
}
|
||||
|
||||
func (kcpvc *KCPVconn) RemoteAddr() net.Addr {
|
||||
return kcpvc.hc.RemoteAddr()
|
||||
}
|
||||
|
||||
func (kcpvc *KCPVconn) SetDeadline(t time.Time) error {
|
||||
return kcpvc.hc.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (kcpvc *KCPVconn) SetReadDeadline(t time.Time) error {
|
||||
return kcpvc.hc.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (kcpvc *KCPVconn) SetWriteDeadline(t time.Time) error {
|
||||
return kcpvc.hc.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func DialKCP(dest v2net.Destination) (*KCPVconn, error) {
|
||||
kcpconf := transport.KcpConfig
|
||||
cpip, _ := kcpv.GetChipher(kcpconf.Key)
|
||||
kcv, err := kcp.DialWithOptions(kcpconf.AdvancedConfigs.Fec, dest.NetAddr(), cpip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kcvn := &KCPVconn{hc: kcv}
|
||||
kcvn.conf = kcpconf
|
||||
err = kcvn.ApplyConf()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return kcvn, nil
|
||||
}
|
||||
|
||||
func ListenKCP(address v2net.Address, port v2net.Port) (*KCPVlistener, error) {
|
||||
kcpconf := transport.KcpConfig
|
||||
cpip, _ := kcpv.GetChipher(kcpconf.Key)
|
||||
laddr := address.String() + ":" + port.String()
|
||||
kcl, err := kcp.ListenWithOptions(kcpconf.AdvancedConfigs.Fec, laddr, cpip)
|
||||
kcvl := &KCPVlistener{lst: kcl, conf: kcpconf}
|
||||
return kcvl, err
|
||||
}
|
31
transport/hub/kcp_test.go
Normal file
31
transport/hub/kcp_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
package hub_test
|
||||
|
||||
import "testing"
|
||||
|
||||
import (
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
"github.com/v2ray/v2ray-core/transport"
|
||||
"github.com/v2ray/v2ray-core/transport/hub"
|
||||
"github.com/v2ray/v2ray-core/transport/hub/kcpv"
|
||||
)
|
||||
|
||||
func Test_Pair(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
transport.KcpConfig = &kcpv.Config{}
|
||||
transport.KcpConfig.Mode = "fast2"
|
||||
transport.KcpConfig.Key = "key"
|
||||
transport.KcpConfig.AdvancedConfigs = kcpv.DefaultAdvancedConfigs
|
||||
lst, _ := hub.ListenKCP(v2net.ParseAddress("127.0.0.1"), 17777)
|
||||
go func() {
|
||||
connx, err2 := lst.Accept()
|
||||
assert.Error(err2).IsNil()
|
||||
connx.Close()
|
||||
}()
|
||||
conn, _ := hub.DialKCP(v2net.TCPDestination(v2net.ParseAddress("127.0.0.1"), 17777))
|
||||
conn.LocalAddr()
|
||||
conn.RemoteAddr()
|
||||
conn.ApplyConf()
|
||||
conn.Write([]byte("x"))
|
||||
conn.Close()
|
||||
}
|
65
transport/hub/kcpv/config.go
Normal file
65
transport/hub/kcpv/config.go
Normal file
@ -0,0 +1,65 @@
|
||||
package kcpv
|
||||
|
||||
/*AdvancedConfig define behavior of KCP in detail
|
||||
|
||||
MaximumTransmissionUnit:
|
||||
Largest protocol data unit that the layer can pass onwards
|
||||
can be discovered by running tracepath
|
||||
|
||||
SendingWindowSize , ReceivingWindowSize:
|
||||
the size of Tx/Rx window, by packet
|
||||
|
||||
ForwardErrorCorrectionGroupSize:
|
||||
The the size of packet to generate a Forward Error Correction
|
||||
packet, this is used to counteract packet loss.
|
||||
|
||||
AcknowledgeNoDelay:
|
||||
Do not wait a certain of time before sending the ACK packet,
|
||||
increase bandwich cost and might increase performance
|
||||
|
||||
Dscp:
|
||||
Differentiated services code point,
|
||||
be used to reconized to discriminate packet.
|
||||
It is recommanded to keep it 0 to avoid being detected.
|
||||
|
||||
ReadTimeout,WriteTimeout:
|
||||
Close the Socket if it have been silent for too long,
|
||||
Small value can recycle zombie socket faster but
|
||||
can cause v2ray to kill the proxy connection it is relaying,
|
||||
Higher value can prevent server from closing zombie socket and
|
||||
waste resources.
|
||||
*/
|
||||
type AdvancedConfig struct {
|
||||
Mtu int `json:"MaximumTransmissionUnit"`
|
||||
Sndwnd int `json:"SendingWindowSize"`
|
||||
Rcvwnd int `json:"ReceivingWindowSize"`
|
||||
Fec int `json:"ForwardErrorCorrectionGroupSize"`
|
||||
Acknodelay bool `json:"AcknowledgeNoDelay"`
|
||||
Dscp int `json:"Dscp"`
|
||||
ReadTimeout int `json:"ReadTimeout"`
|
||||
WriteTimeout int `json:"WriteTimeout"`
|
||||
}
|
||||
|
||||
/*Config define basic behavior of KCP
|
||||
Mode:
|
||||
can be one of these values:
|
||||
fast3,fast2,fast,normal
|
||||
<<<<<<- less delay
|
||||
->>>>>> less bandwich wasted
|
||||
|
||||
EncryptionKey:
|
||||
a string that will be the EncryptionKey of
|
||||
All KCP connection we Listen-Accpet or
|
||||
Dial, We are not very sure about how this
|
||||
encryption hehave and DO use a unique randomly
|
||||
generated key.
|
||||
*/
|
||||
type Config struct {
|
||||
Mode string `json:"Mode"`
|
||||
Key string `json:"EncryptionKey"`
|
||||
AdvancedConfigs *AdvancedConfig `json:"AdvancedConfig,omitempty"`
|
||||
}
|
||||
|
||||
var DefaultAdvancedConfigs = &AdvancedConfig{
|
||||
Mtu: 1350, Sndwnd: 1024, Rcvwnd: 1024, Fec: 4, Dscp: 0, ReadTimeout: 600, WriteTimeout: 500, Acknodelay: false,
|
||||
}
|
3
transport/hub/kcpv/config_json.go
Normal file
3
transport/hub/kcpv/config_json.go
Normal file
@ -0,0 +1,3 @@
|
||||
package kcpv
|
||||
|
||||
//We can use the default version of json parser
|
21
transport/hub/kcpv/crypto.go
Normal file
21
transport/hub/kcpv/crypto.go
Normal file
@ -0,0 +1,21 @@
|
||||
package kcpv
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
)
|
||||
|
||||
func generateKeyFromConfigString(key string) []byte {
|
||||
key += "consensus salt: Let's fight arcifical deceleration with our code. We shall prove our believes with action."
|
||||
keyw := sha256.Sum256([]byte(key))
|
||||
return keyw[:]
|
||||
}
|
||||
|
||||
func generateBlockWithKey(key []byte) (cipher.Block, error) {
|
||||
return aes.NewCipher(key)
|
||||
}
|
||||
|
||||
func GetChipher(key string) (cipher.Block, error) {
|
||||
return generateBlockWithKey(generateKeyFromConfigString(key))
|
||||
}
|
@ -8,6 +8,8 @@ import (
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/transport"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -47,6 +49,36 @@ func ListenTCP(address v2net.Address, port v2net.Port, callback ConnectionHandle
|
||||
go hub.start()
|
||||
return hub, nil
|
||||
}
|
||||
func ListenKCPhub(address v2net.Address, port v2net.Port, callback ConnectionHandler, tlsConfig *tls.Config) (*TCPHub, error) {
|
||||
listener, err := ListenKCP(address, port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var hub *TCPHub
|
||||
if tlsConfig != nil {
|
||||
tlsListener := tls.NewListener(listener, tlsConfig)
|
||||
hub = &TCPHub{
|
||||
listener: tlsListener,
|
||||
connCallback: callback,
|
||||
}
|
||||
} else {
|
||||
hub = &TCPHub{
|
||||
listener: listener,
|
||||
connCallback: callback,
|
||||
}
|
||||
}
|
||||
|
||||
go hub.start()
|
||||
return hub, nil
|
||||
}
|
||||
func ListenTCP6(address v2net.Address, port v2net.Port, callback ConnectionHandler, proxyMeta *proxy.InboundHandlerMeta, tlsConfig *tls.Config) (*TCPHub, error) {
|
||||
if proxyMeta.KcpSupported && transport.IsKcpEnabled() {
|
||||
return ListenKCPhub(address, port, callback, tlsConfig)
|
||||
} else {
|
||||
return ListenTCP(address, port, callback, tlsConfig)
|
||||
}
|
||||
return nil, errors.New("ListenTCP6: Not Implemented")
|
||||
}
|
||||
|
||||
func (this *TCPHub) Close() {
|
||||
this.accepting = false
|
||||
|
@ -1,10 +1,18 @@
|
||||
package transport
|
||||
|
||||
import "github.com/v2ray/v2ray-core/transport/hub/kcpv"
|
||||
|
||||
var (
|
||||
connectionReuse = true
|
||||
enableKcp = false
|
||||
KcpConfig *kcpv.Config
|
||||
)
|
||||
|
||||
// IsConnectionReusable returns true if V2Ray is trying to reuse TCP connections.
|
||||
func IsConnectionReusable() bool {
|
||||
return connectionReuse
|
||||
}
|
||||
|
||||
func IsKcpEnabled() bool {
|
||||
return enableKcp
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user