From 37341af2d14608f5094a4fb3859b468fbd9b3ead Mon Sep 17 00:00:00 2001 From: sagitchu Date: Tue, 21 Apr 2026 10:20:21 +0800 Subject: [PATCH] fix(kcp): prevent zero-value override of window/buffer params causing 10x perf drop The metadata parser unconditionally assigned GetInt results (which return 0 for missing keys) to all KCP config fields, overwriting defaults like SndWnd=1024, RcvWnd=1024, StreamBuf=2097152 with 0. KCP's WndSize only applies positive values, so snd_wnd=0 caused the write path condition waitsnd < s.kcp.snd_wnd to never succeed, effectively stalling throughput at ~10Mbps instead of 400Mbps+. - Only apply metadata values when the key actually exists (IsExists check) - Use Clone() instead of sharing the global DefaultConfig pointer - Add Config.Clone() deep-copy method --- go-gost/x/dialer/kcp/metadata.go | 59 ++++++++++++++++++++------- go-gost/x/internal/util/kcp/config.go | 9 ++++ go-gost/x/listener/kcp/metadata.go | 59 ++++++++++++++++++++------- 3 files changed, 99 insertions(+), 28 deletions(-) diff --git a/go-gost/x/dialer/kcp/metadata.go b/go-gost/x/dialer/kcp/metadata.go index a5cf570..c544eda 100644 --- a/go-gost/x/dialer/kcp/metadata.go +++ b/go-gost/x/dialer/kcp/metadata.go @@ -40,21 +40,52 @@ func (d *kcpDialer) parseMetadata(md mdata.Metadata) (err error) { d.md.config = cfg } if d.md.config == nil { - d.md.config = kcp_util.DefaultConfig + d.md.config = kcp_util.DefaultConfig.Clone() + } else { + d.md.config = d.md.config.Clone() + } + + if md != nil { + if md.IsExists("kcp.tcp") { + d.md.config.TCP = mdutil.GetBool(md, "kcp.tcp") + } + if md.IsExists("kcp.key") { + d.md.config.Key = mdutil.GetString(md, "kcp.key") + } + if md.IsExists("kcp.crypt") { + d.md.config.Crypt = mdutil.GetString(md, "kcp.crypt") + } + if md.IsExists("kcp.mode") { + d.md.config.Mode = mdutil.GetString(md, "kcp.mode") + } + if md.IsExists("kcp.keepalive") { + d.md.config.KeepAlive = mdutil.GetInt(md, "kcp.keepalive") + } + if md.IsExists("kcp.interval") { + d.md.config.Interval = mdutil.GetInt(md, "kcp.interval") + } + if md.IsExists("kcp.mtu") { + d.md.config.MTU = mdutil.GetInt(md, "kcp.mtu") + } + if md.IsExists("kcp.rcvwnd") { + d.md.config.RcvWnd = mdutil.GetInt(md, "kcp.rcvwnd") + } + if md.IsExists("kcp.sndwnd") { + d.md.config.SndWnd = mdutil.GetInt(md, "kcp.sndwnd") + } + if md.IsExists("kcp.smuxver") { + d.md.config.SmuxVer = mdutil.GetInt(md, "kcp.smuxver") + } + if md.IsExists("kcp.smuxbuf") { + d.md.config.SmuxBuf = mdutil.GetInt(md, "kcp.smuxbuf") + } + if md.IsExists("kcp.streambuf") { + d.md.config.StreamBuf = mdutil.GetInt(md, "kcp.streambuf") + } + if md.IsExists("kcp.nocomp") { + d.md.config.NoComp = mdutil.GetBool(md, "kcp.nocomp") + } } - d.md.config.TCP = mdutil.GetBool(md, "kcp.tcp") - d.md.config.Key = mdutil.GetString(md, "kcp.key") - d.md.config.Crypt = mdutil.GetString(md, "kcp.crypt") - d.md.config.Mode = mdutil.GetString(md, "kcp.mode") - d.md.config.KeepAlive = mdutil.GetInt(md, "kcp.keepalive") - d.md.config.Interval = mdutil.GetInt(md, "kcp.interval") - d.md.config.MTU = mdutil.GetInt(md, "kcp.mtu") - d.md.config.RcvWnd = mdutil.GetInt(md, "kcp.rcvwnd") - d.md.config.SndWnd = mdutil.GetInt(md, "kcp.sndwnd") - d.md.config.SmuxVer = mdutil.GetInt(md, "kcp.smuxver") - d.md.config.SmuxBuf = mdutil.GetInt(md, "kcp.smuxbuf") - d.md.config.StreamBuf = mdutil.GetInt(md, "kcp.streambuf") - d.md.config.NoComp = mdutil.GetBool(md, "kcp.nocomp") d.md.handshakeTimeout = mdutil.GetDuration(md, handshakeTimeout) return diff --git a/go-gost/x/internal/util/kcp/config.go b/go-gost/x/internal/util/kcp/config.go index 985d2eb..43cd9db 100644 --- a/go-gost/x/internal/util/kcp/config.go +++ b/go-gost/x/internal/util/kcp/config.go @@ -89,6 +89,15 @@ func ParseFromFile(filename string) (*Config, error) { return config, nil } +// Clone returns a deep copy of the Config. +func (c *Config) Clone() *Config { + if c == nil { + return nil + } + clone := *c + return &clone +} + // Init initializes the KCP config. func (c *Config) Init() { switch c.Mode { diff --git a/go-gost/x/listener/kcp/metadata.go b/go-gost/x/listener/kcp/metadata.go index c66a44f..5b7ce9b 100644 --- a/go-gost/x/listener/kcp/metadata.go +++ b/go-gost/x/listener/kcp/metadata.go @@ -43,21 +43,52 @@ func (l *kcpListener) parseMetadata(md mdata.Metadata) (err error) { } if l.md.config == nil { - l.md.config = kcp_util.DefaultConfig + l.md.config = kcp_util.DefaultConfig.Clone() + } else { + l.md.config = l.md.config.Clone() + } + + if md != nil { + if md.IsExists("kcp.tcp") { + l.md.config.TCP = mdutil.GetBool(md, "kcp.tcp") + } + if md.IsExists("kcp.key") { + l.md.config.Key = mdutil.GetString(md, "kcp.key") + } + if md.IsExists("kcp.crypt") { + l.md.config.Crypt = mdutil.GetString(md, "kcp.crypt") + } + if md.IsExists("kcp.mode") { + l.md.config.Mode = mdutil.GetString(md, "kcp.mode") + } + if md.IsExists("kcp.keepalive") { + l.md.config.KeepAlive = mdutil.GetInt(md, "kcp.keepalive") + } + if md.IsExists("kcp.interval") { + l.md.config.Interval = mdutil.GetInt(md, "kcp.interval") + } + if md.IsExists("kcp.mtu") { + l.md.config.MTU = mdutil.GetInt(md, "kcp.mtu") + } + if md.IsExists("kcp.rcvwnd") { + l.md.config.RcvWnd = mdutil.GetInt(md, "kcp.rcvwnd") + } + if md.IsExists("kcp.sndwnd") { + l.md.config.SndWnd = mdutil.GetInt(md, "kcp.sndwnd") + } + if md.IsExists("kcp.smuxver") { + l.md.config.SmuxVer = mdutil.GetInt(md, "kcp.smuxver") + } + if md.IsExists("kcp.smuxbuf") { + l.md.config.SmuxBuf = mdutil.GetInt(md, "kcp.smuxbuf") + } + if md.IsExists("kcp.streambuf") { + l.md.config.StreamBuf = mdutil.GetInt(md, "kcp.streambuf") + } + if md.IsExists("kcp.nocomp") { + l.md.config.NoComp = mdutil.GetBool(md, "kcp.nocomp") + } } - l.md.config.TCP = mdutil.GetBool(md, "kcp.tcp") - l.md.config.Key = mdutil.GetString(md, "kcp.key") - l.md.config.Crypt = mdutil.GetString(md, "kcp.crypt") - l.md.config.Mode = mdutil.GetString(md, "kcp.mode") - l.md.config.KeepAlive = mdutil.GetInt(md, "kcp.keepalive") - l.md.config.Interval = mdutil.GetInt(md, "kcp.interval") - l.md.config.MTU = mdutil.GetInt(md, "kcp.mtu") - l.md.config.RcvWnd = mdutil.GetInt(md, "kcp.rcvwnd") - l.md.config.SndWnd = mdutil.GetInt(md, "kcp.sndwnd") - l.md.config.SmuxVer = mdutil.GetInt(md, "kcp.smuxver") - l.md.config.SmuxBuf = mdutil.GetInt(md, "kcp.smuxbuf") - l.md.config.StreamBuf = mdutil.GetInt(md, "kcp.streambuf") - l.md.config.NoComp = mdutil.GetBool(md, "kcp.nocomp") l.md.backlog = mdutil.GetInt(md, backlog) if l.md.backlog <= 0 {