当前位置:   article > 正文

websocket实现多聊天室,聊天(附完整代码)_java 使用websocket实现多人聊天室

java 使用websocket实现多人聊天室

需求

1、最开始想到是今年的课程设计,想着做个有点意思的东西,发现关于websocket技术没有接触过,就搞了类似一个QQ的多聊天室聊天的小项目。
2、需要实现,登录,注册,等等等…当然最核心,最基础的还是我们的聊天的实现
3、采用的技术,后端java,前端vue

关于websocket

1、WebSocket是一种在单个TCP连接上进行全双工通信的协议
2、WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
3、简单来说,就是客户端给服务器端发消息,服务器端会有监听,并可以再给客户端发消息,客户端也有监听,对服务器端的消息进行处理。甲——>服务端——>其他用户

websocket的方法

后端

1、引入依赖
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
  • 1
  • 2
  • 3
  • 4
2、在自己的websocket上加ServerEndpoint注解

因为要实现多聊天室,多用户聊天,所以,需要一个当前聊天室的id(page),以及当前用户id(userId)

@ServerEndpoint(value = "/webSocket/{page}/{userId}")
  • 1
3、监听的方法
1、@OnOpen(连接建立时触发)
2、@OnClose(关闭连接时触发)
3、@OnMessage( 收到客户端消息触发事件)
4、@OnError(通信发生错误时触发)
4、发送消息的方法(利用Session会话)
1、在建立连接时触发@OnOpen,

将当前用户的会话存到一个map集合<房间id,该房间内用户sessionId集合set>

  • 如果不实现,多聊天室,set集合即可
2、收到客户端消息时,将该消息进行转发,
//遍历集合,将消息给该集合中的所有人
for (Session s : sessions) {
	// if (!s.getId().equals(session.getId()))  可以加判断,不发给谁,		
       s.getBasicRemote().sendText(JSONUtil.toJsonStr(contentBo));
}
  • 1
  • 2
  • 3
  • 4
  • 5

前端

1、实例化websocket

1、利用vue在data里面,定义一个变量socket
2、进行实例化操作,之后便可利用socket进行操作

 if (typeof (WebSocket) === "undefined") {
 		alert("您的浏览器不支持socket")
 } else {
     // 实例化socket
     	this.socket = new WebSocket("ws://url(服务地址) + webSocket/" + page + "/" +userId)
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
2、监听的方法

3、4方法需要进行实时监听,建议写在vue 生命周期的mounted里面

1、websocket.onopen(建立连接成功时触发)
2、websocket.onclose(连接关闭时触发)
3、websocket.onmessage(收到服务器的数据触发)
4、websocket.onerror(连接出现错误时触发)
3、发送消息的方法
 send: function (msg) {
 	this.socket.send(msg)
 }
  • 1
  • 2
  • 3

websocket核心代码

后端

package cn.itcast.config;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import cn.itcast.api.bo.ContentBo;
import cn.itcast.api.entity.Content;
import cn.itcast.api.entity.User;
import cn.itcast.api.service.IContentService;
import cn.itcast.api.service.IUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author: zpt
 * @Date: 2019-09-20 15:12
 * @Description:
 */
@Component
@ServerEndpoint(value = "/webSocket/{page}/{userId}")
public class WebSocket {

    private static IUserService userService;

    @Autowired
    public void setMyServiceImpl(IUserService userService){
        WebSocket.userService = userService;
    }


    private static IContentService contentService;

    @Autowired
    public void setMyServiceImpl(IContentService contentService){
        WebSocket.contentService = contentService;
    }


    private Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * 用来记录房间及房间内用户session_id
     */
    private static Map<String, Set> roomMap = new ConcurrentHashMap(8);

    /**
     * 用来记录session_id与用户id之间的关系
     */
    private static Map<String, Integer> userIdMap = new ConcurrentHashMap(8);


    @OnOpen
    public void open( @PathParam("page") String page, Session session, @PathParam("userId") Integer userId ) throws IOException {
        Set set = roomMap.get(page);
        userIdMap.put(session.getId(), userId);
        // 如果是新的房间,则创建一个映射,如果房间已存在,则把用户放进去
        if (set == null) {
            set = new CopyOnWriteArraySet();
            set.add(session);
            roomMap.put(page, set);
        } else {
            set.add(session);
        }
    }

    @OnClose
    public void close( @PathParam("page") String page, Session session, @PathParam("userId") Integer userId ) {
        userIdMap.remove(session.getId());
        // 如果某个用户离开了,就移除相应的信息
        if (roomMap.containsKey(page)) {
            roomMap.get(page).remove(session);
        }
    }

    @OnMessage
    public void reveiveMessage( @PathParam("page") String page, Session session, String message ) throws IOException {
        log.info("接受到用户{}的数据:{}", session.getId(), message);

        Integer userId = userIdMap.get(session.getId());

        //存入数据库中
        Content content = new Content();
        content.setRoomId(Integer.valueOf(page));
        content.setContent(message);
        content.setSendTime(LocalDateTime.now());
        content.setSendId(userId);
        contentService.save(content);

        //查询当前用户信息
        User user = userService.getById(userId);

        //返回给前端的Bo对象
        ContentBo contentBo = new ContentBo();
        BeanUtil.copyProperties(content, contentBo);
        BeanUtil.copyProperties(user, contentBo);

        // 拼接一下用户信息
        //String msg = session.getId() + " : " + message;
        Set<Session> sessions = roomMap.get(page);
        // 给房间内所有用户推送信息
        for (Session s : sessions) {
            if (!s.getId().equals(session.getId())) {
                s.getBasicRemote().sendText(JSONUtil.toJsonStr(contentBo));
            }
        }
    }


    @OnError
    public void error( Throwable throwable ) {
        try {
            throw throwable;
        } catch (Throwable e) {
            log.error("未知错误");
        }
    }
}

  • 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

前端(首先要在created里面获取到当前聊天的roomId,以及当前用户userId!!!)

    new Vue({
        el: '#app',
        data: {
            "roomId": null,
            "userId": null,
            "websocket": null,
        },
        created() {
        	//因为我是html页面引入的vue,因此页面之间的数据直接用了,location.href = ""
        	//getQueryVariable方法起到了一个接受上一个页面传的参数的作用
            this.userId = this.getQueryVariable('userId')
            this.roomId = this.getQueryVariable('roomId')
        },

        mounted() {
            // 初始化
            this.init()
        },

        computed() {

        },
        methods: {
            init: function () {
                if (typeof (WebSocket) === "undefined") {
                    alert("您的浏览器不支持socket")
                } else {
                    // 实例化socket
                    this.socket = new WebSocket("ws://url(服务端地址)/webSocket/" + this.roomId + "/" + this.userId)
                    // 监听socket连接
                    this.socket.onopen = this.open
                    // 监听socket错误信息
                    this.socket.onerror = this.error
                    // 监听socket消息
                    this.socket.onmessage = this.getMessage
                }
            },
            open: function () {
                console.log("socket连接成功")
            },
            error: function () {
                console.log("连接错误")
            },
            send: function (msg) {
                this.socket.send(msg)
            },
            close: function () {
                console.log("socket已经关闭")
            },
            // 获取多个参数的时候
            getQueryVariable: function (variable) {
                var query = window.location.search.substring(1);
                var vars = query.split("&");
                for (var i = 0; i < vars.length; i++) {
                    var pair = vars[i].split("=");
                    if (pair[0] == variable) { return pair[1]; }
                }
                return (false);
            },
        destroyed() {
            // 销毁监听
            this.socket.onclose = this.close
        },
    }
)
  • 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

遇到的问题

1、websocket不能正常注入service(直接@Autowired),需采用以下方式

https://blog.csdn.net/qq_43532386/article/details/111783423

2、后端服务器部署,websocket不能正常使用,需要在nginx里面配置一些东西

https://blog.csdn.net/qq_43532386/article/details/111784152

3、实现聊天的时候,聊天界面的滚动条,vuejs不能控制到最下边

https://blog.csdn.net/qq_43532386/article/details/111784809

完整代码(ui设计比较简略)

链接:https://pan.baidu.com/s/1FWnDR-psILqPEC4XLeRs6w
提取码:jqzq

文件上传,直接上传的服务器,通过配置nginx进行访问。
整体仅供参考,实用意义不大!!!

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/325456
推荐阅读
相关标签
  

闽ICP备14008679号