mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
Merge 81e7bd5adb304b69f0f4e74effbb9cd77038e054 into 8ce44b765f5438e01b3bdd46e8acff2a66877df4
This commit is contained in:
commit
1841a4bc98
@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
@ -14,7 +14,8 @@
|
||||
"interval": "",
|
||||
"tolerance": 0,
|
||||
"idle_timeout": "",
|
||||
"interrupt_exist_connections": false
|
||||
"interrupt_exist_connections": false,
|
||||
"randomize": false
|
||||
}
|
||||
```
|
||||
|
||||
@ -47,3 +48,9 @@ The idle timeout. `30m` will be used if empty.
|
||||
Interrupt existing connections when the selected outbound has changed.
|
||||
|
||||
Only inbound connections are affected by this setting, internal connections will always be interrupted.
|
||||
|
||||
#### randomize
|
||||
|
||||
Outbound would be selected randomly within the best latency in the tolerance range. It's deactivated by default.
|
||||
|
||||
The interrupt_exist_connections will be ignored if the randomize is activated.
|
@ -14,7 +14,8 @@
|
||||
"interval": "",
|
||||
"tolerance": 50,
|
||||
"idle_timeout": "",
|
||||
"interrupt_exist_connections": false
|
||||
"interrupt_exist_connections": false,
|
||||
"randomize": false
|
||||
}
|
||||
```
|
||||
|
||||
@ -47,3 +48,10 @@
|
||||
当选定的出站发生更改时,中断现有连接。
|
||||
|
||||
仅入站连接受此设置影响,内部连接将始终被中断。
|
||||
|
||||
|
||||
#### randomize
|
||||
|
||||
出站将在容忍范围内的最佳延迟内随机选择。 默认情况下它处于禁用状态。
|
||||
|
||||
如果激活了随机化,则interrupt_exist_connections将被忽略。
|
@ -13,4 +13,5 @@ type URLTestOutboundOptions struct {
|
||||
Tolerance uint16 `json:"tolerance,omitempty"`
|
||||
IdleTimeout Duration `json:"idle_timeout,omitempty"`
|
||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||
Randomize bool `json:"randomize,omitempty"`
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
@ -38,6 +39,7 @@ type URLTest struct {
|
||||
idleTimeout time.Duration
|
||||
group *URLTestGroup
|
||||
interruptExternalConnections bool
|
||||
randomize bool
|
||||
}
|
||||
|
||||
func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) {
|
||||
@ -57,6 +59,7 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
tolerance: options.Tolerance,
|
||||
idleTimeout: time.Duration(options.IdleTimeout),
|
||||
interruptExternalConnections: options.InterruptExistConnections,
|
||||
randomize: options.Randomize,
|
||||
}
|
||||
if len(outbound.tags) == 0 {
|
||||
return nil, E.New("missing tags")
|
||||
@ -83,6 +86,7 @@ func (s *URLTest) Start() error {
|
||||
s.tolerance,
|
||||
s.idleTimeout,
|
||||
s.interruptExternalConnections,
|
||||
s.randomize,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -126,6 +130,9 @@ func (s *URLTest) CheckOutbounds() {
|
||||
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
s.group.Touch()
|
||||
var outbound adapter.Outbound
|
||||
if s.randomize {
|
||||
outbound = s.group.selectRandomOutbound(network)
|
||||
} else {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
outbound = s.group.selectedOutboundTCP
|
||||
@ -137,6 +144,7 @@ func (s *URLTest) DialContext(ctx context.Context, network string, destination M
|
||||
if outbound == nil {
|
||||
outbound, _ = s.group.Select(network)
|
||||
}
|
||||
}
|
||||
if outbound == nil {
|
||||
return nil, E.New("missing supported outbound")
|
||||
}
|
||||
@ -151,10 +159,15 @@ func (s *URLTest) DialContext(ctx context.Context, network string, destination M
|
||||
|
||||
func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
s.group.Touch()
|
||||
outbound := s.group.selectedOutboundUDP
|
||||
var outbound adapter.Outbound
|
||||
if s.randomize {
|
||||
outbound = s.group.selectRandomOutbound(N.NetworkUDP) // Since ListenPacket is for UDP, we pass "N.NetworkUDP" as the network type
|
||||
} else {
|
||||
outbound = s.group.selectedOutboundUDP
|
||||
if outbound == nil {
|
||||
outbound, _ = s.group.Select(N.NetworkUDP)
|
||||
}
|
||||
}
|
||||
if outbound == nil {
|
||||
return nil, E.New("missing supported outbound")
|
||||
}
|
||||
@ -196,6 +209,9 @@ type URLTestGroup struct {
|
||||
pauseManager pause.Manager
|
||||
selectedOutboundTCP adapter.Outbound
|
||||
selectedOutboundUDP adapter.Outbound
|
||||
randomize bool
|
||||
bestTCPLatencyOutbounds []adapter.Outbound
|
||||
bestUDPLatencyOutbounds []adapter.Outbound
|
||||
interruptGroup *interrupt.Group
|
||||
interruptExternalConnections bool
|
||||
|
||||
@ -216,6 +232,7 @@ func NewURLTestGroup(
|
||||
tolerance uint16,
|
||||
idleTimeout time.Duration,
|
||||
interruptExternalConnections bool,
|
||||
randomize bool,
|
||||
) (*URLTestGroup, error) {
|
||||
if interval == 0 {
|
||||
interval = C.DefaultURLTestInterval
|
||||
@ -250,6 +267,7 @@ func NewURLTestGroup(
|
||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
||||
interruptGroup: interrupt.NewGroup(),
|
||||
interruptExternalConnections: interruptExternalConnections,
|
||||
randomize: randomize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -349,6 +367,9 @@ func (g *URLTestGroup) loopCheck() {
|
||||
}
|
||||
g.pauseManager.WaitActive()
|
||||
g.CheckOutbounds(false)
|
||||
if g.randomize {
|
||||
g.selectBestLatencyOutbounds()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,7 +378,15 @@ func (g *URLTestGroup) CheckOutbounds(force bool) {
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) URLTest(ctx context.Context) (map[string]uint16, error) {
|
||||
return g.urlTest(ctx, false)
|
||||
result, err := g.urlTest(ctx, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g.randomize {
|
||||
g.selectBestLatencyOutbounds()
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint16, error) {
|
||||
@ -423,3 +452,69 @@ func (g *URLTestGroup) performUpdateCheck() {
|
||||
g.interruptGroup.Interrupt(g.interruptExternalConnections)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) selectBestLatencyOutbounds() {
|
||||
var bestTCPLatency uint16
|
||||
var bestUDPLatency uint16
|
||||
|
||||
var bestTCPOutbounds []adapter.Outbound
|
||||
var bestUDPOutbounds []adapter.Outbound
|
||||
|
||||
for _, detour := range g.outbounds {
|
||||
history := g.history.LoadURLTestHistory(RealTag(detour))
|
||||
if history == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if common.Contains(detour.Network(), N.NetworkTCP) {
|
||||
if bestTCPLatency == 0 || history.Delay < bestTCPLatency {
|
||||
bestTCPLatency = history.Delay
|
||||
}
|
||||
}
|
||||
if common.Contains(detour.Network(), N.NetworkUDP) {
|
||||
if bestUDPLatency == 0 || history.Delay < bestUDPLatency {
|
||||
bestUDPLatency = history.Delay
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, detour := range g.outbounds {
|
||||
history := g.history.LoadURLTestHistory(RealTag(detour))
|
||||
if history == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if common.Contains(detour.Network(), N.NetworkTCP) && history.Delay <= bestTCPLatency+g.tolerance {
|
||||
bestTCPOutbounds = append(bestTCPOutbounds, detour)
|
||||
}
|
||||
if common.Contains(detour.Network(), N.NetworkUDP) && history.Delay <= bestUDPLatency+g.tolerance {
|
||||
bestUDPOutbounds = append(bestUDPOutbounds, detour)
|
||||
}
|
||||
}
|
||||
|
||||
g.bestTCPLatencyOutbounds = bestTCPOutbounds
|
||||
g.bestUDPLatencyOutbounds = bestUDPOutbounds
|
||||
}
|
||||
|
||||
// selectRandomOutbound selects an outbound randomly among the outbounds with the best latency
|
||||
func (g *URLTestGroup) selectRandomOutbound(network string) adapter.Outbound {
|
||||
var bestOutbounds []adapter.Outbound
|
||||
|
||||
switch network {
|
||||
case N.NetworkTCP:
|
||||
bestOutbounds = g.bestTCPLatencyOutbounds
|
||||
case N.NetworkUDP:
|
||||
bestOutbounds = g.bestUDPLatencyOutbounds
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(bestOutbounds) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
randIndex := rand.Intn(len(bestOutbounds))
|
||||
g.logger.Debug("Random outbound selection: ", bestOutbounds[randIndex].Tag())
|
||||
|
||||
return bestOutbounds[randIndex]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user