Websocket深入理解以及簡單操作(網頁版QQ聊天實現)

一,Websocket是一個基於TCP、對傳統HTTP協議(短連接)的升級版,它實現了瀏覽器與服務器的全雙工通信,擴展了瀏覽器與服務端的通信功能,使服務端也能主動向客戶端發送數據。
解決的問題
1.解決了多次握手的問題(長連接),提高效率
2.服務器可以推送數據給客戶端,不需要客戶端輪詢等low操作
客戶端實現方式
JavaScript對WebSocket的支持:
2.1.創建客戶端連接的方式:
websocket = new WebSocket(“ws://localhost:9090/websocket”);
2.2.websocket對象常用事件:
onerror: 連接到服務端錯誤時觸發
onmessage: 收到服務器推送的消息時觸發
onclose: 連接關閉時觸發
onopen: 連接到服務端成功後觸發
二.WebSocket示例(實現簡單的信息交互)
2.1.新建WebSocket示例(網頁版QQ聊天實現)
在這裏插入圖片描述
在pom.xml中添加Jar包依賴

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>${spring.version}</version>
</dependency>

2.2.spring整合websocket方法

spring整合,此方式基於spring mvc框架

WebSocketConfig.java

這個類是配置類,所以需要在spring mvc配置文件中加入對這個類的掃描,第一個addHandler是對正常連接的配置,第二個是如果瀏覽器不支持websocket,使用socketjs模擬websocket的連接。

package com.ssm.websocket;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
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 org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
/*
@Configuration的作用
聲明當前類是一個配置類
@Configuration不可以是final類型;
@Configuration不可以是匿名類;
嵌套的configuration必須是靜態類。
@EnableWebSocket  聲明該類支持WebSocket
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    //websocket入口,允許訪問的域、註冊Handler、SockJs支持和攔截器。
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {

        registry.addHandler(myHandler(),"/websocket")//入口路徑爲websocket
                .addInterceptors(new SpringWebSocketHandler())
                .setAllowedOrigins("*");
        registry.addHandler(myHandler(), "/wbsockjs/webSocketServer")
                .addInterceptors(new SpringWebSocketHandler()).withSockJS();
        //setAllowedOrigins()方法的支持spring4.1.5之後的版本才支持
        //setAllowedOrigins(String... val),允許指定的域名或IP(含端口號)建立長連接,
        // 可以只設置允許自家域名訪問,如果不限時使用"*"號,如果指定了域名,
        // 則必須要以http或https開頭。
    }

    @Bean
    public WebSocketHandler myHandler(){
        return new MySocketHandler();
    }

    @Bean
    public HttpSessionHandshakeInterceptor myHandlerInterceptor(){
        return new SpringWebSocketHandler();
    }

}

SpringWebSocketHandler.java

這個類的作用就是在連接成功前和成功後增加一些額外的功能,Constants.java類是一個工具類,兩個常量。

package com.ssm.websocket;

import com.lingdian.pojo.User;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.*;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

import javax.servlet.http.HttpSession;
import java.util.Map;
/*
握手攔截器類
 */
public class SpringWebSocketHandler extends HttpSessionHandshakeInterceptor {

    @Override
    public boolean beforeHandshake(ServerHttpRequest request,
                                   ServerHttpResponse response,
                                   WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        System.out.println("Before Handshake");
        //在握手之前將HttpSession中的用戶,copy放到WebSocket Session中
        if (request instanceof ServletServerHttpRequest){
            ServletServerHttpRequest servletServerHttpRequest=
                    (ServletServerHttpRequest) request;
            HttpSession session=
                    servletServerHttpRequest.getServletRequest().getSession(true);
            if (null!=session){
               User user=(User)session.getAttribute("user");
               //WebSocket Session
               attributes.put("user",user);
            }
        }
        return super.beforeHandshake(request,response,wsHandler,attributes);
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response
            , WebSocketHandler wsHandler, Exception ex) {
        super.afterHandshake(request, response, wsHandler, ex);
    }


}

MySocketHandler .java

這個類是對消息的一些處理,比如是發給一個人,還是發給所有人,並且前端連接時觸發的一些動作

package com.ssm.websocket;

import com.lingdian.pojo.Message;
import com.lingdian.pojo.User;
import com.lingdian.service.MessageService;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CopyOnWriteArraySet;
/***
 * 消息處理類
 */
public class MySocketHandler extends TextWebSocketHandler {

    //使用CopyOnWriteArraySet,保證線程安全,當一個用戶
    // 退出時,這邊的用戶查看用戶列表時不會出現安全失敗
    private static CopyOnWriteArraySet<WebSocketSession> users=new CopyOnWriteArraySet<WebSocketSession>();;


    @Resource
    private MessageService messageService;

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //super.handleTextMessage(session, message);
        SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = sd.format(new Date());
        //從 WebSocket Session中取得用戶
        User user=(User) session.getAttributes().get("user");
        Message message1=new Message();
        message1.setUid(user.getId());
        message1.setMessage(message.getPayload());
        message1.setSendTime(new Date());
        //將消息保存到數據庫
        messageService.insertMessage(message1);
        //封裝要輸出的消息到TextMessage,姓名,時間,消息
        TextMessage returnMessage =
                new TextMessage(user.getName()+":" +
                        ""+time+"<br/>"+message.getPayload());
        for (WebSocketSession session1 : users) {
            try{
                //使用sendMessage()方法輸出消息到客戶端
                session1.sendMessage(returnMessage);
            }catch (Exception e){
                e.printStackTrace();
                continue;
            }

        }
    }

    /**
     * 加入一個用戶 add進集合
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        users.add(session);
        System.out.println("connect to the websocket success......當前數量:"+users.size());

    }

    /**
     * 當用戶退出時,將用戶從集合remove
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        users.remove(session);
        System.out.println("剩餘在線用戶"+users.size());
    }

    public static CopyOnWriteArraySet<WebSocketSession> getUsers() {
        return users;
    }

    public static void setUsers(CopyOnWriteArraySet<WebSocketSession> users) {
        MySocketHandler.users = users;
    }

    public MessageService getMessageService() {
        return messageService;
    }

    public void setMessageService(MessageService messageService) {
        this.messageService = messageService;
    }
}

spring-mvc.xml

正常的配置文件,同時需要增加對WebSocketConfig.java類的掃描,並且增加

<!--配置握手攔截器-->
<websocket:handlers>
    <!--path=websocket-->
    <websocket:mapping path="/websocket" handler="websocket"/>
    <websocket:handshake-interceptors>
        <bean class="com.ssm.websocket.SpringWebSocketHandler"/>
    </websocket:handshake-interceptors>
</websocket:handlers>

jsp簡單聊天界面

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<html>
<head>
    <title>聊天室</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/Css/chat.css" />
    <script src="${pageContext.request.contextPath}/Js/jquery-1.8.2.min.js"></script>
    <script src="${pageContext.request.contextPath}/Js/chat.js"></script>    
   <script type="text/javascript">
       //獲取當前用戶
       function getUser(){
       $("#chat-user-con ul").html("");
          $.post("${pageContext.request.contextPath}/user/getAll",{},
             function(data){
             var temp;
             for(temp=0;temp<data.length;temp++){
                $("#chat-user-con ul").append("<li>"+data[temp].name+"</li>");
             }
          },"json");
       }
    
       //下線
       function downLine(){
          $.post("${pageContext.request.contextPath}/user/downLine",{},
          function(){});
       }
   </script>
</head>
<body>
   <span id="message"></span>
    <div id="chat">
        <div id="chat-top">
            <div id="chat-dialog">
                <div id="chat-dialog-t">聊天室</div>
                <div id="chat-dialog-con">
                    <ul>

                    </ul>
                </div>
            </div>
            <div id="chat-user">
                <div id="chat-user-t">當前在線用戶</div>
                <div id="chat-user-con">
                    <ul>

                    </ul>
                </div>
            </div>
        </div>
        <div id="chat-bottom">
            <div id="chat-input">
                <div id="chat-input-expr">
                    <!--<img src="Images/1.gif" id="1" /><img src="Images/2.gif" id="2" /><img src="Images/3.gif" id="3" /><img src="Images/4.gif" id="4" /><img src="Images/5.gif" id="5" /><img src="Images/6.gif" id="6" /><img src="Images/7.gif" id="7" /><img src="Images/8.gif" id="8" /><img src="Images/9.gif" id="9" /><img src="Images/10.gif" id="10" />-->
                </div>
                <div id="chat-input-edit">
                    <div id="input-field">
                        <textarea id="txtInput"></textarea>
                    </div>
                    <div id="input-btn">
                        <input id="btnSend" type="button" value="發送" />
                    </div>
                </div>
                <div id="chat-input-tip">發送內容不能爲空</div>
            </div>
        </div>
    </div>
    <div id="chat-msg"></div>
</body>
</html>

List item

注意導入wbsockjs時要使用地址全稱,並且連接使用的是http而不是websocket的ws

聲明: 整理本文僅供參考,若有疑問可以同時參考原文。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章