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

声明: 整理本文仅供参考,若有疑问可以同时参考原文。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值