working
This commit is contained in:
118
pkg/transport/socket.go
Normal file
118
pkg/transport/socket.go
Normal file
@@ -0,0 +1,118 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user