zoukankan      html  css  js  c++  java
  • SpringBoot基于websocket的网页聊天

    一、入门简介
    正常聊天程序需要使用消息组件ActiveMQ或者Kafka等,这里是一个Websocket入门程序。

    有人有疑问这个技术有什么作用,为什么要有它?
    其实我们虽然有http协议,但是它有一个缺陷就是不能主动向客户端发送消息,而我们的基于Tcp协议的Websocket能够做到,所以这在多台机器之间通信提供了大大的方便。


    二、入门案例
    本案例使用Springboot+WebSocket+Thymeleaf

    1.1pom.xml
    <!-- websocket -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    <!-- StringUtils -->
    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    </dependency>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    1.2WebSocketConfig
    package cn.xdl.config;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;

    /**
    * 开启WebSocket支持
    * @author liurui
    *
    */
    @Configuration
    public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
    return new ServerEndpointExporter();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    1.3CheckCenterController连接测试
    package cn.xdl.controller;

    import java.io.IOException;

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.servlet.ModelAndView;

    import cn.xdl.utils.result.AllResult;

    /**
    * 调用WebSocketServer
    * @author liurui
    *
    */
    @Controller
    @RequestMapping("/checkcenter")
    public class CheckCenterController {

    //页面请求
    @GetMapping("/socket/{cid}")
    public ModelAndView socket(@PathVariable String cid) {
    ModelAndView mav=new ModelAndView("/socket");
    mav.addObject("cid", cid);
    return mav;
    }
    //推送数据接口
    @ResponseBody
    @RequestMapping("/socket/push/{cid}")
    public AllResult pushToWeb(@PathVariable String cid,String message) {
    try {
    WebSocketServer.sendInfo(message,cid);
    } catch (IOException e) {
    e.printStackTrace();
    return AllResult.build(500,cid+"#"+e.getMessage());
    }
    return AllResult.ok(cid);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    1.4测试socket.html
    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title>websocket</title>
    <script type="text/javascript" src="http://nim.prec-robot.com/jzrobot/liurui/js/jquery.js"></script>
    </head>
    <body>
    <h3>websocket测试页面</h3>
    <input type="hidden" name="cid" th:value="${cid}" id="cid">
    <input type="button" value="测试" onclick="push()">
    <script>
    var cid = $("#cid").val();
    console.info("cid===="+cid);
    var socket;
    if(typeof(WebSocket) == "undefined") {
    console.log("您的浏览器不支持WebSocket");
    }else{
    console.log("您的浏览器支持WebSocket");
    //实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接
    //等同于socket = new WebSocket("ws://localhost:8083/checkcentersys/websocket/20");
    socket = new WebSocket(("http://localhost:8888/hello-boot/websocket/"+cid).replace("http","ws"));
    //打开事件
    socket.onopen = function() {
    console.log("Socket 已打开");
    //socket.send("这是来自客户端的消息" + location.href + new Date());
    };
    //获得消息事件
    socket.onmessage = function(msg) {
    console.log(msg.data);
    //发现消息进入 开始处理前端触发逻辑
    };
    //关闭事件
    socket.onclose = function() {
    console.log("Socket已关闭");
    };
    //发生了错误事件
    socket.onerror = function() {
    alert("Socket发生了错误");
    //此时可以尝试刷新页面
    }
    //离开页面时,关闭socket
    //jquery1.8中已经被废弃,3.0中已经移除
    // $(window).unload(function(){
    // socket.close();
    //});
    }
    function push(){
    var socket = new WebSocket(("http://localhost:8888/hello-boot/websocket/push/"+cid).replace("http","ws"));
    }
    </script>
    </body>
    </html>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    1.5聊天ImController
    package cn.xdl.controller;

    import java.io.IOException;
    import java.util.concurrent.ConcurrentHashMap;

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

    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Component;

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;

    import cn.xdl.utils.result.AllResult;
    import lombok.extern.slf4j.Slf4j;

    /**
    * websocket通信
    * @author liurui
    *
    */
    @ServerEndpoint("/im/{userId}")
    @Component
    @Slf4j
    public class ImController {

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //旧:concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    //private static CopyOnWriteArraySet<ImController> webSocketSet = new CopyOnWriteArraySet<ImController>();
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    //新:使用map对象,便于根据userId来获取对应的WebSocket
    private static ConcurrentHashMap<String,ImController> websocketList = new ConcurrentHashMap<>();
    //接收sid
    private String userId="";
    /**
    * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session,@PathParam("userId") String userId) {
    this.session = session;
    websocketList.put(userId,this);
    log.info("websocketList->"+JSON.toJSONString(websocketList));
    //webSocketSet.add(this); //加入set中
    addOnlineCount(); //在线数加1
    log.info("有新窗口开始监听:"+userId+",当前在线人数为" + getOnlineCount());
    this.userId=userId;
    try {
    sendMessage(JSON.toJSONString(AllResult.ok("连接成功")));
    } catch (IOException e) {
    log.error("websocket IO异常");
    }
    }

    /**
    * 连接关闭调用的方法
    */
    @OnClose
    public void onClose() {
    if(websocketList.get(this.userId)!=null){
    websocketList.remove(this.userId);
    //webSocketSet.remove(this); //从set中删除
    subOnlineCount(); //在线数减1
    log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }
    }

    /**
    * 收到客户端消息后调用的方法
    *
    * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
    log.info("收到来自窗口"+userId+"的信息:"+message);
    if(StringUtils.isNotBlank(message)){
    JSONArray list=JSONArray.parseArray(message);
    for (int i = 0; i < list.size(); i++) {
    try {
    //解析发送的报文
    JSONObject object = list.getJSONObject(i);
    String toUserId=object.getString("toUserId");
    String contentText=object.getString("contentText");
    object.put("fromUserId",this.userId);
    //传送给对应用户的websocket
    if(StringUtils.isNotBlank(toUserId)&&StringUtils.isNotBlank(contentText)){
    ImController socketx=websocketList.get(toUserId);
    //需要进行转换,userId
    if(socketx!=null){
    log.debug("服务端发送给客户端信息--object:{}",object);
    socketx.sendMessage(JSON.toJSONString(AllResult.ok(object)));
    //此处可以放置相关业务代码,例如存储到数据库
    }
    }
    }catch (Exception e){
    e.printStackTrace();
    }
    }
    }
    }

    /**
    *
    * @param session
    * @param error
    */
    @OnError
    public void onError(Session session, Throwable error) {
    log.error("发生错误");
    error.printStackTrace();
    }
    /**
    * 实现服务器主动推送
    */
    public void sendMessage(String message) throws IOException {
    this.session.getBasicRemote().sendText(message);
    }


    /**
    * 群发自定义消息
    * */
    /*public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
    log.info("推送消息到窗口"+userId+",推送内容:"+message);
    for (ImController item : webSocketSet) {
    try {
    //这里可以设定只推送给这个sid的,为null则全部推送
    if(userId==null) {
    item.sendMessage(message);
    }else if(item.userId.equals(userId)){
    item.sendMessage(message);
    }
    } catch (IOException e) {
    continue;
    }
    }
    }*/

    public static synchronized int getOnlineCount() {
    return onlineCount;
    }

    public static synchronized void addOnlineCount() {
    ImController.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
    ImController.onlineCount--;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    1.6聊天socket_im.html
    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <style>
    .chat_div{
    500px;
    height:500px;
    border:1px solid red;
    text-align:center;
    }
    .chat_send_div{
    float:left;
    196px;
    height:498px;
    border:1px solid blue;
    }
    .chat_receive_div{
    float:right;
    290px;
    height:498px;
    border:1px solid green;
    }
    </style>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script>
    var socket;
    function openSocket() {
    if(typeof(WebSocket) == "undefined") {
    console.log("您的浏览器不支持WebSocket");
    }else{
    console.log("您的浏览器支持WebSocket");
    //实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接
    //等同于socket = new WebSocket("ws://localhost:8888/xxxx/im/25");
    //var socketUrl="${request.contextPath}/im/"+$("#userId").val();
    var socketUrl="http://localhost:8888/hello-boot/im/"+$("#userId").val();
    socketUrl=socketUrl.replace("https","ws").replace("http","ws");
    console.log(socketUrl)
    socket = new WebSocket(socketUrl);
    //打开事件
    socket.onopen = function() {
    console.log("websocket已打开");
    //socket.send("这是来自客户端的消息" + location.href + new Date());
    };
    //获得消息事件
    socket.onmessage = function(msg) {
    console.log(msg);
    //发现消息进入 开始处理前端触发逻辑contentText
    //$.parseJSON(msg);
    var data = msg.data;
    var result = $.parseJSON(data);
    var str = '';
    if(result.data=="连接成功"){
    console.info("test----if----"+data);
    str +='<h3 style="color:blue;">';
    str +='server:'+result.data;
    str +='</h3>';
    $(".chat_receive_div").append(str);
    }else{
    console.info("test---else-----"+result.data.contentText);
    str +='<h3 style="color:blue;">';
    str +='server:'+result.data.contentText;
    str +='</h3>';
    $(".chat_receive_div").append(str);
    }
    };
    //关闭事件
    socket.onclose = function() {
    console.log("websocket已关闭");
    };
    //发生了错误事件
    socket.onerror = function() {
    console.log("websocket发生了错误");
    }
    }
    }
    function sendMessage() {
    if(typeof(WebSocket) == "undefined") {
    console.log("您的浏览器不支持WebSocket");
    }else {
    var str = '';
    console.log("您的浏览器支持WebSocket");
    console.log('[{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}]');
    socket.send('[{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}]');
    str +='<h3 style="color:gold;">';
    str +=$("#toUserId").val()+':'+$("#contentText").val();
    str +='</h3>';
    $(".chat_send_div").append(str);
    }
    }
    </script>
    <body>
    <p>【userId】:<div><input id="userId" name="userId" type="text" value="25"></div>
    <p>【toUserId】:<div><input id="toUserId" name="toUserId" type="text" value="26"></div>
    <p>【toUserId】:<div><input id="contentText" name="contentText" type="text" value="嗷嗷嗷"></div>
    <p>【操作】:<div><a onclick="openSocket()">开启socket</a></div>
    <p>【操作】:<div><a onclick="sendMessage()">发送消息</a></div>
    <div class="chat_div">
    <div class="chat_send_div">
    <h3>
    发送消息
    </h3>
    </div>
    <div class="chat_receive_div">
    <h3>
    接受消息
    </h3>
    </div>
    </div>
    </body>
    </html>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    本程序实现SpringBoot基于websocket的网页聊天。
    --------------------- 

  • 相关阅读:
    给一个二维数组随机赋值
    sql server use WAITFOR DELAY to wait some milliseconds
    sql server print time with milliseconds
    UE4 LeapMotion 获取手掌朝向
    Unity打包Android出现Failed to find Build Tools revision
    UE4中Yaw,Roll,Pitch分别对应什么轴?
    Unity 遍历场景所有物体(包括隐藏及被禁用的物体)
    C#-Linq扩展方法DistinctBy
    Unity编辑器扩展--Scene Manager
    Unity查找功能编辑器扩展Searcher
  • 原文地址:https://www.cnblogs.com/hyhy904/p/10989699.html
Copyright © 2011-2022 走看看