feat(protocol): Enhanced the heartbeat control mechanism of the frame writer

Added a heartbeatControl channel to support dynamic start/stop of the heartbeat function and optimized related resource management logic,

ensuring that the heartbeat ticker can be correctly stopped and rebuilt. Also adjusted the field initialization order to ensure concurrency safety.

fix(ui): Improved the tunnel connection status display style

Updated the URL line display content, added the "(forwarded link)" prompt text; adjusted the style layout of the local forwarding address and prompt information,

making the interface clearer and easier to read.
This commit is contained in:
Gouryella
2025-12-03 10:52:53 +08:00
parent fbd910b3a2
commit 0aa438e202
2 changed files with 53 additions and 14 deletions

View File

@@ -21,6 +21,7 @@ type FrameWriter struct {
heartbeatInterval time.Duration
heartbeatCallback func() *Frame
heartbeatEnabled bool
heartbeatControl chan struct{}
}
func NewFrameWriter(conn io.Writer) *FrameWriter {
@@ -29,12 +30,13 @@ func NewFrameWriter(conn io.Writer) *FrameWriter {
func NewFrameWriterWithConfig(conn io.Writer, maxBatch int, maxBatchWait time.Duration, queueSize int) *FrameWriter {
w := &FrameWriter{
conn: conn,
queue: make(chan *Frame, queueSize),
batch: make([]*Frame, 0, maxBatch),
maxBatch: maxBatch,
maxBatchWait: maxBatchWait,
done: make(chan struct{}),
conn: conn,
queue: make(chan *Frame, queueSize),
batch: make([]*Frame, 0, maxBatch),
maxBatch: maxBatch,
maxBatchWait: maxBatchWait,
done: make(chan struct{}),
heartbeatControl: make(chan struct{}, 1),
}
go w.writeLoop()
return w
@@ -54,9 +56,11 @@ func (w *FrameWriter) writeLoop() {
}
w.mu.Unlock()
if heartbeatTicker != nil {
defer heartbeatTicker.Stop()
}
defer func() {
if heartbeatTicker != nil {
heartbeatTicker.Stop()
}
}()
for {
select {
@@ -93,6 +97,19 @@ func (w *FrameWriter) writeLoop() {
}
w.mu.Unlock()
case <-w.heartbeatControl:
w.mu.Lock()
if heartbeatTicker != nil {
heartbeatTicker.Stop()
heartbeatTicker = nil
heartbeatCh = nil
}
if w.heartbeatEnabled && w.heartbeatInterval > 0 {
heartbeatTicker = time.NewTicker(w.heartbeatInterval)
heartbeatCh = heartbeatTicker.C
}
w.mu.Unlock()
case <-w.done:
w.mu.Lock()
w.flushBatchLocked()
@@ -156,15 +173,27 @@ func (w *FrameWriter) Flush() {
// EnableHeartbeat enables automatic heartbeat sending in the write loop.
func (w *FrameWriter) EnableHeartbeat(interval time.Duration, callback func() *Frame) {
w.mu.Lock()
defer w.mu.Unlock()
w.heartbeatInterval = interval
w.heartbeatCallback = callback
w.heartbeatEnabled = true
w.mu.Unlock()
select {
case w.heartbeatControl <- struct{}{}:
default:
// Channel already has a pending signal, no need to send another
}
}
// DisableHeartbeat disables automatic heartbeat sending.
func (w *FrameWriter) DisableHeartbeat() {
w.mu.Lock()
defer w.mu.Unlock()
w.heartbeatEnabled = false
w.mu.Unlock()
select {
case w.heartbeatControl <- struct{}{}:
default:
// Channel already has a pending signal, no need to send another
}
}