119 lines
2.4 KiB
Go
119 lines
2.4 KiB
Go
package transport
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"go-ts/pkg/protocol"
|
|
)
|
|
|
|
// TS3Conn handles the UDP connection to the TeamSpeak server.
|
|
type TS3Conn struct {
|
|
conn *net.UDPConn
|
|
readQueue chan *protocol.Packet
|
|
closeChan chan struct{}
|
|
wg sync.WaitGroup
|
|
writeMu sync.Mutex
|
|
}
|
|
|
|
// NewTS3Conn creates a new connection to the specified address.
|
|
func NewTS3Conn(address string) (*TS3Conn, error) {
|
|
raddr, err := net.ResolveUDPAddr("udp", address)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to resolve address: %w", err)
|
|
}
|
|
|
|
conn, err := net.DialUDP("udp", nil, raddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to dial UDP: %w", err)
|
|
}
|
|
|
|
ts3c := &TS3Conn{
|
|
conn: conn,
|
|
readQueue: make(chan *protocol.Packet, 100),
|
|
closeChan: make(chan struct{}),
|
|
}
|
|
|
|
ts3c.startReader()
|
|
|
|
return ts3c, nil
|
|
}
|
|
|
|
func (c *TS3Conn) startReader() {
|
|
c.wg.Add(1)
|
|
go func() {
|
|
defer c.wg.Done()
|
|
buf := make([]byte, 2048) // Max packet size should be around 500, but use larger buffer to be safe
|
|
for {
|
|
select {
|
|
case <-c.closeChan:
|
|
return
|
|
default:
|
|
c.conn.SetReadDeadline(time.Now().Add(1 * time.Second))
|
|
n, _, err := c.conn.ReadFromUDP(buf)
|
|
if err != nil {
|
|
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
|
continue
|
|
}
|
|
// If closed, return
|
|
select {
|
|
case <-c.closeChan:
|
|
return
|
|
default:
|
|
fmt.Printf("Error reading valid UDP packet: %v\n", err)
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Parse packet
|
|
pkt, err := protocol.Decode(buf[:n], false) // Server -> Client
|
|
if err != nil {
|
|
fmt.Printf("Failed to decode packet: %v\n", err)
|
|
continue
|
|
}
|
|
|
|
select {
|
|
case c.readQueue <- pkt:
|
|
default:
|
|
fmt.Println("Read queue full, dropping packet")
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// SendPacket sends a packet to the server.
|
|
func (c *TS3Conn) SendPacket(pkt *protocol.Packet) error {
|
|
c.writeMu.Lock()
|
|
defer c.writeMu.Unlock()
|
|
|
|
// Client -> Server
|
|
bytes, err := pkt.Encode(true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = c.conn.Write(bytes)
|
|
return err
|
|
}
|
|
|
|
// ReadPacket returns the next received packet channel.
|
|
func (c *TS3Conn) PacketChan() <-chan *protocol.Packet {
|
|
return c.readQueue
|
|
}
|
|
|
|
// RemoteAddr returns the remote network address.
|
|
func (c *TS3Conn) RemoteAddr() net.Addr {
|
|
return c.conn.RemoteAddr()
|
|
}
|
|
|
|
// Close closes the connection.
|
|
func (c *TS3Conn) Close() error {
|
|
close(c.closeChan)
|
|
c.conn.Close()
|
|
c.wg.Wait()
|
|
return nil
|
|
}
|