美文网首页
基于webSocket的网页在线聊天

基于webSocket的网页在线聊天

作者: Lobo_asd | 来源:发表于2019-08-06 16:45 被阅读0次

1、现在通过QQ、微信聊天已经非常普遍了,我们常用的网站中联系商家等也是链接到QQ中,一般不会自己去开发聊天模块,一来是因为浏览器权限有限,二来是安全性不高,开发一个完整系统的在线聊天难度系统也并不小。
但是也有些客户有这种网页单聊群聊的需求,我也就做了一个基于webSocket的网页在线聊天。
2、技术栈包括springboot+webSocket+Jquery,先展示一下效果。

![1ZOFE]RR9HK7F2]985~``SK.png](https://img.haomeiwen.com/i14217092/f8cc2d3661f3bb2d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![GXB{D_L7U]2DCGM2EYYE9.png](https://img.haomeiwen.com/i14217092/856d7b39280dea50.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

3、接下来直接贴代码,贴一下结构图:

![7H[IGUAG(95VR5UWY)IK.png

新建springboot项目后按照以下操作完成操作。
先导入Maven依赖:

<!--websocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
<!--lombok是用来做日志的-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

4、新建WebSocketConfig 类,用来处理socket异常,必须要有,否则会显示找不到socket服务器404错误

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

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

5、新建控制类WebSocketController,用来校验身份,我就没写实现了,跟我们今天做的关系不大。

import com.example.testchat2.server.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;

@RestController
public class WebSocketController {
    @Autowired
    WebSocketServer server;
    @PostMapping("/login")
    public String login(String username,String password) throws IOException {
        //TODO: 校验密码
//        server.sendInfo(username + "进入了聊天室!");
        return username;
    }
}

6、新建WebSocketServer ,socket服务器处理类,定义连接、关闭、发送消息时触发的功能。

import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@ServerEndpoint("/websocket/{userId}")
@Component
public class WebSocketServer {
    private  final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //创建一个线程安全的map
    private static Map<String,WebSocketServer> users = Collections.synchronizedMap(new HashMap());

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    //放入map中的key,用来表示该连接对象
    private String username;

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String username) {
        this.session=session;
        this.username =username;
        users.put(username,this);   //加入map中,为了测试方便使用username做key
        addOnlineCount();           //在线数加1
        System.out.println("连接成功");
        logger.info(username+"加入!当前在线人数为" + getOnlineCount());
        try {
            this.session.getBasicRemote().sendText("连接成功");
        } catch (IOException e) {
            logger.error("websocket IO异常");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        users.remove(this.username);  //从set中删除
        subOnlineCount();           //在线数减1
        logger.info("一个连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后触发的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message) throws IOException {
        /*String msg =username +": "+ message;
        sendMessageToSomeBody(username,message);
        sendInfo(msg);//群发消息*/
        logger.info("来自客户端的消息:" + message);
        //群发消息
        try {
            if (StringUtils.isEmpty(message)){
                return ;
            }
            //如果给所有人发消息携带@ALL, 给特定人发消息携带@xxx#message
            String[] split = message.split("#");
            if (split.length>1){
                String[] users = split[0].split("@");
                if (users.length<2){return;}
                String firstuser = users[1].trim();
                if (StringUtils.isEmpty(firstuser)||"ALL".equals(firstuser.toUpperCase())){
                    String msg =username +": "+ split[1];
                    sendInfo(msg);//群发消息
                }else{//给特定人员发消息
                    for (String user : users) {
                        if (!StringUtils.isEmpty(user.trim())){
                            sendMessageToSomeBody(user.trim(),split[1]);
                        }
                    }
                }
            }else{
                sendInfo(username +": "+message);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("发生错误 session: "+session);
        error.printStackTrace();
    }

    //    给特定人员发送消息
    public void sendMessageToSomeBody(String username,String message) throws IOException {
        if(users.get(username)==null){
            return;
        }
        users.get(username).session.getBasicRemote().sendText(message);
        this.session.getBasicRemote().sendText(this.username+"@"+username+": "+message);
    }

    /**
     * 群发自定义消息
     */
    public  void sendInfo(String message) throws IOException {
        for (WebSocketServer item : users.values()) {
            try {
                item.session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

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

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

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}

7、新建chat.html,只是一个简易的聊天界面,想要更为好看,就自己修改css和js吧。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
    <script type="application/javascript">
        var socket ;
        //登录过后初始化socket连接
        function initSocket(userId) {
            if(typeof(WebSocket) == "undefined") {
                console.log("您的浏览器不支持WebSocket");
            }else {
                console.log("您的浏览器支持WebSocket/websocket");
            }
            if ('WebSocket' in window) {
                //socket连接地址: 注意是ws协议
                socket = new WebSocket("ws://localhost:8080/websocket/" + userId);
            }else {
                alert('当前浏览器 Not support websocket')
            }
            socket.onopen = function() {
                console.log("Socket 已打开");
            };
            //获得消息事件
            socket.onmessage = function(msg) {
                var histroy = $("#history").val();
                $("#history").val(histroy+"\r\n"+msg.data);
                console.log($(msg));
            };
            //关闭事件
            socket.onclose = function() {
                console.log("Socket已关闭");
            };
            //错误事件
            socket.onerror = function() {
                alert("Socket发生了错误");
            }
            $(window).unload(function(){
                socket.close();
            });
        }
        //点击按钮发送消息
        function send() {
            console.log("发送消息: "+$("#msg").val());
            socket.send($("#msg").val());
        }
        //登录
        function login() {
            console.log("登录成功!");
            $.ajax({
                url: "/login",
                data: $("#loginForm").serialize(),
                type: "POST",
                success: function (userId) {
                    if (userId){
                        console.log("登录成功!");
                        console.log(userId);
                        initSocket(userId);
                    }
                }
            });
        }
    </script>
</head>
<body>

<form id="loginForm"  >
    用户名: <input name="username" id="username"><br>
    密&emsp;码: <input name="password" id=""password><br>
    <input type="button" onclick="login()" value="登录">
</form>

<textarea id="msg" placeholder="格式:@xxx#消息 , 或者@ALL#消息"  style="width: 500px;height: 50px" ></textarea>
<input type="button" onclick="send()" value="发送消息"  >
<br>
<textarea id="history"   style="width: 500px;height: 200px ; max-lines: 10" >
</textarea>
</body>
</html>

8、@xxx#消息 , 或者@ALL#消息,实现给某一个人发送消息或者给所有人发送消息。自己测试功能是否实现,socket前后端是否连通,我是通过360浏览器测试的,按住f12查看浏览器后台,找到具体问题。
9、本片文章只实现了单个房间的单聊和群聊,看懂代码你也可以写出只有两个人的单聊。下一篇文章会介绍多个房间的群聊,其实实现方案也差不多,了解了socket是如何发送消息给指定客户端后,单房间和多房间也就是思维逻辑的区别。

相关文章

网友评论

      本文标题:基于webSocket的网页在线聊天

      本文链接:https://www.haomeiwen.com/subject/kbaidctx.html