type Connection struct { conn net.Conn // <- 얘가 접속 정보를 가지고 있음 writer *packet.PacketWriter reader *packet.PacketReader}
Dispatcher 코드
package dispatchimport ( "game-server/network" "reflect")// 핸들러는 무조건 두 정보를 가져야 함 (연결 정보 + 메시지)type HandlerFunc func(conn *network.Connection, msg any)// MessageType에 대응하는 핸들러를 연결하는 딕셔너리(맵)var handlers = map[reflect.Type]HandlerFunc{}// reflect.Type은 런타임에 확인하는 타입임// 핸들러 딕셔너리에 등록func Register(msg any, handler HandlerFunc) { t := reflect.TypeOf(msg) handlers[t] = handler}// 핸들러 딕셔너리를 기반으로 디스패치func Dispatch(conn *network.Connection, msg any) { t := reflect.TypeOf(msg) handler := handlers[t] handler(conn, msg)}
Handler (Ping) 코드
package handlerimport ( "fmt" "game-server/dispatch" "game-server/network" "game-server/protocol" "time")func init() { fmt.Println(time.Now(), " | Initialize Handler Started") dispatch.Register(&protocol.Ping{}, handlePing) fmt.Println(time.Now(), " | Initialize Handler Ended")}// MessageType : Ping 일 때 핸들러func handlePing(conn *network.Connection, msg any) { _, ok := msg.(*protocol.Ping) // 참고 : 여기서 msg 는 any 라는 타입인데, any 는 말 그대로 뭐든 받을 수 있음 // 타입 단언이라는 문법임. msg 안에 있는 데이터의 타입이 protocol.Ping 이면 // 데이터를 꺼내서 req 에 넣고, 아니라면 실패함 // 그래서 실제 데이터가 들어가는 req 와 성공 여부가 들어가는 ok 가 있음 if !ok { return } fmt.Println(time.Now(), " | Ping Request") serverTime := time.Now().UnixMilli() fmt.Println(time.Now(), " | Pong Response : ", serverTime) pong := &protocol.Pong{ Timestamp: serverTime, } conn.SendMessage(protocol.MessageType_PONG, pong)}