在使用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>
 

Java后端使用socketio,实现小程序答题pk功能的更多相关文章

  1. 微信小程序答题系统实现随机出题 答题小程序如何实现随机出题 微信小程序 答题系统

    最近头脑王者非常火爆,公司也在开发类似头脑王者的答题系统,这个重任交到我这边来了,我们在开发的这个微信小程序答题系统,需要实现随机出题.尤其是一些比如闯关的环节,需要随机从题库里抽取若干道题目,给到用 ...

  2. Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战

    Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战 说明:Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战,优惠券是一种常见的促销方式,在规定的周期内购买对应商品类型和额度的商品 ...

  3. Java生鲜电商平台-小程序或者APP拼团功能设计与架构实战

    Java生鲜电商平台-小程序或者APP拼团功能设计与架构实战 说明:Java生鲜电商平台拼团是拉新引流的利器,将拼团运用到极致的就是拼多多,前期通过选取性价比高.实用性强的商品进行拼团,在社交圈(主要 ...

  4. 微信小程序答题,怎么设计页面渲染,答完一题,跳到下一题

    想要的效果 1.第一页只显示第一道题的内容,如图红框2.答题后,点击下一题,内容显示第二道题的内容 代码 answer.wxml <!--pages/answer/answer.wxml--&g ...

  5. 微信小程序调用蓝牙功能控制车位锁

    第一次学用微信小程序,项目需要,被逼着研究了一下,功能是调用微信小程序的蓝牙功能,连接上智能车位锁,控制升降,大概步骤及调用的小程序接口API如下: 1.打开蓝牙模块 wx.openBluetooth ...

  6. 微信小程序新闻列表功能(读取文件、template模板使用)

    微信小程序新闻列表功能(读取文件.template) 不忘初心,方得始终.初心易得,始终难守. 在之前的项目基础上进行修改,实现读取文件内容作为新闻内容进行展示. 首先,修改 post.wxml 文件 ...

  7. 微信小程序实战 购物车功能

    代码地址如下:http://www.demodashi.com/demo/12400.html 一.准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.com/ ...

  8. 微信小程序在线支付功能使用总结

    最近需要在微信小程序中用到在线支付功能,于是看了一下官方的文档,发现要在小程序里实现微信支付还是很方便的,如果你以前开发过服务号下的微信支付,那么你会发现其实小程序里的微信支付和服务号里的开发过程如出 ...

  9. 微信小程序开发-蓝牙功能开发

    0. 前言 这两天刚好了解了一下微信小程序的蓝牙功能.主要用于配网功能.发现微信的小程序蓝牙API已经封装的很好了.编程起来很方便.什么蓝牙知识都不懂的情况下,不到两天就晚上数据的收发了,剩下的就是数 ...

随机推荐

  1. 金九银十已到!Cookie 和 Session的这些知识你必须知道,面试必问!

    前言 会话:一次会话中包含多次请求和响应 注:一次会话表示浏览器第一次给服务器发送请求,会话建立,直到有一方断开为止 功能:在一次会话的多次请求间共享数据 方式: (1) 客户端会话技术:Cookie ...

  2. MindManager使用教程:如何导出HTML5交互式导图

    Mindmanager思维导图软件有着友好的用户界面以及丰富的思维导图制作功能.再搭配与Microsoft 软件的无缝集成功能,使得这款思维导图软件越来越受到职场人士的喜爱. 不仅是作为制作思维导图的 ...

  3. 「CF578F」 Mirror Box

    description CF578F solution 考虑转化题目的要求 1.对于任意一条边,都存在一条从界垂直射入的光线,经过反射穿过这条边. 当图中有环时,环内的边一定不满足条件,而在不存在环时 ...

  4. MySQL常用命令与语句

    目录 Shell命令 查看系统信息 查看系统变量 设置系统变量 数据库操作 查看表信息 修改表语句 操作表 操作索引 操作约束 操作列 查询常用语句 Shell命令 mysql -uroot -p12 ...

  5. 通俗解析莱文斯坦距离(Levenshtein Distance)计算原理(最小编辑距离)

    [版权声明]:本文章由danvid发布于http://danvid.cnblogs.com/,如需转载或部分使用请注明出处 最近看到一些动态规划的东西讲到莱文斯坦距离(编辑距离)的计算,发现很多都讲的 ...

  6. symfony框架中使用service

    在config文件里面的service.yml写入自己service 1 chat.group_list: //service的名字 2 class: Chat\Service\GroupListSe ...

  7. 《高并发下的.NET》第2季 - 《memcached连接暴增案》第1集:问题表现

    在<.NET 5.0 背锅案>第7集-大结局之后,园子和 .NET 继续过上了幸福生活...剧情很美好,现实很残酷...现实是旧案刚结,新案立至,而且新案与旧案有关联,被迫继续拍剧,并对该 ...

  8. Java蓝桥杯——排列组合

    排列组合介绍 排列,就是指从给定n个数的元素中取出指定m个数的元素,进行排序. 组合,则是指从给定n个数的元素中仅仅取出指定m个数的元素,不考虑排序. 全排列(permutation) 以数字为例,全 ...

  9. Java基础教程——解析注解

    解析注解 Java 5开始,java.lang.reflect包下的反射API可以在运行时读取Annotation. 应用:定义一个自动执行方法的注解,解析注解.通过反射执行方法,替代配置文件. pa ...

  10. 关于深度学习之中Batch Size的一点理解(待更新)

    batch 概念:训练时候一批一批的进行正向推导和反向传播.一批计算一次loss mini batch:不去计算这个batch下所有的iter,仅计算一部分iter的loss平均值代替所有的. 以下来 ...