websocket簡單實現五子棋即時對戰功能


幾年前做的一個小demo,代碼比較老,先上下html顯示效果圖

因為代碼中注釋比較詳細,所以就直接上代碼了

html代碼,也就是上圖展示的效果頁面

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript" src="../js/jquery-1.12.3.min.js" ></script>
        <script type="text/javascript" src="../js/json2.js" ></script>
        <script type="text/javascript" src="../js/rule.js" ></script>
        <style type="text/css">
            #background{
                position: relative;
                margin: 20px auto;
                /*background: #EAC000;*/
            }
            .chessman{
                border-radius: 15px;
                width: 30px;
                height: 30px;
                margin: 0 auto;
            }
            .white{
                background-color:white ;
            }
            .black{
                background-color: black;
            }
            .grid,.b_grid{
                float: left;
            }
            .message{
                width: 320px;
                height: 100px;
                margin: 0 auto;
            }
            #messageContent{
                width: 320px;
                height: 80px;
            }
        </style>
    </head>
    <body>
        <div id="background">
            <!--<div id="back_grid"></div>-->
            <div id="chess"></div>
        </div>
        <div class="message">
            <textarea id="messageContent" disabled="disabled" readonly="readonly"></textarea>
            <div><input id="message"/> <button onclick="WuZiQi.sendMessage()">發送</button></div>
        </div>
    </body>
</html>
chess.html

 

接下來是頁面rule.js功能代碼

 

var bout = false;//是否允許落子
var color = "";//自己落子顏色
var websocket = null;
var row = 15;
var col = 15;
var widthAndHeight = 30;//格子寬度高度
var WuZiQi = {
    isEnd:function(xy,chessmanColor){//判斷是否結束游戲
        var id = parseInt(xy);
        //豎的計算
        var num = 1;
        num = WuZiQi.shujia(num,id,chessmanColor);
        num = WuZiQi.shujian(num,id,chessmanColor);
        if(num>=5){
            if(chessmanColor==color){
                confirm("游戲結束!你贏了!");
            }else{
                confirm("游戲結束!你輸了!");
            }
            return ;
        }
        num = 1;
        num = WuZiQi.hengjia(num,id,chessmanColor);
        num = WuZiQi.hengjian(num,id,chessmanColor);
        if(num>=5){
            if(chessmanColor==color){
                confirm("游戲結束!你贏了!");
            }else{
                confirm("游戲結束!你輸了!");
            }
            return ;
        }
;        num = 1;
        num = WuZiQi.zuoxiejia(num,id,chessmanColor);
        num = WuZiQi.zuoxiejian(num,id,chessmanColor);
        if(num>=5){
            if(chessmanColor==color){
                confirm("游戲結束!你贏了!");
            }else{
                confirm("游戲結束!你輸了!");
            }
            return ;
        }
        num = 1;
        num = WuZiQi.youxiejia(num,id,chessmanColor);
        num = WuZiQi.youxiejian(num,id,chessmanColor);
        if(num>=5){
            if(chessmanColor==color){
                confirm("游戲結束!你贏了!");
            }else{
                confirm("游戲結束!你輸了!");
            }
            return ;
        }
    },youxiejia:function(num,id,color){
        var yu = id%row;
        id = id+(row-1);
        if(id<(row*col)&&(id%row)<yu){
            var flag = WuZiQi.checkColor(id,color);
            if(flag){
                num++;
                return WuZiQi.youxiejia(num,id,color);
            }else{
                return num;
            }
        }else{
            return num;
        }
    },youxiejian:function(num,id,color){
        var yu = id%row;
        id = id-(row-1);
        if(id>=0&&(id%row)>yu){
            var flag = WuZiQi.checkColor(id,color);
            if(flag){
                num++;
                return WuZiQi.youxiejian(num,id,color);
            }else{
                return num;
            }
        }else{
            return num;
        }
    },zuoxiejia:function(num,id,color){
        var yu = id%row;
        id = id+(row+1);
        if(id<(row*col)&&(id%row)>yu){
            var flag = WuZiQi.checkColor(id,color);
            if(flag){
                num++;
                return WuZiQi.zuoxiejia(num,id,color);
            }else{
                return num;
            }
        }else{
            return num;
        }
    },zuoxiejian:function(num,id,color){
        var yu = id%row;
        id = id-(row+1);
        if(id>=0&&(id%row)<yu){
            var flag = WuZiQi.checkColor(id,color);
            if(flag){
                num++;
                return WuZiQi.zuoxiejian(num,id,color);
            }else{
                return num;
            }
        }else{
            return num;
        }
    },
    hengjia:function(num,id,color){
        var yu = id%row;
        id = id+1;
        if(id<(row*col)&&(id%row)>yu){
            var flag = WuZiQi.checkColor(id,color);
            if(flag){
                num++;
                return WuZiQi.hengjia(num,id,color);
            }else{
                return num;
            }
        }else{
            return num;
        }
        
    },
    hengjian:function(num,id,color){
        var yu = id%row;
        id = id-1;
        if(id>=0&(id%row)<yu){
            var flag = WuZiQi.checkColor(id,color);
            if(flag){
                num++;
                return WuZiQi.hengjian(num,id,color);
            }else{
                return num;
            }
        }else{
            return num;
        }
    },
    shujia:function(num,id,color){
        id = id+row;
        if(id<(row*col)){
            var flag = WuZiQi.checkColor(id,color);
            if(flag){
                num++;
                return WuZiQi.shujia(num,id,color);
            }else{
                return num;
            }
        }else{
            return num;
        }
    },
    shujian:function(num,id,color){
        id = id-row;
        if(id>=0){
            var flag = WuZiQi.checkColor(id,color);
            if(flag){
                num++;
                return WuZiQi.shujian(num,id,color);
            }else{
                return num;
            }
        }else{
            return num;
        }
    },
    checkColor:function(xy,color){
        if($("#"+xy).children("div").hasClass(color)){
            return true;
        }else {
            return false;
        }
    },
    playchess:function(e){
        if(bout&&color!=""){
            if($(e).children("div").length>0){
                alert("這里已經有子了!請在其它地方落子!");
                return;
            }
            var result = {};
            result.xy = $(e).attr("id");
            result.color = color;
            result.message = "系統:您已落子,請等待對手落子!";
            result.bout = false;
            if(websocket!=null){
                websocket.send(JSON.stringify(result));
            }else{
                $("#messageContent").append("系統:已斷開連接");
                $("#messageContent").append("\n");
            }
        }else{
            if(color==""){
                $("#messageContent").append("系統:游戲還沒有開始!");
                $("#messageContent").append("\n");
                $("#messageContent").scrollTop($("#messageContent")[0].scrollHeight - $("#messageContent").height());
            }else{
                $("#messageContent").append("系統:請等待你的對手落子!");
                $("#messageContent").append("\n");
                $("#messageContent").scrollTop($("#messageContent")[0].scrollHeight - $("#messageContent").height());
            }
        }
        
    },
    //發送消息
    sendMessage:function(){
          var message = $("#message").val();
          if(message!=""){
              var result = {};
              result.message = message;
              websocket.send(JSON.stringify(result));
              $("#message").val("");
          }else{
              $("#messageContent").append("系統:請不要發送空信息!");
            $("#messageContent").append("\n");
            $("#messageContent").scrollTop($("#messageContent")[0].scrollHeight - $("#messageContent").height());
          }
          
      }
};
$(function(){
    //根據棋盤格子數得到棋盤大小
    $("#background").css({width:(row*widthAndHeight)+"px",height:(col*widthAndHeight)+"px"});
    //用canvas畫棋盤
    var canvas = document.createElement("canvas");
//    $(canvas).attr({width:((row-1)*widthAndHeight)+"px",height:(col-1)*widthAndHeight+"px"});
//    $(canvas).css({"position":"relative","top":(widthAndHeight/2)+"px","left":(widthAndHeight/2)+"px","z-index":9999});
    $(canvas).attr({width:(row*widthAndHeight)+"px",height:col*widthAndHeight+"px"});
    $(canvas).css({position:"relative","z-index":9999});
    var cot = canvas.getContext("2d");
    cot.fillStyle = "#EAC000";
    cot.fillRect(0,0,row*widthAndHeight,col*widthAndHeight);
    cot.lineWidth = 1;
    var offset = widthAndHeight/2;
    for(var i=0;i<row;i++){//面板大小和棋盤一致,但格子線條比棋盤的行列少1
        cot.moveTo((widthAndHeight*i)+offset,0+offset);
        cot.lineTo((widthAndHeight*i)+offset,(col*widthAndHeight)-offset);
    }
    for(var j=0;j<col;j++){
        cot.moveTo(0+offset,(widthAndHeight*j)+offset);
        cot.lineTo((widthAndHeight*row)-offset,(j*widthAndHeight)+offset);
    }    
    cot.stroke();
    $("#background").prepend(canvas);
    //生成格子橫線
//    var b_str="";
//    for(var i=0;i<(row-1);i++){
//        for(var j=0;j<(col-1);j++){
//            b_str+="<div class='b_grid'></div>";
//        }
//    }
//    $("#back_grid").append(b_str);
//    $("#back_grid").css({width:((row-1)*widthAndHeight)+"px",height:(col-1)*widthAndHeight+"px",position: "absolute",top:(widthAndHeight/2)+"px",left:(widthAndHeight/2)+"px",border:"solid 1px black"});
//    //減去線的寬度
//    $(".b_grid").css({width:(widthAndHeight-2)+"px",height:(widthAndHeight-2)+"px",border:"solid 1px black"});
    //生成落子格子
    var str="";
    var index = 0;
    for(var i=0;i<row;i++){
        for(var j=0;j<col;j++){
            str+="<div class='grid' id=\""+index+"\"></div>";
            index++;
        }
    }
    $("#chess").empty();
    $("#chess").append(str);
    $("#chess").css({width:(row*widthAndHeight)+"px",height:(col*widthAndHeight)+"px",position: "absolute",top:"0px",left:"0px","z-index":99999});
    $(".grid").on("click",function(){
        WuZiQi.playchess(this);
    });
    $(".grid").css({width:widthAndHeight+"px",height:widthAndHeight+"px"});
    
    
      //判斷當前瀏覽器是否支持WebSocket
      if('WebSocket' in window){
          websocket = new WebSocket("ws://"+window.location.host+"/WuZiQi/wuziqisocket");
      }
      else{
          alert('Not support websocket');
      }
       
      //連接發生錯誤的回調方法
      websocket.onerror = function(){
          
      };
       
      //連接成功建立的回調方法
      websocket.onopen = function(event){
          
      };
       
      //接收到消息的回調方法(包含了聊天,落子,開始游戲)
      websocket.onmessage = function(){
          var result = JSON.parse(event.data);
        if(result.message!=""){
            $("#messageContent").append(result.message);
            $("#messageContent").append("\n");
            //將多行文本滾動總是在最下方
            $("#messageContent").scrollTop($("#messageContent")[0].scrollHeight - $("#messageContent").height());
        }
        if(result.xy!=""&&result.color!=""){
            $("#"+result.xy).html("<div class=\"chessman "+result.color+"\"></div>");
            bout = result.bout;//落子后才改狀態
            WuZiQi.isEnd(result.xy,result.color);
        }else if(result.xy==""&&result.bout){//沒有坐標且bout為true,則為對局首次開始落子
            bout = result.bout;
        }
            
        if(result.xy==""&&result.color!=""){//沒有坐標,但有顏色,則為首次賦予棋子顏色
            color = result.color;
        }
      };
       
      //連接關閉的回調方法
      websocket.onclose = function(){

      };
       
      //監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
      window.onbeforeunload = function(){
          websocket.close();
      };
       
       
      //關閉連接
      function closeWebSocket(){
          websocket.close();
      }
});
rule.js

java封裝用來傳輸信息的對象代碼

 

package com.weguard.websocket;

/**
 * 
 * @author xuhan
 *
 */
public class Result {
    /**
     * 落子坐標
     */
    private String xy;
    /**
     * 發送消息
     */
    private String message;
    /**
     * 是否允許落子
     */
    private boolean bout;
    /**
     * 落子顏色
     */
    private String color;
    public String getXy() {
        return xy;
    }
    public void setXy(String xy) {
        this.xy = xy;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public boolean isBout() {
        return bout;
    }
    public void setBout(boolean bout) {
        this.bout = bout;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    
    
}
Result.java

 

最后是WebSocket的代碼

package com.weguard.websocket;

import java.io.IOException;
import java.util.HashMap;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import net.sf.json.JSONObject;

//該注解用來指定一個URI,客戶端可以通過這個URI來連接到WebSocket。類似Servlet的注解mapping。無需在web.xml中配置。
@ServerEndpoint(value="/wuziqisocket") 
public class WebSocket {
  //concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。若要實現服務端與單一客戶端通信的話,可以使用Map來存放,其中Key可以為用戶標識
  private static HashMap<String,WebSocket> webSocketMap = new HashMap<String,WebSocket>();
  //與某個客戶端的連接會話,需要通過它來給客戶端發送數據
  private Session session;
  //連上來的頁面序號,用來配對對戰,1與2一組,3與4一組,依次類推,奇數為黑先走,偶數為白,后走
  private static int index = 0;
  //同上,用來從hashMap中獲取websocket,(我也忘記當時為啥要另外用一個mykey了,而不是直接用index來獲取)
  private int mykey = 0;
   
  /**
   * 連接建立成功調用的方法
   * @param session  可選的參數。session為與某個客戶端的連接會話,需要通過它來給客戶端發送數據
 * @throws IOException 
   */
  @OnOpen
  public void onOpen( Session session){
      this.session = session;
      index++;
      try {
          Result result = new Result();
          if(index%2==0){
              WebSocket socket1 = webSocketMap.get((index-1)+"");
              if(socket1!=null){
                  result.setBout(true);
                  result.setMessage("系統:游戲開始,請您先落子!");
                  result.setColor("black");
                  JSONObject json1 = JSONObject.fromObject(result);
                  socket1.sendMessage(json1.toString());
                  //對先落子的對象發送數據結束
                  result.setMessage("系統:游戲開始,請等待對手落子!");
                  result.setBout(false);
                  result.setColor("white");
                  this.sendMessage(JSONObject.fromObject(result).toString());
                  //對后出手的發送消息結束
              }else{//偶數時沒有查詢到與之對應的對手,則其變為奇數,成為等待匹配的人
                  index--;
                  result.setMessage("系統:等待玩家匹配!");
                  this.sendMessage(JSONObject.fromObject(result).toString());
              }
          }else{
              result.setMessage("系統:等待玩家匹配!");
              this.sendMessage(JSONObject.fromObject(result).toString());
          }
          this.mykey = index;
          webSocketMap.put(mykey+"", this);     //加入map中
          System.out.println(webSocketMap.size());
    } catch (Exception e) {
        e.printStackTrace();
    }
  }
   
  /**
   * 連接關閉調用的方法
 * @throws IOException 
   */
  @OnClose
  public void onClose(){
      webSocketMap.remove(mykey+"");  //從set中刪除
      try {
          WebSocket socket = null;
          if(mykey%2==0){
              socket = webSocketMap.get((mykey-1)+"");
          }else{
              socket = webSocketMap.get((mykey+1)+"");
          }
          if(socket!=null){
              Result result = new Result();
              result.setMessage("你的對手已離開!");
              socket.sendMessage(JSONObject.fromObject(result).toString());
          }
    } catch (Exception e) {
        e.printStackTrace();
    }
  }
  /**
   * 收到客戶端消息后調用的方法
   * @param message 客戶端發送過來的消息
   * @param session 可選的參數
   */
  @OnMessage
  public void onMessage(String message) {
      System.out.println(message);
      JSONObject json = JSONObject.fromObject(message);
      Result result = (Result) JSONObject.toBean(json,Result.class);
      try {
          WebSocket socket = null;
          if(mykey%2==0){
              socket = webSocketMap.get((mykey-1)+"");
          }else{
              socket = webSocketMap.get((mykey+1)+"");
          }
          if(socket!=null){
              if(result.getXy()!=null&&!"".equals(result.getXy())){//有坐標表示為落子,反之則為發送信息
                  this.sendMessage(message);
                  result.setBout(true);//對手的bout改為true,表示接下來可以落子
                  result.setMessage("系統:對方已落子,正在等待您落子!");
                  socket.sendMessage(JSONObject.fromObject(result).toString());
              }else{//沒有坐標表示為單純的聊天
                  Result newResult = new Result();
                  newResult.setMessage("自己:"+result.getMessage());
                  this.sendMessage(JSONObject.fromObject(newResult).toString());
                  newResult.setMessage("對方:"+result.getMessage());
                  socket.sendMessage(JSONObject.fromObject(newResult).toString());
              }
          }
          
          
    } catch (Exception e) {
        e.printStackTrace();
    }
  }
   
  /**
   * 發生錯誤時調用
   * @param session
   * @param error
   */
  @OnError
  public void onError(Session session, Throwable error){
      System.out.println("連接斷開");
//      error.printStackTrace();
  }
  /**
   * 這個方法與上面幾個方法不一樣。沒有用注解,是根據自己需要添加的方法。
   * @param message
   * @throws IOException
   */
  public void sendMessage(String message) throws IOException{
      this.session.getBasicRemote().sendText(message);
      //this.session.getAsyncRemote().sendText(message);
  }
}
WebSocket.java

只需要上面的4個代碼,當然jquery,json2這些依然還是需要的,同學們新建一個


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM