1
\$\begingroup\$

I'm writing a service for a ORPG and I expect traffic of about 10kb of packets per second from multiple clients. I just want to know if my approach is correct? Are there areas I can improve?

This is a bit basic the full app would have a lot more handlers and packet structs.

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "io"
    "net"
)

var handlers map[uint16]func(conn net.Conn, data []byte)
var currDataVer uint16 = 100

type Packet struct {
    Length, ID uint16
}

// 1000+ more packet types with different fields
type VersionPacket struct {
    Packet
    Client, Upgrade, Data uint16
}

func main() {
    handlers = make(map[uint16]func(conn net.Conn, data []byte))

    listener, err := net.Listen("tcp", "127.0.0.1:2222")
    if err != nil {
        return // or panic?
    }
    defer listener.Close()

    // possibly up to 1000 handlers
    handlers[300] = func(conn net.Conn, data []byte) {
        var pVer VersionPacket

        err := binary.Read(bytes.NewReader(data), binary.LittleEndian, &pVer)
        if err != nil {
            return
        }

        // if version does not match
        if currDataVer != pVer.Data {
            resp := bytes.NewBuffer(make([]byte, 2))
            binary.Write(resp, binary.LittleEndian, uint16(100)) // packet id
            binary.Write(resp, binary.LittleEndian, uint16(13))  // packet data
            binary.Write(resp, binary.LittleEndian, uint16(35))
            binary.Write(resp, binary.LittleEndian, uint16(0))
            resb := resp.Bytes()
            binary.LittleEndian.PutUint16(resb, uint16(len(resb)))
            fmt.Println(resb)
            conn.Write(resb)
        } else {
            resp := bytes.NewBuffer(make([]byte, 2))
            binary.Write(resp, binary.LittleEndian, uint16(1001)) // packet id
            binary.Write(resp, binary.LittleEndian, uint16(60))   // packet data

            for i := 0; i < 2; i++ {
                binary.Write(resp, binary.LittleEndian, uint16(i))
            }

            resb := resp.Bytes()
            binary.LittleEndian.PutUint16(resb, uint16(len(resb)))
            conn.Write(resb)

            resp = bytes.NewBuffer(make([]byte, 2))
            binary.Write(resp, binary.LittleEndian, uint16(1002)) // packet id
            binary.Write(resp, binary.LittleEndian, uint16(61))   // packet data

            for i := 0; i < 3; i++ {
                binary.Write(resp, binary.LittleEndian, uint16(i))
            }

            resb = resp.Bytes()
            binary.LittleEndian.PutUint16(resb, uint16(len(resb)))
            conn.Write(resb)
        }
    }

    for {
        conn, err := listener.Accept()
        if err != nil {
            break
        }

        go process(conn)
    }
}

func process(conn net.Conn) {
    buffer := make([]byte, 2000)

    for {
        _, err := io.ReadFull(conn, buffer[:2])
        if err != nil {
            // error
            break
        }

        length := binary.LittleEndian.Uint16(buffer)

        if length > 2000 {
           // error too long
           // consume ignore continue
           // or disconnect
           conn.Close()
           return
        }

        // length only packet (keepalive)
        if length <= 2 {
            continue
        }

        _, err = io.ReadFull(conn, buffer[2:length-2])
        if err != nil {
            break
        }

        packetID := binary.LittleEndian.Uint16(buffer[2:])

        handler, ok := handlers[packetID]
        if ok {
            handler(conn, buffer[:length])
        } else {
            fmt.Println("packet with no handler:", packetID)
        }
    }
}
  • Is creating a new goroutine per accepted client worth it? I'm expecting about 500-1000 daily unique clients.
  • Will using the handler handlers[300] from multiple go routines result in a race condition?
  • Can the creation of packets be improved (DRY)?
  • Should I check the length of a []byte every time I try to access with an index to avoid getting a panic?
  • How about the error handling?
\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.