1. 程式人生 > >Spring-WebSocket實現

Spring-WebSocket實現

之前剛寫了java 本身的websocket,再使用spring中的websocket,理解起來就比較容易了;
spring 中多出了 WebSocket握手請求的攔截器, 繼承 Interface HandshakeInterceptor;
具體的過程跟 java 沒什麼區別;
按照執行順序貼出程式碼:
index.jsp:

<body>
<form action="msg/login" method="post">
    使用者名稱:
    <select name="id">
        <option value="1"
>張三</option> <option value="2">李四</option> <option value="3">王五</option> </select><br> 密碼: <input name="password" type="text" value="123456"> <input type="submit" value="登入"> </form> </body>

ChatController:

package com.controller;


import com.entity.User;
import com.handler.MyWebSocketHandler;
import com.sun.org.glassfish.gmbal.ParameterNames;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation
.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; import javax.jws.WebParam; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; @Controller @RequestMapping(value = "/msg" ) public class ChatController { @Autowired private MyWebSocketHandler handler; Map<Long, User> users = new HashMap<Long, User>(); // 模擬一些資料 @ModelAttribute public void setReqAndRes() { User u1 = new User(); u1.setId(1l); u1.setName("張三"); users.put(u1.getId(), u1); User u2 = new User(); u2.setId(2L); u2.setName("李四"); users.put(u2.getId(), u2); User u3 = new User(); u3.setId(3L); u3.setName("王五"); users.put(u3.getId(), u3); } @RequestMapping(value = "/login",method = RequestMethod.POST) public ModelAndView login(User user, HttpServletRequest request){ request.getSession().setAttribute("uid",user.getId()); request.getSession().setAttribute("name",users.get(user.getId()).getName()); return new ModelAndView("redirect:talk"); } @RequestMapping(value = "/talk",method = RequestMethod.GET) public ModelAndView talk(){ return new ModelAndView("talk"); } }

talk.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getServerName() + ":"
            + request.getServerPort() + path + "/";
    String basePath2 = request.getScheme() + "://"
            + request.getServerName() + ":" + request.getServerPort()
            + path + "/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<script type="text/javascript" src="<%=basePath2%>resources/jquery.js"></script>
<style>
textarea {
    height: 300px;
    width: 100%;
    resize: none;
    outline: none;
}

input[type=button] {
    float: right;
    margin: 5px;
    width: 50px;
    height: 35px;
    border: none;
    color: white;
    font-weight: bold;
    outline: none;
}

.clear {
    background: red;
}

.send {
    background: green;
}

.clear:active {
    background: yellow;
}

.send:active {
    background: yellow;
}

.msg {
    width: 100%;
    height: 25px;
    outline: none;
}

#content {
    border: 1px solid gray;
    width: 100%;
    height: 400px;
    overflow-y: scroll;
}

.from {
    background-color: green;
    width: 80%;
    border-radius: 10px;
    height: 30px;
    line-height: 30px;
    margin: 5px;
    float: left;
    color: white;
    padding: 5px;
    font-size: 22px;
}

.to {
    background-color: gray;
    width: 80%;
    border-radius: 10px;
    height: 30px;
    line-height: 30px;
    margin: 5px;
    float: right;
    color: white;
    padding: 5px;
    font-size: 22px;
}

.name {
    color: gray;
    font-size: 12px;
}

.tmsg_text {
    color: white;
    background-color: rgb(47, 47, 47);
    font-size: 18px;
    border-radius: 5px;
    padding: 2px;
}

.fmsg_text {
    color: white;
    background-color: rgb(66, 138, 140);
    font-size: 18px;
    border-radius: 5px;
    padding: 2px;
}

.sfmsg_text {
    color: white;
    background-color: rgb(148, 16, 16);
    font-size: 18px;
    border-radius: 5px;
    padding: 2px;
}

.tmsg {
    clear: both;
    float: right;
    width: 80%;
    text-align: right;
}

.fmsg {
    clear: both;
    float: left;
    width: 80%;
}
</style>
<script>
        var path = '<%=basePath%>';
        <%--var uid=${uid eq null?-1:uid};--%>
        var uid = ${uid};
        if(uid==-1){
            location.href="<%=basePath2%>";
        }
        var from=uid;
        var fromName='${name}';
        var to=uid==1?2:1;

        var websocket;
        if ('WebSocket' in window) {
            websocket = new WebSocket("ws://" + path + "/ws?uid="+uid);
        } else if ('MozWebSocket' in window) {
            websocket = new MozWebSocket("ws://" + path + "/ws"+uid);
        } else {
            websocket = new SockJS("http://" + path + "/ws/sockjs"+uid);
        }
        websocket.onopen = function(event) {
            console.log("WebSocket:已連線");
            console.log(event);
        };
        websocket.onmessage = function(event) {
            var data=JSON.parse(event.data);
            console.log("WebSocket:收到一條訊息",data);
            var textCss=data.from==-1?"sfmsg_text":"fmsg_text";
            $("#content").append("<div class='fmsg'><label class='name'>"+data.fromName+"&nbsp;"+data.date+"</label><div class='"+textCss+"'>"+data.text+"</div></div>");
            scrollToBottom();
        };
        websocket.onerror = function(event) {
            console.log("WebSocket:發生錯誤 ");
            console.log(event);
        };
        websocket.onclose = function(event) {
            console.log("WebSocket:已關閉");
            console.log(event);
        }
        function sendMsg(){
            var v=$("#msg").val();
            if(v==""){
                return;
            }else{
                var data={};
                data["from"]=from;
                data["fromName"]=fromName;
                data["to"]=to;
                data["text"]=v;
                websocket.send(JSON.stringify(data));
                $("#content").append("<div class='tmsg'><label class='name'>我&nbsp;"+new Date().Format("yyyy-MM-dd hh:mm:ss")+"</label><div class='tmsg_text'>"+data.text+"</div></div>");
                scrollToBottom();
                $("#msg").val("");
            }
        }

        function scrollToBottom(){
            var div = document.getElementById('content');
            div.scrollTop = div.scrollHeight;
        }

        Date.prototype.Format = function (fmt) { //author: meizz
            var o = {
                "M+": this.getMonth() + 1, //月份
                "d+": this.getDate(), //日
                "h+": this.getHours(), //小時
                "m+": this.getMinutes(), //分
                "s+": this.getSeconds(), //秒
                "q+": Math.floor((this.getMonth() + 3) / 3), //季度
                "S": this.getMilliseconds() //毫秒
            };
            if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
            for (var k in o)
            if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            return fmt;
        }

        function send(event){
            var code;
             if(window.event){
                 code = window.event.keyCode; // IE
             }else{
                 code = e.which; // Firefox
             }
            if(code==13){
                sendMsg();
            }
        }

        function clearAll(){
            $("#content").empty();
        }
</script>
</head>
<body>
    歡迎:${name}
    <div id="content"></div>
    <input type="text" placeholder="請輸入要傳送的資訊" id="msg" class="msg" onkeydown="send(event)">
    <input type="button" value="傳送" class="send" onclick="sendMsg()" >
    <input type="button" value="清空" class="clear" onclick="clearAll()">
</body>
</html>

WebSocketConfig :

package com.config;


import com.handler.MyWebSocketHandler;
import com.interceptor.HandShake;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

import javax.annotation.Resource;

@Component
@EnableWebSocket
public class WebSocketConfig  extends WebMvcConfigurerAdapter implements WebSocketConfigurer {

    @Autowired
    MyWebSocketHandler handler;

    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(handler, "/ws").addInterceptors(new HandShake());

        registry.addHandler(handler, "/ws/sockjs").addInterceptors(new HandShake()).withSockJS();
    }

}

HandShake :

package com.interceptor;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import javax.servlet.http.HttpSession;
import java.util.Map;

public class HandShake implements HandshakeInterceptor {
    public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
        System.out.println("Websocket:使用者[ID:" + ((ServletServerHttpRequest) serverHttpRequest).getServletRequest().getSession(false).getAttribute("uid") + "]已經建立連線");
        if (serverHttpRequest instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) serverHttpRequest;
            HttpSession session = servletRequest.getServletRequest().getSession(false);
            // 標記使用者
            Long uid = (Long) session.getAttribute("uid");
            if(uid!=null){
                map.put("uid", uid);
            }else{
                return false;
            }
        }
        return true;
    }

    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {

    }
}

MyWebSocketHandler :

package com.handler;


import com.entity.Message;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

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

@Component
public class MyWebSocketHandler implements WebSocketHandler {

    public static final Map<Long, WebSocketSession> userSocketSessionMap;
    static {
        userSocketSessionMap = new HashMap<Long, WebSocketSession>();
    }

    public void afterConnectionEstablished(WebSocketSession session)
            throws Exception {
        System.out.println("------->connection!!!!");
        Long uid = (Long) session.getAttributes().get("uid");
        if (userSocketSessionMap.get(uid) == null) {
            userSocketSessionMap.put(uid, session);
        }
    }

    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
        if(webSocketMessage.getPayloadLength()==0)return;
        Message msg = new Gson().fromJson(webSocketMessage.getPayload().toString(),Message.class);  //轉換格式
        msg.setDate(new Date());
        System.out.println(msg);
        sendMessageToUser(msg.getTo(),new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
    }

    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {

    }

    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {

    }

    public boolean supportsPartialMessages() {
        return false;
    }
    public void sendMessageToUser(Long uid,TextMessage textMessage) throws IOException{
        WebSocketSession session = userSocketSessionMap.get(uid);
        if(session!=null&&session.isOpen()){
            session.sendMessage(textMessage);
        }
    }
}

還有兩個實體類:
User:

package com.entity;

public class User {
    private Long id;

    private String name;

    private String password;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

Message:

package com.entity;

import java.util.Date;

public class Message {

    //傳送者
    public Long from;
    //傳送者名稱
    public String fromName;
    //接收者
    public Long to;
    //傳送的文字
    public String text;
    //傳送日期
    public Date date;

    public Long getFrom() {
        return from;
    }

    public void setFrom(Long from) {
        this.from = from;
    }

    public Long getTo() {
        return to;
    }

    public void setTo(Long to) {
        this.to = to;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getFromName() {
        return fromName;
    }

    public void setFromName(String fromName) {
        this.fromName = fromName;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "Message{" +
                "from=" + from +
                ", fromName='" + fromName + '\'' +
                ", to=" + to +
                ", text='" + text + '\'' +
                ", date=" + date +
                '}';
    }
}

附上一張專案分層的截圖:
這裡寫圖片描述
pom.xml:

<dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.3.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.3.1</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.3.3</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-messaging</artifactId>
      <version>4.0.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-websocket</artifactId>
      <version>4.0.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.0.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.3.1</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

其他的配置應該都沒問題了;

簡單描述一下整個過程,以後再看的時候能快速理解,

登入頁面 —> controller ,controller中執行login方法,在session中存了兩個值 uid 和 name
再跳轉到talk.jsp , 在talk.jsp中,獲取session中的值,通過
websocket = new WebSocket("ws://" + path + "/ws?uid="+uid); 來建立websocket連線,
這個路徑的配置在 WebSocketConfig 中
registry.addHandler(handler, "/ws").addInterceptors(new HandShake()); 註冊一個處理類的路徑,並且新增一個攔截器 new HandShake(); 攔截器中會根據判斷來斷開連線,或繼續連線;
連線成功後,進入 MyWebSocketHandler 中的 afterConnectionEstablished ,這樣一個連線就建立完成; 資料的傳送 在handleMessage方法中,前端接收onmessage ; 指定的傳送,在前端頁面已經寫死,
可以根據自己需求更改;