Improve UDP batch support

This commit is contained in:
世界
2026-04-24 08:54:40 +08:00
parent 686b9b08df
commit 4cdacb3e63
28 changed files with 304 additions and 205 deletions
+2 -1
View File
@@ -3,6 +3,7 @@ package interrupt
import (
"net"
"github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/x/list"
)
@@ -71,5 +72,5 @@ func (c *PacketConn) WriterReplaceable() bool {
}
func (c *PacketConn) Upstream() any {
return c.PacketConn
return bufio.NewPacketConn(c.PacketConn)
}
+6 -6
View File
@@ -25,9 +25,9 @@ type Listener struct {
logger logger.ContextLogger
network []string
listenOptions option.ListenOptions
connHandler adapter.ConnectionHandlerEx
packetHandler adapter.PacketHandlerEx
oobPacketHandler adapter.OOBPacketHandlerEx
connHandler adapter.ConnectionHandler
packetHandler adapter.PacketHandler
oobPacketHandler adapter.OOBPacketHandler
threadUnsafePacketWriter bool
disablePacketOutput bool
setSystemProxy bool
@@ -48,9 +48,9 @@ type Options struct {
Logger logger.ContextLogger
Network []string
Listen option.ListenOptions
ConnectionHandler adapter.ConnectionHandlerEx
PacketHandler adapter.PacketHandlerEx
OOBPacketHandler adapter.OOBPacketHandlerEx
ConnectionHandler adapter.ConnectionHandler
PacketHandler adapter.PacketHandler
OOBPacketHandler adapter.OOBPacketHandler
ThreadUnsafePacketWriter bool
DisablePacketOutput bool
SetSystemProxy bool
+1 -1
View File
@@ -106,6 +106,6 @@ func (l *Listener) loopTCPIn() {
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
ctx := log.ContextWithNewID(l.ctx)
l.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
go l.connHandler.NewConnectionEx(ctx, conn, metadata, nil)
go l.connHandler.NewConnection(ctx, conn, metadata, nil)
}
}
+99 -17
View File
@@ -11,6 +11,7 @@ import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/redir"
"github.com/sagernet/sing/common/buf"
sBufio "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
@@ -18,6 +19,8 @@ import (
"github.com/sagernet/sing/service"
)
const udpOutputBatchSize = 128
func (l *Listener) ListenUDP() (net.PacketConn, error) {
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(netip.AddrFrom4([4]byte{127, 0, 0, 1})), l.listenOptions.ListenPort)
var listenConfig net.ListenConfig
@@ -98,6 +101,15 @@ func (l *Listener) PacketWriter() N.PacketWriter {
func (l *Listener) loopUDPIn() {
defer close(l.packetOutboundClosed)
if l.oobPacketHandler == nil {
if batchHandler, isBatchHandler := l.packetHandler.(adapter.PacketBatchHandler); isBatchHandler {
packetConn := sBufio.NewPacketConn(l.udpConn)
if readWaiter, created := sBufio.CreatePacketBatchReadWaiter(packetConn); created {
l.loopUDPInBatch(batchHandler, readWaiter)
return
}
}
}
var buffer *buf.Buffer
if !l.threadUnsafePacketWriter {
buffer = buf.NewPacket()
@@ -126,7 +138,7 @@ func (l *Listener) loopUDPIn() {
return
}
buffer.Truncate(n)
l.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap())
l.oobPacketHandler.NewPacket(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap())
}
} else {
for {
@@ -148,37 +160,82 @@ func (l *Listener) loopUDPIn() {
return
}
buffer.Truncate(n)
l.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap())
l.packetHandler.NewPacket(buffer, M.SocksaddrFromNetIP(addr).Unwrap())
}
}
}
func (l *Listener) loopUDPInBatch(handler adapter.PacketBatchHandler, readWaiter N.PacketBatchReadWaiter) {
readWaitOptions := N.ReadWaitOptions{
BatchSize: sBufio.DefaultPacketReadBatchSize,
}
readWaiter.InitializeReadWaiter(readWaitOptions)
for {
buffers, sources, err := readWaiter.WaitReadPackets()
if err != nil {
buf.ReleaseMulti(buffers)
if l.shutdown.Load() && E.IsClosed(err) {
return
}
l.udpConn.Close()
l.logger.Error("udp listener closed: ", err)
return
}
handler.NewPacketBatch(buffers, sources)
}
}
func (l *Listener) loopUDPOut() {
packetConn := sBufio.NewPacketConn(l.udpConn)
batchWriter := sBufio.NewPacketBatchWriter(packetConn)
packets := make([]*N.PacketBuffer, 0, udpOutputBatchSize)
buffers := make([]*buf.Buffer, 0, udpOutputBatchSize)
destinations := make([]M.Socksaddr, 0, udpOutputBatchSize)
for {
select {
case packet := <-l.packetOutbound:
destination := packet.Destination.AddrPort()
_, err := l.udpConn.WriteToUDPAddrPort(packet.Buffer.Bytes(), destination)
packet.Buffer.Release()
N.PutPacketBuffer(packet)
if err != nil {
if l.shutdown.Load() && E.IsClosed(err) {
return
}
l.logger.Error("udp listener write back: ", destination, ": ", err)
continue
}
continue
packets = append(packets, packet)
case <-l.packetOutboundClosed:
l.releasePacketOutbound()
return
}
for {
drain:
for len(packets) < udpOutputBatchSize {
select {
case packet := <-l.packetOutbound:
packet.Buffer.Release()
N.PutPacketBuffer(packet)
packets = append(packets, packet)
default:
break drain
}
}
for _, packet := range packets {
buffers = append(buffers, packet.Buffer)
destinations = append(destinations, packet.Destination)
}
err := batchWriter.WritePacketBatch(buffers, destinations)
for _, packet := range packets {
N.PutPacketBuffer(packet)
}
packets = packets[:0]
buffers = buffers[:0]
destinations = destinations[:0]
if err != nil {
if l.shutdown.Load() && E.IsClosed(err) {
return
}
l.logger.Error("udp listener write back: ", err)
}
}
}
func (l *Listener) releasePacketOutbound() {
for {
select {
case packet := <-l.packetOutbound:
packet.Buffer.Release()
N.PutPacketBuffer(packet)
default:
return
}
}
}
@@ -203,5 +260,30 @@ func (w *packetWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr)
}
}
func (w *packetWriter) WritePacketBatch(buffers []*buf.Buffer, destinations []M.Socksaddr) error {
if len(buffers) == 0 || len(buffers) != len(destinations) {
buf.ReleaseMulti(buffers)
return os.ErrInvalid
}
for index, buffer := range buffers {
packet := N.NewPacketBuffer()
packet.Buffer = buffer
packet.Destination = destinations[index]
select {
case w.packetOutbound <- packet:
default:
buffer.Release()
N.PutPacketBuffer(packet)
buf.ReleaseMulti(buffers[index+1:])
if w.shutdown.Load() {
return os.ErrClosed
}
w.logger.Trace("dropped packet batch to ", destinations[index])
return nil
}
}
return nil
}
func (w *packetWriter) WriteIsThreadUnsafe() {
}
+1 -1
View File
@@ -42,7 +42,7 @@ func NewRouterWithOptions(router adapter.ConnectionRouterEx, logger logger.Conte
return log.ContextWithNewID(ctx)
},
Logger: logger,
HandlerEx: adapter.NewRouteContextHandlerEx(router),
HandlerEx: adapter.NewRouteContextHandler(router),
Padding: options.Padding,
Brutal: brutalOptions,
})