package main import ( "encoding/json" "fmt" "github.com/beevik/guid" "github.com/gorilla/websocket" "net" "net/http" "strconv" "sync" "time" ) var ( upgrader = websocket.Upgrader{ //允许跨域访问 CheckOrigin: func(r *http.Request) bool { return true }, EnableCompression: false, } ) func serviceHandler(w http.ResponseWriter, r *http.Request) { var mutex sync.Mutex sessionId := r.FormValue("session_id") build := r.FormValue("build") role := r.FormValue("role") w.Header().Set("Content-Type", "application/json; charset=utf-8") if len(sessionId) < 10 { sessionId = r.Header.Get("Sec-WebSocket-Protocol") w.Header().Add("sec-websocket-protocol", sessionId) } session := parsecService.GetSession(sessionId) if session == nil { err := &ErrorResponse{ Error: "invalid session ID", } w.WriteHeader(401) w.Write(formatJson(err)) return } wsConn, upgradeErr := upgrader.Upgrade(w, r, w.Header()) if upgradeErr != nil { return } defer func() { if reco := recover(); reco != any(nil) { fmt.Printf("Service Runtime error caught: %v", reco) } }() hostPeer := parsecService.GetPeer(session.HostPeerId) hostPeer.Build = build sessionGuid := guid.New().String() wsSession := parsecService.NewWsSession(sessionId, role, sessionGuid, wsConn) go func() { for { mutex.Lock() err2 := wsConn.WriteMessage(websocket.PingMessage, []byte{0x70, 0x69, 0x6E, 0x67}) mutex.Unlock() if err2 != nil { parsecService.RemoveWsSession(sessionId, role, sessionGuid) if role == ROLE_HOST { parsecService.SetupOnlineStatus(hostPeer) } wsConn.Close() return } time.Sleep(30 * time.Second) } }() for { //wsConn.SetReadDeadline(time.Now().In(TimeLocation).Add(time.Second * 60)) messageType, p, readError := wsConn.ReadMessage() if readError != nil { Logger.Println("Service WebSocket断开,PeerId=" + hostPeer.PeerId + " Role=" + role + ",信息:" + readError.Error()) parsecService.RemoveWsSession(sessionId, role, sessionGuid) if role == ROLE_HOST { parsecService.SetupOnlineStatus(hostPeer) } wsConn.Close() return } if messageType == websocket.CloseMessage { Logger.Println("Service WebSocket断开,PeerId=" + hostPeer.PeerId + " Role=" + role) parsecService.RemoveWsSession(sessionId, role, sessionGuid) if role == ROLE_HOST { parsecService.SetupOnlineStatus(hostPeer) } wsConn.Close() return } strData := string(p) Logger.Println("DATA:" + strData + "\n") var baseRequest *SocketRequest json.Unmarshal(p, &baseRequest) mutex.Lock() switch baseRequest.Action { case "conn_update": var connUpdateRequest *ConnUpdateRequest json.Unmarshal(p, &connUpdateRequest) peer := parsecService.GetPeer(session.HostPeerId) peer.Name = connUpdateRequest.Payload.Name peer.Players = connUpdateRequest.Payload.Players peer.Public = connUpdateRequest.Payload.Public peer.Secret = connUpdateRequest.Payload.Secret peer.Online = true case "offer": var offerRequest *OfferModel json.Unmarshal(p, &offerRequest) targetPeer := parsecService.GetPeer(offerRequest.Payload.To) if targetPeer != nil { wsSession.AttemptId = offerRequest.Payload.AttemptId targetWsSession := parsecService.GetWsSessionByPeerId(ROLE_HOST, targetPeer.PeerId) if targetWsSession != nil && targetWsSession.WsConn != nil { targetUser := parsecService.GetUserByEmail(targetPeer.Owner) offerRelay := &OfferModel{ SocketRequest: SocketRequest{ Version: 1, Action: "offer_relay", }, Payload: OfferPayload{ To: offerRequest.Payload.To, Data: offerRequest.Payload.Data, AttemptId: offerRequest.Payload.AttemptId, Secret: offerRequest.Payload.Secret, AccessLinkId: offerRequest.Payload.AccessLinkId, From: wsSession.HostPeerId, IsOwner: targetPeer.Owner == wsSession.Email, SkipApproval: targetPeer.Owner == wsSession.Email || targetPeer.Public || StringListContain(targetPeer.Assign, wsSession.Email), Permissions: Permission{ Gamepad: targetPeer.Owner == wsSession.Email || targetPeer.Public || offerRequest.Payload.Secret == targetPeer.Secret || StringListContain(targetPeer.Assign, wsSession.Email), Keyboard: targetPeer.Owner == wsSession.Email || targetPeer.Public || offerRequest.Payload.Secret == targetPeer.Secret || StringListContain(targetPeer.Assign, wsSession.Email), Mouse: targetPeer.Owner == wsSession.Email || targetPeer.Public || offerRequest.Payload.Secret == targetPeer.Secret || StringListContain(targetPeer.Assign, wsSession.Email), }, User: OfferUser{ Id: wsSession.UserId, TeamId: "", Name: wsSession.Email, ExternalId: "", ExternalProvider: "", }, HostUser: OfferUser{ Id: targetUser.UserId, TeamId: "", Name: "", ExternalId: "", ExternalProvider: "", }, }, } targetWsSession.WsConn.WriteMessage(websocket.TextMessage, formatJson(offerRelay)) } } case "answer": var answerRequest *AnswerModel json.Unmarshal(p, &answerRequest) targetSession := parsecService.GetWsSessionByAttemptId(answerRequest.Payload.To, answerRequest.Payload.AttemptId) if targetSession != nil && targetSession.WsConn != nil { answerRelay := &AnswerModel{ SocketRequest: SocketRequest{ Version: 1, Action: "answer_relay", }, Payload: AnswerPayload{ To: answerRequest.Payload.To, Data: answerRequest.Payload.Data, AttemptId: answerRequest.Payload.AttemptId, Approved: answerRequest.Payload.Approved, From: wsSession.HostPeerId, UserId: wsSession.UserId, }, } targetSession.WsConn.WriteMessage(websocket.TextMessage, formatJson(answerRelay)) } case "candex": var candexRequest *CandexModel json.Unmarshal(p, &candexRequest) targetSession := parsecService.GetWsSessionByAttemptId(candexRequest.Payload.To, candexRequest.Payload.AttemptId) if targetSession == nil { targetSession = parsecService.GetWsSessionByPeerId(ROLE_HOST, candexRequest.Payload.To) } if targetSession != nil && targetSession.WsConn != nil { candexRelay := &CandexModel{ SocketRequest: SocketRequest{ Version: 1, Action: "candex_relay", }, Payload: CandexPayload{ To: candexRequest.Payload.To, Data: candexRequest.Payload.Data, AttemptId: candexRequest.Payload.AttemptId, From: wsSession.HostPeerId, }, } targetSession.WsConn.WriteMessage(websocket.TextMessage, formatJson(candexRelay)) if hostPeer.External != "" { addr, _ := net.ResolveIPAddr("ip", hostPeer.External) candexRelayExternal := &CandexModel{ SocketRequest: SocketRequest{ Version: 1, Action: "candex_relay", }, Payload: CandexPayload{ To: candexRequest.Payload.To, AttemptId: candexRequest.Payload.AttemptId, From: wsSession.HostPeerId, Data: CandexData{ FromStun: true, Ip: "::ffff:" + addr.IP.String(), Lan: false, Port: candexRequest.Payload.Data.Port, Sync: false, VerData: candexRequest.Payload.Data.VerData, }, }, } Logger.Println("UseCustom External: IP=" + addr.IP.String() + " PORT=" + strconv.Itoa(candexRequest.Payload.Data.Port)) targetSession.WsConn.WriteMessage(websocket.TextMessage, formatJson(candexRelayExternal)) } } case "offer_cancel": var offerCancelRequest *OfferCancelModel json.Unmarshal(p, &offerCancelRequest) targetSession := parsecService.GetWsSessionByAttemptId(offerCancelRequest.Payload.To, offerCancelRequest.Payload.AttemptId) if targetSession != nil && targetSession.WsConn != nil { offerCancelRelay := &OfferCancelModel{ SocketRequest: SocketRequest{ Version: 1, Action: "offer_cancel_relay", }, Payload: OfferCancelPayload{ To: offerCancelRequest.Payload.To, AttemptId: offerCancelRequest.Payload.AttemptId, From: wsSession.HostPeerId, UserId: wsSession.UserId, }, } targetSession.WsConn.WriteMessage(websocket.TextMessage, formatJson(offerCancelRelay)) } } mutex.Unlock() } }