SpringBoot学习——websocket组件学习在线聊天室demo

SpringBoot学习——websocket组件学习在线聊天室demo

因为之前用node.js实现过websocket的聊天室,后来在学习SpringBoot时看到WebSocket组件学习,所以就用之前的页面来实现了一遍

需求

  1. 简单聊天室实现。
  2. 用户上线,下线。
  3. 当前登录用户列表刷新。
  4. 发送消息进行聊天。

demo相关

demo下载:http://pan.baidu.com/s/1jHUBVBg
演示图

具体实现

材料

  1. MyEclipse
  2. Maven
  3. SpringBoot
  4. sockjs.min.js
  5. stomp.min.js
  6. jquery.js

过程

1.新建一个Maven项目

2.结构目录

3.pom.xml

配置pom.xml,引入相关依赖包,webSocket相关包是spring-boot-starter-websocket

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0modelVersion>
  <groupId>com.pzrgroupId>
  <artifactId>chartroomartifactId>
  <version>0.0.1-SNAPSHOTversion>
    
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>1.5.1.RELEASEversion>
    parent>
    
    <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-autoconfigureartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-websocketartifactId>
            <version>1.5.1.RELEASEversion>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <optional>trueoptional>
        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.31version>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

    dependencies>

    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
            
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-jar-pluginartifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>trueaddClasspath>
                            <mainClass>com.pzr.chatroom.LanchermainClass>
                        manifest>
                    archive>
                configuration>
            plugin>
        plugins>
    build>
project>

4.Lancher.java

springboot的启动程序

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 启动器
 * @author pzr
 *
 */
@SpringBootApplication
public class Lancher {
      public static void main(String[] args) { 
            SpringApplication.run(Lancher.class, args); 
        } 

}

5.ChatRoomConfig.java

webSocket的配置类

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

/**
 * WebSocketDemo
 * @author pzr
 *
 */
@Configuration
@EnableWebSocketMessageBroker//开启使用STOMP协议来传输基于代理的消息,支持使用@MessageMapping(类似于@RequestMapping)
public class ChatRoomConfig extends AbstractWebSocketMessageBrokerConfigurer{

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) { //注册STOMP协议节点,并映射指定的URL
        registry.addEndpoint("/endpointWisely").withSockJS(); //注册一个STOMP的endpint,并指定使用SocketJS协议
    }
}

6.ChatRoomController.java

聊天室的控制器,包含上线,下线,聊天和刷新登录用户列表
1. 使用@MessageMapping(“/talk”)供给前端访问调用
2. 使用@SendTo(“/refreshchatwindow”),将消息发送给监听了refreshchatwindow的客户端
3. 使用template.convertAndSend(“/refreshloginlist”,CacheBean.clientList);,从服务端向监听了refreshchatwindow的客户端发送消息

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;

import com.pzr.chatroom.entity.CacheBean;
import com.pzr.chatroom.entity.ChatContent;
import com.pzr.chatroom.entity.User;


/**
 * 演示控制器
 * @author pzr
 *
 */
@Controller
public class ChatRoomController {

     @Autowired
     private SimpMessagingTemplate template;

    /**
     * 聊天
     * @param msg
     * @return
     */
    @MessageMapping("/talk")//浏览器映射地址
    @SendTo("/refreshchatwindow")
    public ChatContent talk(ChatContent content) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        content.setDate(sdf.format(new Date()));
        return content;
    }

    /**
     * 下线
     * @param msg
     * @return
     */
    @MessageMapping("/downLine")
    private void downLine(User msg) {
        List list = CacheBean.clientList;
        list.remove(msg);
        //服务器端通知客户端刷新当前登录人列表
        refreshLoginList();
    }

    /**
     * 上线
     * @param msg
     * @return
     */
    @MessageMapping("/upLine")
    private void upLine(User msg) {
        //内存中将用户加入进来
        List list = CacheBean.clientList;
        list.add(msg);
        //服务器端通知客户端刷新当前登录人列表
        refreshLoginList();
    }

    /**
     * 服务器端通知客户端刷新当前登录人列表
     * @param list
     */
    @MessageMapping("/refreshLoginList")
    public void refreshLoginList( ){
        template.convertAndSend("/refreshloginlist",CacheBean.clientList);
    }
}

7.User.java

  1. 用户实体,包含名称和uuid
  2. uuid用作当用户下线时从内存中移除该对象
  3. 重写equals,判断uuid一致的为相同对象,保证list.remove(msg)有效
/**
 *用户实体
 * 
 * @author pzr
 * 
 */
public class User {

    private String uuid;

    private String name;

    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        if (uuid == null) {
            if (other.uuid != null)
                return false;
        } else if (!uuid.equals(other.uuid))
            return false;
        return true;
    }
}

8.ChatContent.java

  1. 聊天内容实体,包含发送时间,发送人和内容
/**
 * 聊天内容
 * @author pzr
 *
 */
public class ChatContent {
    /**
     * 时间
     */
    public String date;
    /**
     * 内容
     */
    public String content;
    /**
     * 发送消息的人
     */
    public String sendUser;
    public String getDate() {
        return date;
    }
    public void setDate(String date) {
        this.date = date;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public String getSendUser() {
        return sendUser;
    }
    public void setSendUser(String sendUser) {
        this.sendUser = sendUser;
    }
}

9.CacheBean.java

将当前登录人信息存在内存中

import java.util.ArrayList;
import java.util.List;

public class CacheBean {
    /**
     * 使用静态集合来存储当前登录用户
     */
    public static List clientList = new ArrayList();
}

10.chatroom.html

  1. 聊天页面,在sprigboot中,放在static下的静态文件可访问.
  2. 需要引入sockjs.min.js和stomp.min.js两个文件
  3. 先登录才可发言
  4. 可以发言快捷键ctrl+enter

<html lang="en">
<head>
<meta charset="UTF-8">
<title>聊天室title>
<style type="text/css">
style>
<script type="text/javascript" src="js/sockjs.min.js">script>
<script type="text/javascript" src="js/stomp.min.js">script>

<script type="text/javascript">
    var nowUUID = "";
    var stompClient = null;

    window.onload = function() {

        //连接服务器
        var socket = new SockJS("/endpointWisely"); 
        stompClient = Stomp.over(socket);
        //响应事件监听
        stompClient.connect({}, function(frame) {
            //监听刷新当前登录人列表
            stompClient.subscribe("/refreshloginlist", function(respnose) {
                var serverObj = JSON.parse(respnose.body);//返回是一个当前登录人数组
                var nameArrStr = "";
                for (var i = 0; i < serverObj.length; i++) {
                    nameArrStr = nameArrStr + serverObj[i].name + "\n";
                }
                console.log(nameArrStr);
                userList.value = nameArrStr;
            });

            //监听刷新聊天窗口
            stompClient.subscribe("/refreshchatwindow", function(respnose) {
                var serverObj = JSON.parse(respnose.body);//返回是一个当前登录人数组
                var contentArea = document.getElementById("contentArea");
                var val = contentArea.value;
                var returnText = serverObj.sendUser+" "+serverObj.date+":\n "+"  "+serverObj.content+"\n ";
                contentArea.value = val + returnText;
                contentArea.scrollTop = contentArea.scrollHeight;
            });


            //刷新当前登录用户列表
            stompClient.send("/refreshLoginList", {}, {});
        });


        //键盘事件注册
        document.onkeydown = function(event) {
            //ctrl+enter发送消息
            if (window.event.ctrlKey && window.event.keyCode == 13) {
                sendTalk();
            }
        };
    }

    //上下线
    function upLine() {
        var upDownBtn = document.getElementById("upDownBtn");
        var uaerNameTxt = document.getElementById("uaerNameTxt");
        var uuidLab = document.getElementById("uuidLab");
        var userList = document.getElementById("userList");
        if (uaerNameTxt.value == "") {
            alert("请输入名称!");
            return;
        }
        if (upDownBtn.value == "上线") {
            upDownBtn.value = "下线";
            uaerNameTxt.readOnly = true;
            nowUUID = guid();
            uuidLab.innerText=nowUUID;
            //上线完成,添加用户
            var myObj = new Object();
            myObj.uuid = nowUUID;
            myObj.name = uaerNameTxt.value;
            var json_data = JSON.stringify(myObj);
            stompClient.send("/upLine", {}, json_data);
        } else {
            upDownBtn.value = "上线";
            uaerNameTxt.readOnly = false;
            //下线完成,删除用户
            var myObj = new Object();
            myObj.uuid = nowUUID;
            var json_data = JSON.stringify(myObj);
            stompClient.send("/downLine", {}, json_data);
            nowUUID = "";
            uuidLab.innerText=nowUUID;
        }
    }

    //发言
    function sendTalk() {
        var upDownBtn = document.getElementById("upDownBtn");
        if (upDownBtn.value == "上线") {
            alert("请先上线!");
            return;
        }
        var userName = document.getElementById("uaerNameTxt").value;
        var sendContent = document.getElementById("sendContent");
        //往服务器发消息
        var myObj = new Object();
        myObj.sendUser = userName;
        myObj.content = sendContent.value;
        var json_data = JSON.stringify(myObj);
        stompClient.send("/talk", {}, json_data);
        sendContent.value = "";
        sendContent.focus();
    }

    //浏览器关闭事件 发起删除登录人消息
    window.onbeforeunload = onbeforeunload_handler;
    function onbeforeunload_handler() {
            //下线完成,删除用户
            var myObj = new Object();
            myObj.uuid = nowUUID;
            var json_data = JSON.stringify(myObj);
            stompClient.send("/downLine", {}, json_data);
    }

    //生成uuid
    function guid() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,
                function(c) {
                    var r = Math.random() * 16 | 0, v = c == 'x' ? r
                            : (r & 0x3 | 0x8);
                    return v.toString(16);
                });
    }
script>
head>
<body style="">
    <table border="1"
        style="width: 50%;height: 600px; border-color: #00B7FF;margin:0 auto">
        <tr height="40px">
            <td colspan="4">当前登录人: <input id="uaerNameTxt" type="text"
                width="400px" height="100%" /> <input id="upDownBtn" type="button"
                value="上线" onclick="upLine()" /> <label id="uuidLab">label>
            td>
        tr>
        <tr>
            <td colspan="3"><textarea id="contentArea" name="contentArea"
                    disabled="disabled"
                    style="width:100%;height:370px;padding:0px;margin:0px;">textarea>
            td>
            <td rowspan="3" width="150px"><textarea id="userList"
                    name="userList" disabled="disabled"
                    style="width:100%;height:565px;padding:0px;margin:0px;">textarea>
            td>
        tr>
        <tr height="150px">
            <td colspan="3"><textarea id="sendContent"
                    style="width:100%;height:140px;padding:0px;margin:0px;">textarea>
            td>
        tr>
        <tr height="40px">
            <td colspan="2">td>
            <td width="80px" style="padding:0px;"><input type="button"
                onclick="sendTalk()" value="发送"
                style="width:100%;height:40px;margin:0px;border:0px;padding:0px;" />
            td>
        tr>
    table>
body>
html>

参考

http://blog.csdn.net/PandaWang1989/article/details/54863489?locationNum=5&fps=1
http://blog.csdn.net/yingxiake/article/details/51224569

你可能感兴趣的:(springboot)