Dokodemo: Recycle inactive fakeudp connections

This commit is contained in:
风扇滑翔翼
2025-10-29 09:28:57 +00:00
committed by GitHub
parent b69a376aa1
commit 26b246fa92

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
@@ -12,6 +13,7 @@ import (
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
@@ -176,7 +178,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
if err != nil { if err != nil {
return err return err
} }
writer = NewPacketWriter(pConn, &dest, mark, back) writer = NewPacketWriter(ctx, pConn, &dest, mark, back)
defer writer.(*PacketWriter).Close() // close fake UDP conns defer writer.(*PacketWriter).Close() // close fake UDP conns
} }
} }
@@ -190,20 +192,33 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
return nil // Unlike Dispatch(), DispatchLink() will not return until the outbound finishes Process() return nil // Unlike Dispatch(), DispatchLink() will not return until the outbound finishes Process()
} }
func NewPacketWriter(conn net.PacketConn, d *net.Destination, mark int, back *net.UDPAddr) buf.Writer { func NewPacketWriter(ctx context.Context, conn net.PacketConn, d *net.Destination, mark int, back *net.UDPAddr) buf.Writer {
ctx, cancel := context.WithCancel(ctx)
writer := &PacketWriter{ writer := &PacketWriter{
ctx: ctx,
cancel: cancel,
conn: conn, conn: conn,
conns: make(map[net.Destination]net.PacketConn), conns: utils.NewTypedSyncMap[net.Destination, *PacketConnTimeWrapper](),
inactiveConns: make([]*PacketConnTimeWrapper, 0),
mark: mark, mark: mark,
back: back, back: back,
} }
writer.conns[*d] = conn timedconn := &PacketConnTimeWrapper{
PacketConn: conn,
lastUsedTime: time.Now(),
isMainConn: true,
}
writer.conns.Store(*d, timedconn)
go writer.cleanInactiveConns()
return writer return writer
} }
type PacketWriter struct { type PacketWriter struct {
ctx context.Context
cancel context.CancelFunc
conn net.PacketConn conn net.PacketConn
conns map[net.Destination]net.PacketConn conns *utils.TypedSyncMap[net.Destination, *PacketConnTimeWrapper]
inactiveConns []*PacketConnTimeWrapper
mark int mark int
back *net.UDPAddr back *net.UDPAddr
} }
@@ -217,26 +232,30 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
} }
var err error var err error
if b.UDP != nil && b.UDP.Address.Family().IsIP() { if b.UDP != nil && b.UDP.Address.Family().IsIP() {
conn := w.conns[*b.UDP] conn, _ := w.conns.Load(*b.UDP)
if conn == nil { if conn == nil {
conn, err = FakeUDP( fakeudpconn, err := FakeUDP(
&net.UDPAddr{ &net.UDPAddr{
IP: b.UDP.Address.IP(), IP: b.UDP.Address.IP(),
Port: int(b.UDP.Port), Port: int(b.UDP.Port),
}, },
w.mark, w.mark,
) )
conn = &PacketConnTimeWrapper{
PacketConn: fakeudpconn,
lastUsedTime: time.Now(),
}
if err != nil { if err != nil {
errors.LogInfo(context.Background(), err.Error()) errors.LogInfo(context.Background(), err.Error())
b.Release() b.Release()
continue continue
} }
w.conns[*b.UDP] = conn w.conns.Store(*b.UDP, conn)
} }
_, err = conn.WriteTo(b.Bytes(), w.back) _, err = conn.WriteTo(b.Bytes(), w.back)
if err != nil { if err != nil {
errors.LogInfo(context.Background(), err.Error()) errors.LogInfo(context.Background(), err.Error())
w.conns[*b.UDP] = nil w.conns.Delete(*b.UDP)
conn.Close() conn.Close()
} }
b.Release() b.Release()
@@ -253,10 +272,38 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
} }
func (w *PacketWriter) Close() error { func (w *PacketWriter) Close() error {
for _, conn := range w.conns { w.cancel()
if conn != nil { w.conns.Range(func(key net.Destination, conn *PacketConnTimeWrapper) bool {
conn.Close() common.CloseIfExists(conn)
} return true
} })
return nil return nil
} }
func (w *PacketWriter) cleanInactiveConns() {
ticker := time.NewTicker(60 * time.Second)
defer ticker.Stop()
select {
case <-ticker.C:
if len(w.inactiveConns) > 0 {
for _, conn := range w.inactiveConns {
common.CloseIfExists(conn)
}
}
w.conns.Range(func(key net.Destination, conn *PacketConnTimeWrapper) bool {
if conn != nil && !conn.isMainConn && time.Since(conn.lastUsedTime) > 120*time.Second {
w.conns.Delete(key)
}
w.inactiveConns = append(w.inactiveConns, conn)
return true
})
case <-w.ctx.Done():
return
}
}
type PacketConnTimeWrapper struct {
net.PacketConn
lastUsedTime time.Time
isMainConn bool
}