WebSocket – Tomcat 簡單的聊天室

Posted in :

在 Tomcat 裡實作 websocket 滿簡單的。

WebSocket可以在伺服器和瀏覽器之間建立雙向通訊,今天要來用WebSocket來實現一個簡單的聊天室做範例,Tomcat 伺服器的最小版本要求是 Tomcat 7, 目前我是使用 Tomcat 7.

附註1:如果你是走 ssl 需要把 ws:// 修改為 wss://
附註2:如果是 html 去存取別台伺服器的 wss, 如果是自簽的憑證會有問題。
附註3:不需要額外修改 tomcat 的 server.xml 或 web.xml

 


範例1:

==================
webSocketTest.html :

<!DOCTYPE html>
<html>
    <head>
        <title>WebSocket Test</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="js/webSocket.js"></script>
    </head>
    <body>
        <div>
            <form id="chatRoomForm" onsubmit="return false;">
                聊天室
                名字: <input type="text" id="userNameInput" /> 
 
                <input type="button" id="loginBtn" value="登入" /> <span id="infoWindow"></span>
 
                <input type="text" id="userinput" /> 
  
                <input type="submit" value="送出訊息" />
            </form>
        </div>
        <div id="messageDisplay"></div>
    </body>
</html>

webSocket.js :

window.onload = function () {
    //獲取DOM元件
    var loginBtn = document.getElementById("loginBtn");
    var userNameInput = document.getElementById("userNameInput");
    var infoWindow = document.getElementById("infoWindow");
    var userinput = document.getElementById("userinput");
    var chatRoomForm = document.getElementById("chatRoomForm");
    var messageDisplay = document.getElementById("messageDisplay");
 
    var webSocket;
    var isConnectSuccess = false;
 
    //設置登入鈕的動作,沒有登出,登入才可發言
    loginBtn.addEventListener("click", function () {
        //檢查有無輸入名稱
        if (userNameInput.value && userNameInput.value !== "") {
            setWebSocket();  //設置WebSocket連接
        } else {
            infoWindow.innerHTML = "請輸入名稱";
        }
 
    });
    //Submit Form時送出訊息
    chatRoomForm.addEventListener("submit", function () {
        sendMessage();
        return false;
    });
    //使用webSocket擁有的function, send(), 送出訊息
    function sendMessage() {
        //檢查WebSocket連接狀態
        if (webSocket && isConnectSuccess) {
            var messageInfo = {
                userName: userNameInput.value,
                message: userinput.value
            }
            webSocket.send(JSON.stringify(messageInfo));
        } else {
            infoWindow.innerHTML = "未登入";
        }
    }
 
    //設置WebSocket
    function setWebSocket() {
        //開始WebSocket連線
        webSocket = new WebSocket('ws://localhost:8080/WebSocketTest/websocket');
        //以下開始偵測WebSocket的各種事件
         
        //onerror , 連線錯誤時觸發  
        webSocket.onerror = function (event) {
            loginBtn.disabled = false;
            userNameInput.disabled = false;
            infoWindow.innerHTML = "登入失敗";
        };
 
        //onopen , 連線成功時觸發
        webSocket.onopen = function (event) {
            isConnectSuccess = true;
            loginBtn.disabled = true;
            userNameInput.disabled = true;
            infoWindow.innerHTML = "登入成功";
             
            //送一個登入聊天室的訊息
            var firstLoginInfo = {
                userName : "系統",
                message : userNameInput.value + " 登入了聊天室"
            };
            webSocket.send(JSON.stringify(firstLoginInfo));
        };
 
        //onmessage , 接收到來自Server的訊息時觸發
        webSocket.onmessage = function (event) {
            var messageObject = JSON.parse(event.data);
            messageDisplay.innerHTML += "
" + messageObject.userName + " 說 : " + messageObject.message;
        };
    }
};

WebSocketEndpointTest.java:

import java.io.IOException;
import java.util.ArrayList;
import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
 
@ServerEndpoint("/websocket")
public class WebSocketEndpointTest {
    //用來存放WebSocket已連接的Socket
    static ArrayList<Session> sessions;
 
    @OnMessage
    public void onMessage(String message, Session session) throws IOException,
            InterruptedException, EncodeException {
        System.out.println("User input: " + message);
        //session.getBasicRemote().sendText("Hello world Mr. " + message);
        //for (Session s : session.getOpenSessions()) {
        for (Session s : sessions) {    //對每個連接的Client傳送訊息
            if (s.isOpen()) {
                s.getBasicRemote().sendText(message);
            }
        }
    }
 
    @OnOpen
    public void onOpen(Session session) {
        //紀錄連接到sessions中
        System.out.println("Client connected");        
        if (sessions == null) {
            sessions = new ArrayList<Session>();
        }
        sessions.add(session);
        System.out.println("Current sessions size: " + sessions.size());
    }
 
    @OnClose
    public void onClose(Session session) {
        //將連接從sessions中移除
        System.out.println("Connection closed");
        if (sessions == null) {
            sessions = new ArrayList<Session>();
        }
        sessions.remove(session);
        System.out.println("Current sessions size: " + sessions.size());
    }
}

範例 2號,我是用這組來修改。

 

我们需要2个jar包:

  • tomcat7-websocket.jar
  • websocket-api.jar

這两个jar包是在tomcat7及以上版本的lib目录下获得的。主要是封装了websocket的API方法类以及实现了其中的方法。

当跳转到chat.jsp页面的时候,会加载其中的js文件,js中会开启一个通信管道。页面加载判断是否已经开启了这个通道,如果没有开启,就开启。当管道开启的时候就会触发onopen事件。触发的方法在ChatSocket类中。

chat.jsp:

<body>
  <script src="../js/jquery-3.2.1.min.js"></script>
  <script src="../js/popper.min.js"></script>
  <script src="../js/bootstrap.min.js"></script>
<script>
        var ws; //一个ws对象就是一个通信管道!!,只要不关浏览器,不关闭服务器就一直开着
        var target="wss://192.168.1.215:8443/Core/WsPushServer?username=max32002";
        $().ready(function connect() {

                 //页面加载判断是否已经开启了target这个通道,如果没有开启,就开启
                 if ('WebSocket' in window) {
                     ws = new WebSocket(target);
                 } else if ('MozWebSocket' in window) {
                     ws = new MozWebSocket(target);
                 } else {
                     alert('WebSocket is not supported by this browser.');
                     return;
                 }


                //onerror , 連線錯誤時觸發  
                ws.onerror = function (event) {
                    alert("登入失敗");
                };

                 //接收消息 
                 ws.onmessage = function (event) {
                    eval("var result="+event.data);

                    if(result.alert!=undefined){
                        $("#content").append(result.alert+"<br/>");
                    }

                    if(result.names!=undefined){
                        $("#userList").html("");
                        $(result.names).each(function(){
                            $("#userList").append(this+"<br/>");
                        });
                    }

                    if(result.from!=undefined){
                        $("#content").append(result.from+" "+result.date+
                                " 说:<br/>"+result.sendMsg+"<br/>");
                    }

                 };
             });


        //点击发送消息触发事件
        function send(){
            var msg = $("#msg").val();
            ws.send(msg);
            $("#msg").val("");
        }
    </script>


    <h3>欢迎 max32002 使用本系统!!</h3>

    <div id="content"
        style="
        border: 1px solid black; width: 400px; height: 300px;
        float: left;
    "></div>
    <div id="userList"
        style="
        border: 1px solid black; width: 100px; height: 300px;
        float:left;
    "></div>

    <div style="clear: both;">
        <input id="msg" />
        <button onclick="send();">send</button>
    </div>

</body>

Java:

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

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 com.google.gson.Gson;

@ServerEndpoint("/WsPushServer")
public class WsPushServer {
    private  static  Set<WsPushServer>  sockets=new HashSet<WsPushServer>();
    private  static  List<String>   nameList=new ArrayList<String>();
    private  Session  session;
    private String username;
    private Gson  gson=new Gson();
    
    //只要有人连接这个服务,就会打开,执行下面的方法。
    @OnOpen
    public void open(Session session){
        //一个session就代表一个通信会话
        System.out.println("sessionid:"+session.getId()+"通道开启了。。。。");
        //把session添加到容器中
        this.session=session;
        sockets.add(this);

        //getQueryString把url中?后面的所有的串儿都取出来
        String QueryString = session.getQueryString();
        System.out.println(QueryString);
        
        //获取用户名
        this.username = QueryString.substring(QueryString.indexOf("=")+1);
        nameList.add(username);

        String message = (username+"进入聊天室!!");

        broadcast(sockets, gson.toJson(message) );
    }
    
    //退出
    @OnClose
    public void close(Session session){
        //1.清理退出的session
        sockets.remove(this);
        //2.清理列表用户名
        nameList.remove(this.username);
        //3.更新消息信息
        String message=(this.username+"退出聊天室!!");
        //4.广播消息信息
        broadcast(sockets, gson.toJson(message));
    }
    
    @OnError
    public void onError(Throwable e){
            e.printStackTrace();
    }

    //收
    @OnMessage
    /** * * @param session * @param msg 从客户端接收的消息 */
    public void message(Session session,String msg){
        //接收消息

        String  message=(this.username + ":" + msg + ":" + new Date().toString());
        broadcast(sockets, gson.toJson(message));

    }
    
    /** * 广播消息 * @param ss 用户session * @param msg 广播消息 */
    public void broadcast(Set<WsPushServer>  ss ,String msg ){

        for (Iterator<WsPushServer> iterator = ss.iterator(); iterator.hasNext();) {
                WsPushServer chatSocket = (WsPushServer) iterator.next();
            try {
                chatSocket.session.getBasicRemote().sendText(msg);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 

相關文章:

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *