Skip to content

Commit 32e5be4

Browse files
fix(backend): add time limit udp
1 parent d165bb2 commit 32e5be4

5 files changed

Lines changed: 89 additions & 12 deletions

File tree

backend/cmd/config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ keep_alive_ms = 1000 # Keep-alive interval in milliseconds
3636
[udp]
3737
ring_buffer_size = 64 # Size of the ring buffer for incoming packets (number of packets, not bytes)
3838
packet_chan_size = 16 # Size of the channel buffer
39+
keep_alive_check_interval_ms = 5 # How often the keep-alive checks last-received times (ms)
40+
keep_alive_timeout_ms = 100 # Max time without packets from an IP before it is considered disconnected (ms)
3941

4042

4143
# Logger Configuration

backend/cmd/dev-config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ keep_alive_ms = 0 # Keep-alive interval in milliseconds (0 to disab
3838
[udp]
3939
ring_buffer_size = 64 # Size of the ring buffer for incoming packets (number of packets, not bytes)
4040
packet_chan_size = 16 # Size of the channel buffer
41+
keep_alive_check_interval_ms = 5 # How often the keep-alive checks last-received times (ms)
42+
keep_alive_timeout_ms = 100 # Max time without packets from an IP before it is considered disconnected (ms)
4143

4244
# Server Configuration
4345
[server.ethernet-view]

backend/cmd/setup_transport.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,16 @@ func configureUDPServerTransport(
140140

141141
) {
142142
trace.Info().Msg("Starting UDP server")
143-
udpServer := udp.NewServer(adj.Info.Addresses[BACKEND], adj.Info.Ports[UDP], &trace.Logger, config.UDP.RingBufferSize, config.UDP.PacketChanSize)
143+
udpServer := udp.NewServer(
144+
adj.Info.Addresses[BACKEND],
145+
adj.Info.Ports[UDP],
146+
&trace.Logger,
147+
config.UDP.RingBufferSize,
148+
config.UDP.PacketChanSize,
149+
time.Duration(config.UDP.KeepAliveCheckIntervalMs)*time.Millisecond,
150+
time.Duration(config.UDP.KeepAliveTimeoutMs)*time.Millisecond,
151+
transp.SendFault,
152+
)
144153
err := udpServer.Start()
145154
if err != nil {
146155
trace.Fatal().Err(err).Msg("failed to start UDP server: " + err.Error())

backend/internal/config/config_types.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ type TCP struct {
2525
}
2626

2727
type UDP struct {
28-
RingBufferSize int `toml:"ring_buffer_size"`
29-
PacketChanSize int `toml:"packet_chan_size"`
28+
RingBufferSize int `toml:"ring_buffer_size"`
29+
PacketChanSize int `toml:"packet_chan_size"`
30+
KeepAliveCheckIntervalMs int `toml:"keep_alive_check_interval_ms"`
31+
KeepAliveTimeoutMs int `toml:"keep_alive_timeout_ms"`
3032
}
3133

3234
type Logging struct {

backend/pkg/transport/network/udp/server.go

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,38 @@ type Server struct {
3434
count int
3535
ringMutex sync.Mutex
3636
notEmpty *sync.Cond
37+
38+
lastSeen map[string]time.Time
39+
lastSeenMu sync.Mutex
40+
keepAliveCheckInterval time.Duration
41+
keepAliveTimeout time.Duration
42+
OnDisconnect func()
3743
}
3844

39-
func NewServer(address string, port uint16, logger *zerolog.Logger, ringBufferSize int, packetChanSize int) *Server {
45+
const (
46+
defaultKeepAliveCheckInterval = 5 * time.Millisecond
47+
defaultKeepAliveTimeout = 100 * time.Millisecond
48+
)
49+
50+
func NewServer(address string, port uint16, logger *zerolog.Logger, ringBufferSize int, packetChanSize int, keepAliveCheckInterval time.Duration, keepAliveTimeout time.Duration, onDisconnect func()) *Server {
51+
if keepAliveCheckInterval <= 0 {
52+
keepAliveCheckInterval = defaultKeepAliveCheckInterval
53+
}
54+
if keepAliveTimeout <= 0 {
55+
keepAliveTimeout = defaultKeepAliveTimeout
56+
}
57+
4058
s := &Server{
41-
address: address,
42-
port: port,
43-
logger: logger,
44-
packetsCh: make(chan Packet, packetChanSize),
45-
errorsCh: make(chan error, 100),
46-
stopCh: make(chan struct{}),
59+
address: address,
60+
port: port,
61+
logger: logger,
62+
packetsCh: make(chan Packet, packetChanSize),
63+
errorsCh: make(chan error, 100),
64+
stopCh: make(chan struct{}),
65+
lastSeen: make(map[string]time.Time),
66+
keepAliveCheckInterval: keepAliveCheckInterval,
67+
keepAliveTimeout: keepAliveTimeout,
68+
OnDisconnect: onDisconnect,
4769
}
4870

4971
s.ring = make([]Packet, ringBufferSize)
@@ -71,8 +93,9 @@ func (s *Server) Start() error {
7193
Uint16("port", s.port).
7294
Msg("UDP server started")
7395

74-
go s.readLoop()
75-
go s.dispatchLoop()
96+
go s.readLoop() // Read incoming UDP packets
97+
go s.dispatchLoop() // Dispatch packets from ring buffer to channel
98+
go s.keepAliveLoop() // Monitor keep-alive timeouts
7699
return nil
77100
}
78101

@@ -121,12 +144,51 @@ func (s *Server) readLoop() {
121144
Int("size", len(payload)).
122145
Msg("received UDP packet")
123146

147+
// Update keep-alive tracking for this source IP
148+
s.lastSeenMu.Lock()
149+
s.lastSeen[addr.IP.String()] = packet.Timestamp
150+
s.lastSeenMu.Unlock()
151+
124152
// Push packet to ring buffer
125153
s.push(packet)
126154
}
127155
}
128156
}
129157

158+
// keepAliveLoop checks for clients that have not sent any packets within the keepAliveTimeout duration.
159+
func (s *Server) keepAliveLoop() {
160+
ticker := time.NewTicker(s.keepAliveCheckInterval)
161+
defer ticker.Stop()
162+
163+
for {
164+
select {
165+
case <-s.stopCh:
166+
return
167+
case now := <-ticker.C:
168+
var timedOut []string
169+
170+
s.lastSeenMu.Lock()
171+
for ip, last := range s.lastSeen {
172+
if now.Sub(last) > s.keepAliveTimeout {
173+
timedOut = append(timedOut, ip)
174+
delete(s.lastSeen, ip)
175+
}
176+
}
177+
s.lastSeenMu.Unlock()
178+
179+
for _, ip := range timedOut {
180+
s.logger.Error().
181+
Str("ip", ip).
182+
Dur("timeout", s.keepAliveTimeout).
183+
Msg("keep-alive timeout: no UDP packets received")
184+
if s.OnDisconnect != nil {
185+
s.OnDisconnect()
186+
}
187+
}
188+
}
189+
}
190+
}
191+
130192
func (s *Server) GetPackets() <-chan Packet {
131193
return s.packetsCh
132194
}

0 commit comments

Comments
 (0)