利用WebSocket实现聊天室、即时通讯

目录

概述:本项目整合websocket协议,实现一个简单web聊天室功能;

 执行步骤:

1、用户在登录页面点击登录后,跳转到后端的 “  loginvalidate ” 路径下进行,如果用户名密码正确,则跳转到聊天室页面。

2、 聊天室页面首先会先建立一个websocket连接,并广播上线消息提醒,默认连接的时localhost:8080服务器,后续可根据需要自行进行修改。

3、展示在线用户

4、消息的即时发送,针对选定的在线用户进行消息的发送。​编辑

​5、连接关闭:当关闭浏览器或退出页面自动关闭websocket连接

​源码:

概述:本项目整合websocket协议,实现一个简单web聊天室功能;

这个聊天室主要包含一下几个功能:

1、聊天室的登录、退出;登陆时,浏览器自动向服务器发起websocket连接,退出时自动切断。

2、登陆后能够查看当前在线的用户列表。

3、登录的用户可以点击一个在线的其他用户,并给他发送消息,消息先提交到服务器,在通过服务器转发给另一端用用户,支持消息群发功能,群发消息展示给所有用户。

4、支持好友上、下线提醒功能。

 执行步骤:

1、用户在登录页面点击登录后,跳转到后端的 “  loginvalidate ” 路径下进行,如果用户名密码正确,则跳转到聊天室页面。

2、 聊天室页面首先会先建立一个websocket连接,并广播上线消息提醒,默认连接的时localhost:8080服务器,后续可根据需要自行进行修改。

3、展示在线用户

4、消息的即时发送,针对选定的在线用户进行消息的发送。

5、群发消息,给所有在线用户发送消息提醒。

5、连接关闭:当关闭浏览器或退出页面自动关闭websocket连接

源码:

login页面

<!DOCTYPE>
<html>
  <head>
    <title>login</title>
    <script src="./js/jquery-1.12.3.min.js"></script>
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<style type="text/css">
.vertical-center{
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
</style>
  </head>
  <body>
    <div class="container vertical-center">
	<div class="col-md-6 col-md-offset-3">
      <form  action="loginvalidate" method="post">
        <h2 >登录聊天室</h2>
        <label for="inputEmail" class="sr-only">userid</label>
        <input type="text" name="username" id="inputEmail" class="form-control" placeholder="userid" required autofocus/>
        <label for="inputPassword" class="sr-only">Password</label>
        <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required/>
        <label for="inputEmail" class="sr-only">userid</label>
        <button class="btn btn-lg btn-primary btn-block" type="submit">login</button>
      </form>
        	
	</div>
    </div> <!-- /container -->
  </body>
</html>

聊天室页面:chatroom.html

<!DOCTYPE>
<html>
<head>
    <title>聊天室</title>
    <script src="./js/jquery-1.12.3.min.js"></script>
    <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css"/>
    <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <style>
        body{
            margin-top:5px;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-3">
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">当前登录用户</h3>
                </div>
                <div class="panel-body">
                    <div class="list-group">
                        <a href="#" class="list-group-item">你好,<span id="user"></span></a>
                        <a href="logout" class="list-group-item">退出</a>
                    </div>
                </div>
            </div>
            <div class="panel panel-primary" id="online">
                <div class="panel-heading">
                    <h3 class="panel-title">当前在线的其他用户</h3>
                </div>
                <div class="panel-body">
                    <div class="list-group" id="users">
                    </div>
                </div>
            </div>
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">群发系统广播</h3>
                </div>
                <div class="panel-body">
                    <input type="text" class="form-control"  id="msg" /><br>
                    <button id="broadcast" type="button" class="btn btn-primary">发送</button>
                </div>
            </div>
        </div>
        <div class="col-md-9">
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title" id="talktitle"></h3>
                </div>
                <div class="panel-body">
                    <div class="well" id="log-container" style="height:400px;overflow-y:scroll">

                    </div>
                    <input type="text" id="myinfo" class="form-control col-md-12" /> <br>
                    <button id="send" type="button" class="btn btn-primary">发送</button>
                </div>
            </div>
        </div>
    </div>
</div>
<script>
    Date.prototype.format = function(fmt) {
        var o = {
            "M+" : this.getMonth()+1,                 //月份
            "d+" : this.getDate(),                    //日
            "h+" : this.getHours(),                   //小时
            "m+" : this.getMinutes(),                 //分
            "s+" : this.getSeconds(),                 //秒
            "q+" : Math.floor((this.getMonth()+3)/3), //季度
            "S"  : this.getMilliseconds()             //毫秒
        };
        if(/(y+)/.test(fmt)) {
            fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
        }
        for(var k in o) {
            if(new RegExp("("+ k +")").test(fmt)){
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
            }
        }
        return fmt;
    }
    $(document).ready(function() {
        var user;
        var uid;
        // 指定websocket路径
        var websocket;
        $.get("/currentuser",function(data){
            user = data.name;
            uid = data.uid;
            $("#user").html(user);

            if ('WebSocket' in window) {
                websocket = new WebSocket("ws://localhost:8080/webSocket/"+user);
                console.log("连接成功")
            }
            websocket.onmessage = function(event) {
                var data=JSON.parse(event.data);
                if(data.to==0){//上线消息
                    if(data.text!=user)
                    {
                        $("#users").append('<a href="#" onclick="talk(this)" class="list-group-item">'+data.text+'</a>');
                        alert(data.text + "上线了");
                    }
                }else if(data.to==-2){//下线消息
                    if(data.text!=user)
                    {
                        $("#users > a").remove(":contains('"+data.text+"')");
                        alert(data.text + "下线了");
                    }
                }else {
                    // 普通消息
                    // 接收服务端的实时消息并添加到HTML页面中
                    $("#log-container").append("<div class='bg-info'><label class='text-danger'>"+data.from+"&nbsp;"+data.date+"</label><div class='text-success'>"+data.text+"</div></div><br>");
                    // 滚动条滚动到最低部
                    scrollToBottom();
                }
            };

            $.post("/onlineusers?currentuser="+user,function(data){
                for(var i=0;i<data.length;i++)
                    $("#users").append('<a href="#" onclick="talk(this)" class="list-group-item">'+data[i]+'</a>');
            });

        });
        $("#broadcast").click(function(){
            var data = {};
            data["from"] = "系统消息";
            data["to"] = -1;
            data["text"] = $("#msg").val();
            websocket.send(JSON.stringify(data));
            $("#msg").val("");
        });

        $("#send").click(function() {
            if ($("body").data("to")==undefined) {
                alert("请选择聊天对象");
                return false;
            }
            var data = {};
            data["from"] = user;
            data["to"] = $("body").data("to");
            data["text"] = $("#myinfo").val();
            websocket.send(JSON.stringify(data));
            $("#log-container").append("<div class='bg-success'><label class='text-info'>我&nbsp;" + new Date().format("yyyy-MM-dd hh:mm:ss") + "</label><div class='text-info'>" + $("#myinfo").val() + "</div></div><br>");
            scrollToBottom();
            $("#myinfo").val("");
        });

    });

    function talk(a){
        $("#talktitle").text("与"+a.innerHTML+"的聊天");
        $("body").data("to",a.innerHTML);
    }
    function scrollToBottom(){
        var div = document.getElementById('log-container');
        div.scrollTop = div.scrollHeight;
    }
</script>

</body>
</html>

 Login

package com.project.websocketdemo.controller;

import com.alibaba.druid.util.StringUtils;
import com.project.websocketdemo.entity.User;
import com.project.websocketdemo.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpSession;
import java.sql.Struct;

/**
 * @Author:yaojunjie
 * @Package:com.project.websocketdemo.controller
 * @Date:2024/5/2 21:02
 */
@Controller
public class Login {

    @Autowired
    private LoginService loginService;

    @RequestMapping("/loginvalidate")
    public String loginvalidate(@RequestParam("username") String username, @RequestParam("password") String pwd, HttpSession httpSession){
        if (StringUtils.isEmpty(username)){
            return "login";
        }
        String realPwd = loginService.getpwdbyname(username);
        if (!StringUtils.isEmpty(realPwd) && StringUtils.equals(pwd,realPwd)){
            Long uid = loginService.getUidbyname(username);
            httpSession.setAttribute("uid",uid);
            return "chatroom";
        }else {
            return "fail";
        }
    }

    @RequestMapping("/login")
    public String login() {
        return "login";
    }

    @RequestMapping("/logout")
    public String logout(HttpSession httpSession) {
        return "login";
    }

    @RequestMapping(value = "/currentuser", method = RequestMethod.GET)
    @ResponseBody
    public User currentuser(HttpSession httpSession) {
        Long uid = (Long) httpSession.getAttribute("uid");
        String name = loginService.getnamebyid(uid);
        return new User(uid, name);
    }
}

ChatController

package com.project.websocketdemo.controller;

import com.project.websocketdemo.service.LoginService;
import com.project.websocketdemo.service.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.websocket.Session;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author:yaojunjie
 * @Package:com.project.websocketdemo.controller
 * @Date:2024/5/2 23:11
 */
@Controller
public class ChatController {
    @Autowired
    private LoginService loginService;

    @RequestMapping("/onlineusers")
    @ResponseBody
    public Set<String> onlineusers(@RequestParam("currentuser") String currentuser){
        ConcurrentHashMap<String, Session> map = WebSocketServer.getSessionPools();
        Set<String> set = map.keySet();
        Iterator<String> it = set.iterator();
        Set<String> nameset = new HashSet<>();
        while (it.hasNext()){
            String entry = it.next();
            if (!entry.equals(currentuser)){
                nameset.add(entry);
            }
        }
        return nameset;
    }
}

WebSocket

package com.project.websocketdemo.service;

import com.alibaba.fastjson.JSON;
import com.project.websocketdemo.entity.Message;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author:yaojunjie
 * @Package:com.project.websocketdemo.service
 * @Date:2024/5/2 22:20
 */
@ServerEndpoint("/webSocket/{username}")
@Component
public class WebSocketServer {

    // 静态变量,用来记录当前在线连接数。
    private static AtomicInteger onlineNum = new AtomicInteger();

    // concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象
    private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();

    /**
     * 连接成功时调用
     * @param session
     * @param userName
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "username") String userName) {
        sessionPools.put(userName, session);
        addOnlineCount();
        System.out.println("加入webSocket!当前人数为" + onlineNum);
        // 广播上线消息
        Message message = new Message();
        message.setDate(new Date());
        message.setTo("0");
        message.setText(userName);
        broadcast(JSON.toJSONString(message,true));
    }

    // 收到客户端消息后,根据接收人的username把消息推下去或者群发
    // to = -1 是群发消息
    @OnMessage
    public void onMessage(String message){
        System.out.println("Server get "+ message);
        Message msg = JSON.parseObject(message, Message.class);
        msg.setDate(new Date());
        if ("-1".equals(msg.getTo())){
            broadcast(JSON.toJSONString(msg,true));
        }else {
            sendInfo(msg.getTo(),JSON.toJSONString(msg,true));
        }
    }

    /**
     * 用户退出时关闭了页面或浏览器,这导致了 WebSocket 连接的断开。
     * 在 WebSocket 连接断开时,浏览器会触发 WebSocket 的 onclose 事件
     * @param userName
     */
    @OnClose
    public void onClose(@PathParam(value = "username") String userName){
        sessionPools.remove(userName);
        subOnlineCount();
        System.out.println(userName + "断开webSocket连接!当前人数为" + onlineNum);
        // 广播下线消息
        Message msg = new Message();
        msg.setDate(new Date());
        msg.setTo("-2");
        msg.setText(userName);
        broadcast(JSON.toJSONString(msg,true));
    }

    //错误时调用
    @OnError
    public void onError(Session session, Throwable throwable){
        System.out.println("发生错误");
        throwable.printStackTrace();
    }

    private void sendInfo(String username, String jsonString) {
        Session session = sessionPools.get(username);
        try {
            sendMessage(session,jsonString);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // 群发消息
    private void broadcast(String jsonString) {
        for (Session session : sessionPools.values()) {
            try {
                sendMessage(session,jsonString);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    // 发送消息
    public void sendMessage(Session session,String message) throws IOException {
        if (session!=null){
            synchronized (session){
                System.out.println("发送数据:" + message);
                session.getBasicRemote().sendText(message);
            }
        }
    }

    public static void addOnlineCount() {
        onlineNum.incrementAndGet();
    }

    public static void subOnlineCount() {
        onlineNum.decrementAndGet();
    }

    public static AtomicInteger getOnlineNumber() {
        return onlineNum;
    }

    public static ConcurrentHashMap<String, Session> getSessionPools() {
        return sessionPools;
    }


}

转发:http://www.zhipin.com/web/geek/chat

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值