非常简单的用Springboot+websocket 实现聊天室小项目(并保存用户登录信息)

websocket 是http的补充,为了实现实时通信,一次握手便可以保持长连接,避免繁琐的请求头浪费宽带。websocket 可以非常简单的实现一个聊天室的项目。

下面,我简易地用Springboot+websocket 实现聊天室小项目。

(1)pom.xml 



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.1.RELEASE
         
    
    com.microservice
    websocket
    0.0.1-SNAPSHOT
    websocket
    Demo project for Spring Boot

    
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-freemarker
        
        
            org.springframework.boot
            spring-boot-starter-websocket
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            org.springframework.boot
            spring-boot-devtools
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    


(2)一个配置类   WebSocketConfig

package com.microservice.websocket.config;

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

/**
 * @Description: 编写一个WebSocketConfig配置类,注入对象ServerEndpointExporter,
 *      这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
 */
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

(3)一个websocket 业务类

package com.microservice.websocket.socket;

import com.microservice.websocket.entity.UserInfo;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 *  * @Description: websocket的具体实现类
 *  * 使用springboot的唯一区别是要@Component声明下,而使用独立容器是由容器自己管理websocket的,
 *  * 但在springboot中连容器都是spring管理的。
 *     虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,
 *     所以可以用一个静态set保存起来。
*/
@ServerEndpoint(value = "/websocket/{nickName}")
@Component
public class MyWebSocket {
    //用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet();
    private static Map connectmap = new HashMap<>();//用session作为key,保存用户信息
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("nickName") String nickName) {
        this.session = session;
        UserInfo userInfo = new UserInfo(session.getId(),nickName);
        connectmap.put(session,userInfo);
        webSocketSet.add(this);     //加入set中
        System.out.println(nickName+" 上线了!当前在线人数为" + webSocketSet.size());
        //群发消息,告诉每一位
        broadcast(nickName+" 上线了!-->当前在线人数为:"+webSocketSet.size());
    }
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        String nickName=connectmap.get(session).getNickName();
        connectmap.remove(session);
        webSocketSet.remove(this);  //从set中删除
        System.out.println(nickName+" 下线了!当前在线人数为" + webSocketSet.size());
        //群发消息,告诉每一位
        broadcast(nickName+" 下线,当前在线人数为:"+webSocketSet.size());
    }
    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端的消息:" + message);
        //群发消息
        String nickName=connectmap.get(session).getNickName();
        broadcast(nickName+" 说:"+message);
    }
    /**
     * 发生错误时调用
     *
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }
    /**
     * 群发自定义消息
     * */
    public  void broadcast(String message){
        for (MyWebSocket item : webSocketSet) {
            //同步异步说明参考:http://blog.csdn.net/who_is_xiaoming/article/details/53287691
            //this.session.getBasicRemote().sendText(message);
            item.session.getAsyncRemote().sendText(message);//异步发送消息.
        }
    }
}

(3)一个用户实体类  UserInfo

package com.microservice.websocket.entity;

/**
 * @program: websocket->UserInfo
 * @description: 用户信息
 * @author: ChenZhihao
 * @create: 2019-12-26 16:04
 **/
public class UserInfo {
    private String id;
    private String nickName;
    private String password;

    public UserInfo() {
    }

    public UserInfo(String id, String nickName) {
        this.id = id;
        this.nickName = nickName;
    }

    public String getId() {
        return id;
    }

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

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public String getPassword() {
        return password;
    }

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

(4)如果你不是前后端分离,即前后端放在同一个项目里面,还需要配置页面的路径。如果是前后端分离,这步省略。

package com.microservice.websocket.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("web")
public class WebController {

    @RequestMapping("index")
    public String index(){
        return "index";
    }
}

(5)好了,后端的代码写完了。这步开始写页面和js。

页面  index




    Spring Boot Demo - FreeMarker

    
    


    
    
    用户名:
    

消息:

(6)页面的js  :   index.js

var websocket = null;
function connectWebSocket(){
    //判断当前浏览器是否支持WebSocket
    //判断当前浏览器是否支持WebSocket
    if ('WebSocket'in window) {
        var nickName=document.getElementById('nickName').value;
        console.log("ws://localhost:8080/websocket/"+nickName)
        websocket = new WebSocket("ws://localhost:8080/websocket/"+nickName);
    } else {
        alert('当前浏览器不支持websocket');
    }

    //连接发生错误的回调方法
    websocket.onerror = function() {
        setMessageInnerHTML("error");
    };

    //接收到消息的回调方法
    websocket.onmessage = function(event) {
        setMessageInnerHTML(event.data);
    }

    //连接关闭的回调方法
    websocket.onclose = function() {
        setMessageInnerHTML("Loc MSG:关闭连接");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function() {
        websocket.close();
    }
}

//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
    document.getElementById('message').innerHTML += innerHTML + '
'; } //关闭连接 function closeWebSocket() { websocket.close(); } //发送消息 function send() { var message = document.getElementById('text').value; websocket.send(message); }

(7)CSS : index.css

#message{
    margin-top:40px;
    border:1px solid gray;
    padding:20px;
}

(8)好了,全部写完了。现在开始测试。

1. 先打开一个网页:http://localhost:8080/web/index

输入用户名   志豪,点连接,发送一次信息。如图(志豪的界面):

非常简单的用Springboot+websocket 实现聊天室小项目(并保存用户登录信息)_第1张图片

2. 再打开一个页面,输入用户名  老毛,点连接,然后发送信息。如图(老毛的界面):

非常简单的用Springboot+websocket 实现聊天室小项目(并保存用户登录信息)_第2张图片

3. 这时,志豪看到的界面如图:

非常简单的用Springboot+websocket 实现聊天室小项目(并保存用户登录信息)_第3张图片

4. 当老毛关闭了页面时,志豪看到的界面如图:

非常简单的用Springboot+websocket 实现聊天室小项目(并保存用户登录信息)_第4张图片

 

你可能感兴趣的:(websocket)