func WsHandler(ws *websocket.Conn) { addr := ws.Request().RemoteAddr var err error if err = setReadTimeout(ws, 60); err != nil { glog.Errorf("[ws:err] %v websocket.SetReadDeadline() error(%s)\n", addr, err) ws.Close() return } reply := make([]byte, 0, 256) if err = websocket.Message.Receive(ws, &reply); err != nil { glog.Errorf("[ws:err] %v websocket.Message.Receive() error(%v)\n", addr, err) ws.Close() return } // parse login params id, timestamp, timeout, encryShadow, loginErr := getLoginParams(string(reply)) if loginErr != nil { glog.Errorf("[ws:err] [%s] params (%s) error (%v)\n", addr, string(reply), loginErr) websocket.Message.Send(ws, AckWrongParams) ws.Close() return } // check login if err = isAuth(id, timestamp, timeout, encryShadow); err != nil { glog.Errorf("[ws:err] [%s] auth failed:\"%s\", error: %v", addr, string(reply), err) websocket.Message.Send(ws, LoginFailed.ErrorId) ws.Close() return } var bindedIds []int64 var mid byte if id > 0 { // 用户登录,检查其id是否为16整数倍,并为其分配一个1到15内的未使用的手机子id,相加后作为手机 // id,用于本session if id%int64(kUseridUnit) != 0 { glog.Warningf("[ws:err] invalid user id %d, low byte is not zero", id) err = websocket.Message.Send(ws, AckWrongLoginDevice) ws.Close() return } mobileid, err := SelectMobileId(id) if err != nil { glog.Warningf("[ws:err] select mobile id for user %d failed: %v", id, err) err = websocket.Message.Send(ws, AckServerError) ws.Close() return } if mobileid <= 0 { glog.Warningf("[ws:err] no valid mobile id for user %d, the user may have 15 clients now.", id) err = websocket.Message.Send(ws, AckWrongLoginDevice) ws.Close() return } newId := id + int64(mobileid%int(kUseridUnit)) if id > newId { glog.Errorf("[ws:err] user id overflow, origin id: %d, newId %d with mid %d", id, newId, mobileid) } mid = byte(mobileid) // 先用原始的用户id获取设备列表 bindedIds, err = GetUserDevices(id) id = newId // 防止错误的手机id溢出可用的范围 } else if id < 0 { bindedIds, err = GetDeviceUsers(id) } if err != nil { glog.Errorf("[ws:err] id [%d] get devices error: %v, devices: %v", id, err, bindedIds) websocket.Message.Send(ws, LoginFailed.ErrorId) ws.Close() return } statIncConnTotal() statIncConnOnline() defer statDecConnOnline() // 成功登陆后的一次回复 if id > 0 { err = websocket.Message.Send(ws, []byte{0, mid}) } else { err = websocket.Message.Send(ws, []byte{0}) } if err != nil { glog.Errorf("[ws:err] [%s] [uid: %d] sent login-ack error (%v)\n", addr, id, err) ws.Close() return } _, err = SetUserOnline(id, gLocalAddr) if err != nil { glog.Errorf("[ws:err] SetUserOnline error [uid: %d] %v\n", id, err) ws.Close() return } if glog.V(2) { glog.Infof("[ws:online] success id: %d, ip: %v, comet: %s, param: %s, binded ids: %v", id, addr, gLocalAddr, reply, bindedIds) } s := NewWsSession(id, bindedIds, NewWsConn(ws)) selement := gSessionList.AddSession(s) if id < 0 { destIds := gSessionList.CalcDestIds(s, 0) body := msgs.MsgStatus{} body.Type = msgs.MSTDeviceOnline body.Id = id m := msgs.NewMsg(nil, nil) m.FrameHeader.Opcode = 2 m.FrameHeader.SrcId = id m.DataHeader.MsgId = msgs.MIDStatus m.Data, _ = body.Marshal() GMsgBusManager.Push2Bus(id, destIds, m.MarshalBytes()) } if timeout <= 0 { timeout = TIME_OUT } ws.ReadTimeout = time.Duration(3*timeout) * time.Second for { if err = websocket.Message.Receive(ws, &reply); err != nil { glog.Errorf("[ws:err] [err:%v] causing ws closed", err) break } gSessionList.GetBindedIds(s, &bindedIds) if len(reply) == 1 && string(reply) == PING_MSG { if err = websocket.Message.Send(ws, PONG_MSG); err != nil { glog.Errorf("[ws:err] causing ws closed <%s> user_id:\"%d\" write heartbeat to client error(%s)\n", addr, id, err) break } } else { statIncUpStreamIn() msg := reply if len(msg) < kDstIdEnd { glog.Infof("[ws:err] causing ws closed Invalid msg lenght %d bytes, %v", len(msg), msg) break } // 根据手机与嵌入式协议,提取消息中的目标id toId := int64(binary.LittleEndian.Uint64(msg[kDstIdOffset:kDstIdEnd])) destIds := gSessionList.CalcDestIds(s, toId) if glog.V(3) { glog.Infof("[ws|received] %d -> %d, binded(%v), calc to: %v, data: (len: %d)%v...", id, toId, s.BindedIds, destIds, len(msg), msg) } else if glog.V(2) { glog.Infof("[ws|received] %d -> %d, binded(%v), calc to: %v, data: (len: %d)%v...", id, toId, s.BindedIds, destIds, len(msg), msg[0:kDstIdEnd]) } // Send to Message Bus GMsgBusManager.Push2Bus(id, destIds, msg) } //end = time.Now().UnixNano() } offlineErr := err err = SetUserOffline(id, gLocalAddr) if err != nil { glog.Errorf("[ws:offline|error] uid %d, error: %v", id, err) } if glog.V(2) { glog.Infof("[ws:offline] id:%d, comet: %s, reason: %v", id, gLocalAddr, offlineErr) } if id > 0 && mid > 0 { id -= int64(mid) ReturnMobileId(id, mid) // q := ReturnMobileId(id, mid) // if q != 1 { // glog.Errorf("[ws|return] return mid %d for user %d failed, error: %v", mid, id-int64(mid), err) // } else { // glog.Errorf("[ws|return] return mid %d for user %d successed, error: %v", mid, id-int64(mid), err) // } } if id < 0 { destIds := gSessionList.CalcDestIds(s, 0) body := msgs.MsgStatus{} body.Type = msgs.MSTDeviceOffline body.Id = id m := msgs.NewMsg(nil, nil) m.FrameHeader.Opcode = 2 m.FrameHeader.SrcId = id m.DataHeader.MsgId = msgs.MIDStatus m.Data, _ = body.Marshal() glog.Infof("deprecated [ws->udp] %v->%v ctn:%v\n", id, destIds, m) GMsgBusManager.Push2Bus(id, destIds, m.MarshalBytes()) } gSessionList.RemoveSession(selement) return }
func setReadTimeout(conn *websocket.Conn, delaySec int) error { return conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(delaySec))) }