// connect to all services func (p *service_pool) connect_all(directory string) { client := p.client_pool.Get().(*etcd.Client) defer func() { p.client_pool.Put(client) }() // get the keys under directory log.Info("connecting services under:", directory) resp, err := client.Get(directory, true, true) if err != nil { log.Error(err) return } // validation check if !resp.Node.Dir { log.Error("not a directory") return } for _, node := range resp.Node.Nodes { if node.Dir { // service directory for _, service := range node.Nodes { p.add_service(service.Key, service.Value) } } else { log.Warning("malformed service directory:", node.Key) } } log.Info("services add complete") }
//------------------------------------------------------- user login func (s *server) Login(ctx context.Context, in *pb.User_LoginInfo) (*pb.User_LoginResp, error) { uuid := strings.ToUpper(in.Uuid) login_type := in.LoginType if uuid == "" { return nil, errors.New("require uuid") } cert := "" // TODO 根据登录类型进行第三方验证 switch login_type { case LOGIN_TYPE_UUID: // 不需要验证 // md5(uuid) -> cert cert = string(h.Sum([]byte(uuid))) // TODO case LOGIN_TYPE_FACEBOOK: // facebook default: log.Error("login type error", "logintype:", login_type, "uuid:", uuid) return nil, errors.New("login_type error") } new_user := false id, exist := db.IsExist(cert, login_type) if !exist { new_user = true id = s.next_uid() db.New(id, uuid, cert, in.Host, login_type) } return &pb.User_LoginResp{Uid: id, NewUser: new_user}, nil }
func init() { sess, err := mgo.Dial(URL) if err != nil { log.Error("mgo dial error", err, URL) os.Exit(-1) } _sess = sess }
// unlock a key on etcd, returns false if the key cannot be successfully unlocked func (m *Mutex) Unlock() bool { client := _client_pool.Get().(*etcd.Client) _, err := client.CompareAndDelete(LOCK_PATH+m.key, VALUE, m.prevIndex) if err != nil { log.Error(SERVICE, err) return false } return true }
// 玩家1分钟定时器 func timer_work(sess *Session, out *Buffer) { // 发包频率控制,太高的RPS直接踢掉 interval := time.Now().Sub(sess.ConnectTime).Minutes() if interval >= 1 { // 登录时长超过1分钟才开始统计rpm。防脉冲 rpm := float64(sess.PacketCount) / interval if rpm > RPM_LIMIT { sess.Flag |= SESS_KICKED_OUT log.Error("玩家RPM太高 RPM:", rpm) return } } }
func PrintPanicStack(extras ...interface{}) { if x := recover(); x != nil { log.Error(x) i := 0 funcName, file, line, ok := runtime.Caller(i) for ok { log.Errorf("frame %v:[func:%v,file:%v,line:%v]\n", i, runtime.FuncForPC(funcName).Name(), file, line) i++ funcName, file, line, ok = runtime.Caller(i) } for k := range extras { log.Errorf("EXRAS#%v DATA:%v\n", k, spew.Sdump(extras[k])) } } }
//--------------------------------------------------------- create new user func New(id int32, uuid, cert, host string, logintype int32) { ms, c := C(collection_name) defer ms.Close() auth := &types.Auth{ Id: id, Uuid: uuid, Cert: cert, Host: host, LoginType: logintype, CreateTime: time.Now().Unix(), } err := c.Insert(auth) if err != nil { log.Error("create user auth error", err, auth) } }
// lock a key on etcd and return mutex,returns nil if cannot lock the key func Lock(key string) *Mutex { m := &Mutex{} m.key = key client := _client_pool.Get().(*etcd.Client) defer func() { _client_pool.Put(client) }() for i := 0; i < RETRY_MAX; i++ { // create a key with ttl resp, err := client.Create(LOCK_PATH+key, VALUE, TTL) if err != nil { log.Error(SERVICE, err) <-time.After(RETRY_WAIT) continue } // remember index m.prevIndex = resp.Node.ModifiedIndex return m } return nil }
func checkErr(err error) { if err != nil { log.Error(err) panic("error occured in protocol module") } }
// client protocol handle proxy func proxy_user_request(sess *Session, p []byte) []byte { start := time.Now() defer utils.PrintPanicStack(sess, p) // 解密 if sess.Flag&SESS_ENCRYPT != 0 { sess.Decoder.Codec(p) } // 封装为reader reader := packet.Reader(p) // 读客户端数据包序列号(1,2,3...) // 可避免重放攻击-REPLAY-ATTACK seq_id, err := reader.ReadU32() if err != nil { log.Error("read client timestamp failed:", err) sess.Flag |= SESS_KICKED_OUT return nil } // 读协议号 b, err := reader.ReadS16() if err != nil { log.Error("read protocol number failed.") sess.Flag |= SESS_KICKED_OUT return nil } // 数据包序列号验证 if seq_id != sess.PacketCount { log.Errorf("illegal packet sequence id:%v should be:%v proto:%v size:%v", seq_id, sess.PacketCount, b, len(p)-6) sess.Flag |= SESS_KICKED_OUT return nil } var ret []byte if b > MAX_PROTO_NUM { // game协议 // 透传 ret, err = forward(sess, p) if err != nil { log.Errorf("service id:%v execute failed", b) sess.Flag |= SESS_KICKED_OUT return nil } } else { // agent保留协议段 [0, MAX_PROTO_NUM] // handle有效性检查 h := client_handler.Handlers[b] if h == nil { log.Errorf("service id:%v not bind", b) sess.Flag |= SESS_KICKED_OUT return nil } // 执行 ret = h(sess, reader) } // 统计处理时间 elasped := time.Now().Sub(start) if b != 0 { // 排除心跳包日志 log.Trace("[REQ]", b) _statter.Timing(1.0, fmt.Sprintf("%v%v", STATSD_PREFIX, b), elasped) } return ret }
// start a goroutine when a new connection is accepted func handleClient(conn *net.TCPConn) { defer utils.PrintPanicStack() // set per-connection socket buffer conn.SetReadBuffer(SO_RCVBUF) // set initial socket buffer conn.SetWriteBuffer(SO_SNDBUF) // initial network control struct header := make([]byte, 2) in := make(chan []byte) defer func() { close(in) // session will close }() // create a new session object for the connection var sess Session host, port, err := net.SplitHostPort(conn.RemoteAddr().String()) if err != nil { log.Error("cannot get remote address:", err) return } sess.IP = net.ParseIP(host) log.Infof("new connection from:%v port:%v", host, port) // session die signal sess_die := make(chan bool) // create a write buffer out := new_buffer(conn, sess_die) go out.start() // start one agent for handling packet wg.Add(1) go agent(&sess, in, out, sess_die) // network loop for { // solve dead link problem conn.SetReadDeadline(time.Now().Add(TCP_READ_DEADLINE * time.Second)) n, err := io.ReadFull(conn, header) if err != nil { log.Warningf("read header failed, ip:%v reason:%v size:%v", sess.IP, err, n) return } size := binary.BigEndian.Uint16(header) // alloc a byte slice for reading payload := make([]byte, size) // read msg n, err = io.ReadFull(conn, payload) if err != nil { log.Warningf("read payload failed, ip:%v reason:%v size:%v", sess.IP, err, n) return } select { case in <- payload: // payload queued case <-sess_die: log.Warningf("connection closed by logic, flag:%v ip:%v", sess.Flag, sess.IP) return } } }