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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.