eq2go/internal/udp/packet.go

85 lines
1.9 KiB
Go

package udp
import (
"encoding/binary"
"fmt"
"hash/crc32"
"time"
)
// Packet types
const (
PacketTypeData uint8 = iota
PacketTypeAck
PacketTypeSessionRequest
PacketTypeSessionResponse
PacketTypeKeepAlive
PacketTypeDisconnect
PacketTypeFragment
)
// packet represents a protocol packet
type packet struct {
Type uint8
Sequence uint16
Ack uint16
Session uint32
Data []byte
CRC uint32
}
// Marshal serializes the packet
func (p *packet) Marshal() []byte {
dataLen := len(p.Data)
buf := make([]byte, 15+dataLen) // Fixed header + data
buf[0] = p.Type
binary.BigEndian.PutUint16(buf[1:3], p.Sequence)
binary.BigEndian.PutUint16(buf[3:5], p.Ack)
binary.BigEndian.PutUint32(buf[5:9], p.Session)
binary.BigEndian.PutUint16(buf[9:11], uint16(dataLen))
copy(buf[11:11+dataLen], p.Data)
// Calculate CRC32 for header + data
p.CRC = crc32.ChecksumIEEE(buf[:11+dataLen])
binary.BigEndian.PutUint32(buf[11+dataLen:], p.CRC)
return buf
}
// Unmarshal deserializes the packet
func (p *packet) Unmarshal(data []byte) error {
if len(data) < 15 {
return fmt.Errorf("packet too short: %d bytes", len(data))
}
p.Type = data[0]
p.Sequence = binary.BigEndian.Uint16(data[1:3])
p.Ack = binary.BigEndian.Uint16(data[3:5])
p.Session = binary.BigEndian.Uint32(data[5:9])
dataLen := binary.BigEndian.Uint16(data[9:11])
if len(data) < 15+int(dataLen) {
return fmt.Errorf("incomplete packet: expected %d bytes, got %d", 15+dataLen, len(data))
}
p.Data = make([]byte, dataLen)
copy(p.Data, data[11:11+dataLen])
p.CRC = binary.BigEndian.Uint32(data[11+dataLen:])
// Verify CRC
expectedCRC := crc32.ChecksumIEEE(data[:11+dataLen])
if p.CRC != expectedCRC {
return fmt.Errorf("CRC mismatch: expected %x, got %x", expectedCRC, p.CRC)
}
return nil
}
// pendingPacket represents a packet awaiting acknowledgment
type pendingPacket struct {
packet *packet
timestamp time.Time
attempts int
}