Java後端使用socketio,實現小程式答題pk功能
阿新 • • 發佈:2020-12-17
在使用socket.io跟前端通訊過程中,出現了一系列問題,現做下記錄。
一、功能需求是,在小程式端,使用者可相互邀請,進入房間後進行答題PK。實現方法是,使用者點選邀請好友,建立連線,查詢當前是否有房間,有房間傳送訊息給兩人,匹配成功,開始pk。沒有房間新建房間返回,等待20秒,等待別人匹配。
程式碼如下,先看配置,在application.yml配置檔案中增加如下配置
# host在本地測試可以設定為localhost或者本機IP,在Linux伺服器跑可換成伺服器IP socketio: host: 127.0.0.1 #監聽的ip port: 9999 #監聽埠 # 設定最大每幀處理資料的長度,防止他人利用大資料來攻擊伺服器 maxFramePayloadLength:1048576 # 設定http互動最大內容長度 maxHttpContentLength: 1048576 # socket連線數大小(如只監聽一個埠boss執行緒組為1即可) bossCount: 1 workCount: 100 allowCustomRequests: true # 協議升級超時時間(毫秒),預設10秒。HTTP握手升級為ws協議超時時間 upgradeTimeout: 1000000 # Ping訊息超時時間(毫秒),預設60秒,這個時間間隔內沒有接收到心跳訊息就會發送超時事件 pingTimeout: 6000000 # Ping訊息間隔(毫秒),預設25秒。客戶端向伺服器傳送一條心跳訊息間隔 pingInterval:25000
配置類
package com.cwn.wethink.remy.handler; import com.corundumstudio.socketio.SocketConfig; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.corundumstudio.socketio.SocketIOServer;/** * @description: * @author: m1575 * @create: 2020-11-11 **/ @Configuration class SocketIOConfig { @Value("${socketio.host}") private String host; @Value("${socketio.port}") private Integer port; @Value("${socketio.bossCount}") private int bossCount; @Value("${socketio.workCount}") private int workCount; @Value("${socketio.allowCustomRequests}") private boolean allowCustomRequests; @Value("${socketio.upgradeTimeout}") private int upgradeTimeout; @Value("${socketio.pingTimeout}") private int pingTimeout; @Value("${socketio.pingInterval}") private int pingInterval; /** * 以下配置在上面的application.properties中已經註明 * @return */ @Bean public SocketIOServer socketIOServer() { SocketConfig socketConfig = new SocketConfig(); socketConfig.setTcpNoDelay(true); socketConfig.setSoLinger(0); com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration(); config.setSocketConfig(socketConfig); config.setHostname(host); config.setPort(port); config.setBossThreads(bossCount); config.setWorkerThreads(workCount); config.setAllowCustomRequests(allowCustomRequests); config.setUpgradeTimeout(upgradeTimeout); config.setPingTimeout(pingTimeout); config.setPingInterval(pingInterval); return new SocketIOServer(config); } }
後臺實現
package com.cwn.wethink.remy.handler; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import com.alibaba.fastjson.JSONObject; import com.corundumstudio.socketio.*; import com.cwn.wethink.pojo.entity.Question; import com.cwn.wethink.remy.entity.PkAgainGame; import com.cwn.wethink.remy.entity.PkAnswerTime; import com.cwn.wethink.remy.entity.PkGroup; import com.cwn.wethink.remy.entity.WxUserInfo; import com.cwn.wethink.remy.mapper.PkMapper; import com.cwn.wethink.remy.mapper.WxUserInfoMapper; import com.cwn.wethink.remy.service.RemyCourseService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @Slf4j public class MessageEventHandler { // 用來存已連線的客戶端 private static Map<Long, SocketIOClient> clientMap = new ConcurrentHashMap<>(); @Autowired private SocketIOServer socketIOServer; @Autowired private PkMapper pkMapper; @Autowired private WxUserInfoMapper wxUserInfoMapper; @Autowired private RemyCourseService remyCourseService; /** * Spring IoC容器建立之後,在載入SocketIOServiceImpl Bean之後啟動 * @throws Exception */ @PostConstruct private void autoStartup() throws Exception { start(); } /** * Spring IoC容器在銷燬SocketIOServiceImpl Bean之前關閉,避免重啟專案服務端口占用問題 * @throws Exception */ @PreDestroy private void autoStop() throws Exception { stop(); } public void start() { // 監聽客戶端連線,同級挑戰比拼 socketIOServer.addConnectListener(client -> { Long uid = Long.valueOf(getParamsByClient(client)); log.info("connect come in,uid:{}",uid); //0為同級挑戰,1為邀請好友pk int type = 0; //房間號 int pkId = 0; //從請求的連線中拿出引數 Map<String, List<String>> params = client.getHandshakeData().getUrlParams(); List<String> list = params.get("type"); if (list != null && list.size() > 0) { type = Integer.valueOf(list.get(0)); } List<String> list1 = params.get("pkId"); if (list1 != null && list1.size() > 0) { pkId = Integer.valueOf(list1.get(0)); } if (uid != null) { if (clientMap.containsKey(uid)) { log.info(uid + "is reconnect"); clientMap.remove(uid); clientMap.put(uid , client); }else{ clientMap.put(uid, client); log.info("clientMap:"+clientMap); joinSendMessage(uid , client , type , pkId); } } }); // 監聽客戶端斷開連線 socketIOServer.addDisconnectListener(client -> { Long uid = Long.valueOf(getParamsByClient(client)); log.info("disconnect come in,uid:{}",uid); if (uid != null) { log.info("uid is not null come in,uid:{}",uid); clientMap.remove(uid); client.disconnect(); //退出通知對手 Long usrEntId = 0l; PkGroup pkGroup = pkMapper.getPkGroupUserNewRoom(uid); if(pkGroup != null){ log.info("pkGroup is not null come in,uid:{}",uid); if(uid == pkGroup.getInviteUsrEntId()){ usrEntId = pkGroup.getAcceptUsrEntId(); }else if(uid == pkGroup.getAcceptUsrEntId()){ usrEntId = pkGroup.getInviteUsrEntId(); } } if(usrEntId != null && usrEntId != 0l){ log.info("usrEntId is not null come in,uid:{}",uid); log.info("socketIOClient usrEntId:" + usrEntId); JSONObject jsonObject = new JSONObject(); SocketIOClient socketIOClient = clientMap.get(usrEntId); if(socketIOClient != null){ jsonObject.put("disconnect" , 1); socketIOClient.sendEvent("ClientReceive" , jsonObject); } } if(clientMap.get(usrEntId) == null || usrEntId == null){ if(pkGroup != null){ PkGroup updatePkGroup = pkMapper.getPkGroupById(pkGroup.getId()); updatePkGroup.setState(2); pkMapper.updatePkGroup(updatePkGroup); log.info("disconnect opponent is disconnect,uid:{}",uid); } } } log.info("disconnect is success,uid:{}",uid); }); // 處理自定義的事件,與連線監聽類似 // 此示例中測試的json收發 所以接收引數為JSONObject 如果是字元型別可以用String.class或者Object.class socketIOServer.addEventListener("ServerReceive",JSONObject.class, (client, data, ackSender) -> { JSONObject jsonObject = data; if(data != null){ String uid = jsonObject.getString("usrEntId"); String action = jsonObject.getString("action"); if("getAI".equals(action)){ log.info("getAI come in,uid:{}",uid); //和人機pk返回 botSendMessage(uid , client); }else if("challenge".equals(action)){ log.info("challenge come in,uid:{}",uid); //pk過程中每做一道題返回訊息給兩個人 int pkId = 0; if(!"".equals(jsonObject.getString("pkId"))){ pkId = Integer.valueOf(jsonObject.getString("pkId")); } if(pkId == 0){ log.info("challenge pkId is 0"); return; } long usrEntId = -1; if(!"".equals(jsonObject.getString("usrEntId"))){ usrEntId = Long.valueOf(jsonObject.getString("usrEntId")); } if(usrEntId == -1){ log.info("challenge usrEntId is -1"); return; } int answer = 0; if(!"".equals(jsonObject.getString("answer"))){ answer = Integer.valueOf(jsonObject.getString("answer")); } int time = 0; if(!"".equals(jsonObject.getString("time"))){ time = Integer.valueOf(jsonObject.getString("time")); } int queResId = 0; if(!"".equals(jsonObject.getString("queResId"))){ queResId = Integer.valueOf(jsonObject.getString("queResId")); } int orderNum = 0; if(!"".equals(jsonObject.getString("orderNum"))){ orderNum = Integer.valueOf(jsonObject.getString("orderNum")); } int option = 0; if(!"".equals(jsonObject.getString("option"))){ option = Integer.valueOf(jsonObject.getString("option")); } PkAnswerTime pkAnswerNow = new PkAnswerTime(); pkAnswerNow.setPkGroupId(pkId); pkAnswerNow.setUsrEntId(usrEntId); pkAnswerNow.setAnswer(answer); pkAnswerNow.setTime(time); pkAnswerNow.setQueResId(queResId); pkAnswerNow.setOrderNum(orderNum); pkAnswerNow.setOption(option); pkMapper.savePkAnswerTime(pkAnswerNow); PkGroup pkGroup = pkMapper.getPkGroupById(pkId); if(usrEntId == pkGroup.getInviteUsrEntId()){ long acceptUsrEntId = pkGroup.getAcceptUsrEntId(); judgeWinner(acceptUsrEntId , pkAnswerNow ,client); }else if(usrEntId == pkGroup.getAcceptUsrEntId()){ long inviteUsrEntId = pkGroup.getInviteUsrEntId(); judgeWinner(inviteUsrEntId , pkAnswerNow ,client); } }else if("again".equals(action)){ log.info("again come in"); //再來一局 int pkId = Integer.valueOf(jsonObject.getString("pkId")); log.info("pkId:"+pkId+"uid:"+uid); againSendMessage(uid , pkId, client); }else if("skill".equals(action)){ //使用技能 int pkId = Integer.valueOf(jsonObject.getString("pkId")); //技能id int infoId = Integer.valueOf(jsonObject.getString("info")); skillSendMessage(uid , pkId , infoId); } } }); socketIOServer.start(); log.info("socket.io初始化服務完成"); } public void stop() { if (socketIOServer != null) { socketIOServer.stop(); socketIOServer = null; } log.info("socket.io服務已關閉"); } /** * 此方法為獲取client連線中的引數,可根據需求更改 * @param client * @return */ private String getParamsByClient(SocketIOClient client) { // 從請求的連線中拿出引數(這裡的usrEntId必須是唯一標識) Map<String, List<String>> params = client.getHandshakeData().getUrlParams(); List<String> list = params.get("usrEntId"); if (list != null && list.size() > 0) { return list.get(0); } return null; } private synchronized void joinSendMessage(long usrEntId , SocketIOClient client , int type , int pkId){ // 給客戶端傳送一條資訊 傳送ConnectReceive事件 需要客戶端繫結此事件即可接收到訊息 JSONObject jsonObject = new JSONObject(); Date date = new Date(new Date().getTime() - 20000); PkGroup pkGroup = pkMapper.getPkGroupByState(usrEntId , type , date); if(type != 0 && pkId != 0){ pkGroup = pkMapper.getPkGroupById(pkId); } if(type != 0 && pkId == 0){ pkGroup = null; } if(pkGroup != null){ pkGroup.setAcceptUsrEntId(usrEntId); pkGroup.setState(1); pkMapper.updatePkGroup(pkGroup); long inviteUsrEntId = pkGroup.getInviteUsrEntId(); WxUserInfo invite = wxUserInfoMapper.queryWxUserInfoByUsrEntId(inviteUsrEntId); List<Question> questions = remyCourseService.listGetRandomTopic(0); jsonObject.put("state" , 1); jsonObject.put("wxUserInfo" , invite); jsonObject.put("questions" , questions); jsonObject.put("pkId" , pkGroup.getId()); client.sendEvent("ConnectReceive",jsonObject); SocketIOClient socketIOClient = clientMap.get(inviteUsrEntId); WxUserInfo accept = wxUserInfoMapper.queryWxUserInfoByUsrEntId(usrEntId); JSONObject acceptJson = new JSONObject(); acceptJson.put("state" , 1); acceptJson.put("questions" , questions); acceptJson.put("pkId" , pkGroup.getId()); acceptJson.put("wxUserInfo" , accept); socketIOClient.sendEvent("ConnectReceive" , acceptJson); }else{ PkGroup savePkGroup = new PkGroup(); savePkGroup.setInviteUsrEntId(usrEntId); savePkGroup.setState(0); savePkGroup.setCreateTime(new Date()); savePkGroup.setType(type); pkMapper.savePkGroup(savePkGroup); jsonObject.put("state" , 0); jsonObject.put("pkId" , savePkGroup.getId()); client.sendEvent("ConnectReceive",jsonObject); } } private synchronized void botSendMessage(String uid , SocketIOClient client){ JSONObject jsonObject = new JSONObject(); PkGroup pkGroup = pkMapper.getPkGroupByUsrEntIdToAI(Long.valueOf(uid)); if(pkGroup != null){ log.info("getAI pkGroup is not null come in,uid:{}",uid); pkGroup.setAcceptUsrEntId(0l); pkGroup.setState(1); pkMapper.updatePkGroup(pkGroup); List<Question> questions = remyCourseService.listGetRandomTopic(0); jsonObject.put("state" , 1); jsonObject.put("questions" , questions); jsonObject.put("pkId" , pkGroup.getId()); client.sendEvent("AIReceive",jsonObject); } } private synchronized void judgeWinner(long anotherEntId , PkAnswerTime pkAnswerNow, SocketIOClient client){ log.info("judgeWinner come in,anotherEntId:{}",anotherEntId); int pkId = pkAnswerNow.getPkGroupId(); int orderNum = pkAnswerNow.getOrderNum(); int answer = pkAnswerNow.getAnswer(); int time = pkAnswerNow.getTime(); long usrEntId = pkAnswerNow.getUsrEntId(); int option = pkAnswerNow.getOption(); JSONObject json = new JSONObject(); PkAnswerTime pkAnswerTime = pkMapper.getPkAnswerTimeByParam(anotherEntId , pkId , orderNum); if(pkAnswerTime != null){ log.info("judgeWinner pkAnswerTime is not null come in,pkAnswerTime:{}",pkAnswerTime); PkGroup pkGroup = pkMapper.getPkGroupById(pkId); if(orderNum == 5){ pkGroup.setState(2); pkMapper.updatePkGroup(pkGroup); } long winUsrEntId = -1; if(pkAnswerTime.getAnswer() == 1 && answer == 1){ if(time > pkAnswerTime.getTime()){ winUsrEntId = anotherEntId; }else if(time < pkAnswerTime.getTime()){ winUsrEntId = usrEntId; }else{ winUsrEntId = -1; } }else if(pkAnswerTime.getAnswer() == 1){ winUsrEntId = anotherEntId; }else if(answer == 1){ winUsrEntId = usrEntId; }else{ winUsrEntId = -1; } json.put("winUsrEntId" , winUsrEntId); json.put("pkId" , pkId); json.put("usrEntId" , anotherEntId); json.put("answer" , pkAnswerTime.getAnswer()); json.put("time" , pkAnswerTime.getTime()); json.put("option" , pkAnswerTime.getOption()); client.sendEvent("challengeReceive",json); if(anotherEntId != 0){ SocketIOClient socketIOClient = clientMap.get(anotherEntId); JSONObject acceptJson = new JSONObject(); acceptJson.put("pkId" , pkId); acceptJson.put("usrEntId" , usrEntId); acceptJson.put("answer", answer); acceptJson.put("time", time); acceptJson.put("option",option); acceptJson.put("winUsrEntId",winUsrEntId); socketIOClient.sendEvent("challengeReceive" , acceptJson); } if(pkGroup.getInviteUsrEntId() == winUsrEntId){ if(pkGroup.getInviteNum() != null){ pkGroup.setInviteNum(pkGroup.getInviteNum() + 1); }else{ pkGroup.setInviteNum(1); } }else if(pkGroup.getAcceptUsrEntId() == winUsrEntId){ if(pkGroup.getAcceptNum() != null){ pkGroup.setAcceptNum(pkGroup.getAcceptNum() + 1); }else{ pkGroup.setAcceptNum(1); } } pkMapper.updatePkNum(pkGroup); } } private synchronized void againSendMessage(String uid , int pkId , SocketIOClient client){ JSONObject json = new JSONObject(); long usrEntId = Long.valueOf(uid); PkGroup pkGroup = pkMapper.getPkGroupById(pkId); log.info("againSendMessage pkGroup:"+pkGroup); long opponentId = -1; if(pkGroup.getAcceptUsrEntId() != null){ if(usrEntId == pkGroup.getAcceptUsrEntId()){ opponentId = pkGroup.getInviteUsrEntId(); }else{ opponentId = pkGroup.getAcceptUsrEntId(); } } PkAgainGame pkAgainGame = pkMapper.getPkAgainGame(opponentId , pkId); log.info("againSendMessage pkAgainGame:"+pkAgainGame); if(pkAgainGame == null){ PkAgainGame againGame = new PkAgainGame(); againGame.setCreateTime(new Date()); againGame.setUsrEntId(usrEntId); againGame.setPkGroupId(pkId); pkMapper.savePkAgainGame(againGame); json.put("usrEntId" , usrEntId); json.put("state" , 0); SocketIOClient socketIOClient = clientMap.get(opponentId); log.info("againSendMessage socketIOClient:"+socketIOClient); socketIOClient.sendEvent("AgainReceive" , json); }else{ pkAgainGame.setOpponentUsrEntId(usrEntId); pkMapper.updatePkAgainGame(pkAgainGame); //建立房間 PkGroup savePkGroup = new PkGroup(); savePkGroup.setAcceptUsrEntId(usrEntId); savePkGroup.setInviteUsrEntId(opponentId); savePkGroup.setState(1); savePkGroup.setCreateTime(new Date()); savePkGroup.setType(pkGroup.getType()); pkMapper.savePkGroup(savePkGroup); List<Question> questions = remyCourseService.listGetRandomTopic(0); log.info("againSendMessage questions:"+questions); json.put("state" , 1); json.put("questions" , questions); json.put("pkId" , savePkGroup.getId()); if(opponentId == 0){ json.put("wxUserInfo" , ""); }else{ WxUserInfo invite = wxUserInfoMapper.queryWxUserInfoByUsrEntId(opponentId); json.put("wxUserInfo" , invite); } client.sendEvent("AgainReceive",json); if(opponentId != 0 && opponentId != -1){ SocketIOClient socketIOClient = clientMap.get(opponentId); JSONObject acceptJson = new JSONObject(); acceptJson.put("state" , 1); acceptJson.put("questions" , questions); acceptJson.put("pkId" , savePkGroup.getId()); WxUserInfo accept = wxUserInfoMapper.queryWxUserInfoByUsrEntId(usrEntId); acceptJson.put("wxUserInfo" , accept); log.info("againSendMessage socketIOClient:"+socketIOClient); socketIOClient.sendEvent("AgainReceive" , acceptJson); } } } private void skillSendMessage(String uid , int pkId , int infoId){ JSONObject json = new JSONObject(); long usrEntId = Long.valueOf(uid); PkGroup pkGroup = pkMapper.getPkGroupById(pkId); log.info("skillSendMessage pkGroup:"+pkGroup); long opponentId = -1; if(usrEntId == pkGroup.getAcceptUsrEntId()){ opponentId = pkGroup.getInviteUsrEntId(); }else{ opponentId = pkGroup.getAcceptUsrEntId(); } json.put("usrEntId" , usrEntId); json.put("skill" , 1); json.put("info" , infoId); SocketIOClient socketIOClient = clientMap.get(opponentId); log.info("skillSendMessage socketIOClient:"+socketIOClient); socketIOClient.sendEvent("SkillReceive" , json); } }
二、遇到的一些問題
1、最初在傳送訊息給兩人時,有個人能收到訊息,有個人收不到
剛開始clientMap是這樣宣告的:
private static Map<String, SocketIOClient> clientMap = new ConcurrentHashMap<>();
但我在查詢使用者SocketIOClient時使用的是long型別,所以一直沒查到使用者的SocketIOClient,也就傳送訊息發不過去。
後面改為
private static Map<Long, SocketIOClient> clientMap = new ConcurrentHashMap<>();
這樣宣告還是獲取不到,繼而檢查程式碼,開始返回給兩個人的資訊物件JSONObject,使用的同一個物件,雖然重新set了值,但還是返回了相同的物件。
JSONObject jsonObject = new JSONObject(); jsonObject.put("state" , 1); jsonObject.put("wxUserInfo" , invite); jsonObject.put("questions" , questions); jsonObject.put("pkId" , pkGroup.getId()); client.sendEvent("ConnectReceive",jsonObject); SocketIOClient socketIOClient = clientMap.get(inviteUsrEntId); WxUserInfo accept = wxUserInfoMapper.queryWxUserInfoByUsrEntId(usrEntId); jsonObject.put("state" , 1); jsonObject.put("questions" , questions); jsonObject.put("pkId" , pkGroup.getId()); jsonObject.put("wxUserInfo" , accept); socketIOClient.sendEvent("ConnectReceive" , jsonObject);
後改為重新new一個JSONObject物件,問題解決。
2、本地測試沒問題,上了測試環境出現問題。連線時間超過1分鐘,會自動斷開連線。
後經查是伺服器使用了nginx,nginx預設連線60s會斷開連線。
需更改nginx配置,如下:
location /socket.io { proxy_pass http://172.17.0.2:9999; proxy_set_header Host $host; proxy_next_upstream off; proxy_buffering off; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 3600s; }
proxy_read_timeout預設為60s,若需要長時間連線,改大點。
三、前端程式碼示例
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script> <script type="text/javascript"> const connectEvent = "ConnectReceive"; const aiEvent = "AIReceive"; const challengeEvent = "challengeReceive"; const sendEvent = "ServerReceive"; const againEvent = "AgainReceive"; const clientEvent = "ClientReceive"; const skillEvent = "SkillReceive"; var socket; function socketClick() { //連線伺服器,返回房間、題目資訊 socket = io.connect('http://127.0.0.1:9999', { 'force new connection': true, 'query': 'usrEntId=' + 41 + '&type=' + 1 }) socket.on(connectEvent, function (data) { //data:{pkId: 31,state: 0} 房間號、狀態為0表示建立了個房間,為1表示有房間並已加入 //data:{pkId: 31,questions:[{},{}...],state: 1,wxUserInfo:{openId:,unionId:,nickName:,headImg:,empiricalValue:,usrEntId:,createTime:,medal:}} console.log(data) }) socket.on(clientEvent, function (data) { //data:{disconnect: 1} 對手退出了房間 console.log(data) }) socket.on(skillEvent, function (data) { //data:{usrEntId:42 , skill: 1 , info:1} 對手id、使用了技能,技能id console.log(data) }) socket.on(aiEvent, function (data) { //data:{pkId: 31,questions:[{},{}...],state: 1} 房間號、題目集合、狀態 console.log(data) }) //每道題答完接收訊息 socket.on(challengeEvent, function (data) { //返回 data:{pkId: 31,winUsrEntId: 41,usrEntId:42,answer:1,time:3} 房間號、贏家id、對手id、答案1對0錯、時間 console.log('ServerReceive成功') }) socket.on(againEvent, function (data) { //data:{state: 0,usrEntId: 41} state為0邀請資訊、邀請再來一局玩家id //data:{state: 1,wxUserInfo: {},questions:[{},{}...],pkId} state為1再來一局成功、對手資訊、題目、房間號 console.log(data) }) socket.on(clientEvent, function (data) { //data:{disconnect: 1} 對手退出了房間 console.log(data) }) //傳送和人機pk訊息到伺服器 send({ action: 'getAI', usrEntId: 41 }) //每道題答完傳送訊息到伺服器 send({ action: 'challenge', usrEntId: 41, answer: 1, time: 6, pkId: 1, orderNum: 1, queResId: 1, option: 3 }) //再來一局 send({ action: 'again', usrEntId: 41, pkId:1 }) //傳送技能 send({ action: 'skill', usrEntId: 41, pkId:1 }) } function send(data){ socket.emit(sendEvent,data); } function quitClick() { var data={usrEntId:41}; socket.disconnect(); } </script>