Implemented hearthbit client side + tests
This commit is contained in:
@@ -172,27 +172,34 @@ const (
|
||||
reconnectDelay = 2 * time.Second
|
||||
readBufSize = 65536
|
||||
udpRcvBufSize = 8 * 1024 * 1024
|
||||
// keepAliveInterval is the unicast keepalive period. The UDPStreamer
|
||||
// server evicts silent unicast clients after its ClientTimeout (default
|
||||
// 30 s); an ACK from the same socket refreshes its last-seen without
|
||||
// triggering a CONFIG resend (a CONNECT would).
|
||||
keepAliveInterval = 15 * time.Second
|
||||
)
|
||||
|
||||
// UDPClient manages the connection to one MARTe2 streamer source.
|
||||
type UDPClient struct {
|
||||
serverAddr string
|
||||
sourceID string
|
||||
hub *Hub
|
||||
multicastGroup string
|
||||
dataPort int
|
||||
stopCh chan struct{}
|
||||
serverAddr string
|
||||
sourceID string
|
||||
hub *Hub
|
||||
multicastGroup string
|
||||
dataPort int
|
||||
keepAliveInterval time.Duration
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
// NewUDPClient creates a UDPClient bound to a specific source ID.
|
||||
func NewUDPClient(serverAddr, sourceID string, hub *Hub, multicastGroup string, dataPort int) *UDPClient {
|
||||
return &UDPClient{
|
||||
serverAddr: serverAddr,
|
||||
sourceID: sourceID,
|
||||
hub: hub,
|
||||
multicastGroup: multicastGroup,
|
||||
dataPort: dataPort,
|
||||
stopCh: make(chan struct{}),
|
||||
serverAddr: serverAddr,
|
||||
sourceID: sourceID,
|
||||
hub: hub,
|
||||
multicastGroup: multicastGroup,
|
||||
dataPort: dataPort,
|
||||
keepAliveInterval: keepAliveInterval,
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,6 +260,20 @@ func (u *UDPClient) runSession() error {
|
||||
return err
|
||||
}
|
||||
log.Printf("[%s] udp: sent CONNECT", u.sourceID)
|
||||
lastData := time.Now()
|
||||
lastKeepAlive := time.Now()
|
||||
// sendKeepAliveIfDue sends an ACK if the keepalive interval has elapsed.
|
||||
// ACK refreshes the server's last-seen without re-sending CONFIG (which a
|
||||
// repeated CONNECT would trigger).
|
||||
sendKeepAliveIfDue := func() error {
|
||||
if u.keepAliveInterval > 0 && time.Since(lastKeepAlive) >= u.keepAliveInterval {
|
||||
if _, err := conn.WriteToUDP(udpsprotocol.BuildAckPacket(), serverAddr); err != nil {
|
||||
return err
|
||||
}
|
||||
lastKeepAlive = time.Now()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
reassembler := udpsprotocol.NewReassembler(2 * time.Second)
|
||||
buf := make([]byte, readBufSize)
|
||||
@@ -260,14 +281,34 @@ func (u *UDPClient) runSession() error {
|
||||
var currentPublishMode uint8
|
||||
|
||||
for {
|
||||
conn.SetReadDeadline(time.Now().Add(silenceTimeout))
|
||||
// Wake up at least every keepalive interval so ACKs are sent even
|
||||
// when the server is idle; the read deadline also doubles as the
|
||||
// silence detector (no data for silenceTimeout = server gone).
|
||||
wakeup := silenceTimeout
|
||||
if u.keepAliveInterval > 0 && u.keepAliveInterval < wakeup {
|
||||
wakeup = u.keepAliveInterval
|
||||
}
|
||||
conn.SetReadDeadline(time.Now().Add(wakeup))
|
||||
|
||||
n, _, err := conn.ReadFromUDP(buf)
|
||||
arrivalTime := time.Now()
|
||||
if err != nil {
|
||||
if ne, ok := err.(net.Error); ok && ne.Timeout() {
|
||||
if time.Since(lastData) >= silenceTimeout {
|
||||
// True silence: stream is dead — Run() reconnects.
|
||||
conn.WriteToUDP(udpsprotocol.BuildDisconnectPacket(), serverAddr)
|
||||
return err
|
||||
}
|
||||
// Short wakeup: keepalive if due, then keep waiting.
|
||||
if kaErr := sendKeepAliveIfDue(); kaErr != nil {
|
||||
return kaErr
|
||||
}
|
||||
continue
|
||||
}
|
||||
conn.WriteToUDP(udpsprotocol.BuildDisconnectPacket(), serverAddr)
|
||||
return err
|
||||
}
|
||||
lastData = arrivalTime
|
||||
|
||||
if n < udpsprotocol.HeaderSize {
|
||||
log.Printf("[%s] udp: short datagram (%d bytes), skipping", u.sourceID, n)
|
||||
@@ -334,6 +375,10 @@ func (u *UDPClient) runSession() error {
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
if kaErr := sendKeepAliveIfDue(); kaErr != nil {
|
||||
return kaErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user