func (t *Transaction) WriteResult(res TransactionResult) bool {
	if t.resultCh == nil {
		return false
	}
	t.resultCh <- res
	return true
}
WriteResult writes the result to the result channel
package client
import (
	"net"
	"sync"
	"time"
	"github.com/pion/stun"
)
const (
	maxRtxInterval time.Duration = 1600 * time.Millisecond
)
type TransactionResult struct {
	Msg     *stun.Message
	From    net.Addr
	Retries int
	Err     error
}
type TransactionConfig struct {
	Key          string
	Raw          []byte
	To           net.Addr
	Interval     time.Duration
	IgnoreResult bool  
}
type Transaction struct {
	Key      string                  
	Raw      []byte                  
	To       net.Addr                
	nRtx     int                     
	interval time.Duration           
	timer    *time.Timer             
	resultCh chan TransactionResult  
	mutex    sync.RWMutex
}
func NewTransaction(config *TransactionConfig) *Transaction {
	var resultCh chan TransactionResult
	if !config.IgnoreResult {
		resultCh = make(chan TransactionResult)
	}
	return &Transaction{
		Key:      config.Key,       
		Raw:      config.Raw,       
		To:       config.To,        
		interval: config.Interval,  
		resultCh: resultCh,         
	}
}
func (t *Transaction) StartRtxTimer(onTimeout func(trKey string, nRtx int)) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	t.timer = time.AfterFunc(t.interval, func() {
		t.mutex.Lock()
		t.nRtx++
		nRtx := t.nRtx
		t.interval *= 2
		if t.interval > maxRtxInterval {
			t.interval = maxRtxInterval
		}
		t.mutex.Unlock()
		onTimeout(t.Key, nRtx)
	})
}
func (t *Transaction) StopRtxTimer() {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	if t.timer != nil {
		t.timer.Stop()
	}
}