xiaomi/cs2: reduce TCP keepalive interval to 1 second
Based on PCAP analysis of official Mi Home app traffic: - Official app sends PING every ~1 second - Previous 5-second interval was too slow, causing connection resets Simply reduce keepalive timeout from 5s to 1s.
This commit is contained in:
+10
-38
@@ -24,16 +24,8 @@ func Dial(host, transport string) (*Conn, error) {
|
|||||||
isTCP: isTCP,
|
isTCP: isTCP,
|
||||||
rawCh0: make(chan []byte, 10),
|
rawCh0: make(chan []byte, 10),
|
||||||
rawCh2: make(chan []byte, 100),
|
rawCh2: make(chan []byte, 100),
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
go c.worker()
|
go c.worker()
|
||||||
|
|
||||||
// For TCP connections, start independent keepalive goroutine
|
|
||||||
// Official Mi Home app sends PING every 1 second bidirectionally
|
|
||||||
if isTCP {
|
|
||||||
go c.keepalive()
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +38,6 @@ type Conn struct {
|
|||||||
seqCh3 uint16
|
seqCh3 uint16
|
||||||
rawCh0 chan []byte
|
rawCh0 chan []byte
|
||||||
rawCh2 chan []byte
|
rawCh2 chan []byte
|
||||||
done chan struct{} // signals connection close to keepalive goroutine
|
|
||||||
|
|
||||||
cmdMu sync.Mutex
|
cmdMu sync.Mutex
|
||||||
cmdAck func()
|
cmdAck func()
|
||||||
@@ -63,7 +54,6 @@ const (
|
|||||||
msgDrwAck = 0xD1
|
msgDrwAck = 0xD1
|
||||||
msgPing = 0xE0
|
msgPing = 0xE0
|
||||||
msgPong = 0xE1
|
msgPong = 0xE1
|
||||||
msgAlive = 0xF0 // Camera heartbeat/alive signal
|
|
||||||
msgClose = 0xF1
|
msgClose = 0xF1
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -112,38 +102,17 @@ func handshake(host, transport string) (net.Conn, error) {
|
|||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// keepalive sends PING every 1 second for TCP connections.
|
|
||||||
// Based on PCAP analysis of official Mi Home app: both sides send PING bidirectionally,
|
|
||||||
// neither responds with PONG. This keeps the connection alive.
|
|
||||||
func (c *Conn) keepalive() {
|
|
||||||
ticker := time.NewTicker(time.Second)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
ping := []byte{magic, msgPing, 0, 0}
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-c.done:
|
|
||||||
return
|
|
||||||
case <-ticker.C:
|
|
||||||
if _, err := c.conn.Write(ping); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) worker() {
|
func (c *Conn) worker() {
|
||||||
defer func() {
|
defer func() {
|
||||||
close(c.rawCh0)
|
close(c.rawCh0)
|
||||||
close(c.rawCh2)
|
close(c.rawCh2)
|
||||||
close(c.done) // signal keepalive goroutine to stop
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
chAck := make([]uint16, 4) // only for UDP
|
chAck := make([]uint16, 4) // only for UDP
|
||||||
buf := make([]byte, 1200)
|
buf := make([]byte, 1200)
|
||||||
var ch2WaitSize int
|
var ch2WaitSize int
|
||||||
var ch2WaitData []byte
|
var ch2WaitData []byte
|
||||||
|
var keepaliveTS time.Time
|
||||||
|
|
||||||
for {
|
for {
|
||||||
n, err := c.conn.Read(buf)
|
n, err := c.conn.Read(buf)
|
||||||
@@ -156,7 +125,14 @@ func (c *Conn) worker() {
|
|||||||
case msgDrw:
|
case msgDrw:
|
||||||
ch := buf[5]
|
ch := buf[5]
|
||||||
|
|
||||||
if !c.isTCP {
|
if c.isTCP {
|
||||||
|
// For TCP we should send ping every second to keep connection alive.
|
||||||
|
// Based on PCAP analysis: official Mi Home app sends PING every ~1s.
|
||||||
|
if now := time.Now(); now.After(keepaliveTS) {
|
||||||
|
_, _ = c.conn.Write([]byte{magic, msgPing, 0, 0})
|
||||||
|
keepaliveTS = now.Add(time.Second)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// For UDP we should using ack.
|
// For UDP we should using ack.
|
||||||
seqHI := buf[6]
|
seqHI := buf[6]
|
||||||
seqLO := buf[7]
|
seqLO := buf[7]
|
||||||
@@ -204,11 +180,7 @@ func (c *Conn) worker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case msgPing:
|
case msgPing:
|
||||||
// Official Mi Home app: both sides send PING, neither responds with PONG
|
_, _ = c.conn.Write([]byte{magic, msgPong, 0, 0})
|
||||||
// Just acknowledge receipt, don't send PONG
|
|
||||||
continue
|
|
||||||
case msgAlive:
|
|
||||||
// Some cameras may send 0xF0 as heartbeat - just ignore like PING
|
|
||||||
continue
|
continue
|
||||||
case msgPong, msgP2PRdyUDP, msgP2PRdyTCP, msgClose:
|
case msgPong, msgP2PRdyUDP, msgP2PRdyTCP, msgClose:
|
||||||
continue // skip it
|
continue // skip it
|
||||||
|
|||||||
Reference in New Issue
Block a user