java使用websocket实现简单的群聊天功能(spring mvc)

java使用websocket实现简单的群聊天功能(spring mvc)

2854发表于2017-09-10

最近出去团队聚餐,看到店家用的点餐产品还挺不错的。

一桌的每个人只要扫一扫桌位上的二维码都会进入一个H5的点餐页面,而且每个人都可以同时点菜,所点的菜品数据是共享的,都可以看到所点的全部菜品,也显示是谁点的(微信头像和名字)。页面的即时性也很高,只要有人一点,其他人立刻就能看到数据的变化。

我看到这个用户体验,不得不为这个产品点这个赞。作为一个“接地气”技术人,为其点完赞后也会默默的想这里面所用的到技术。我想应该是用到websocket技术,H5页面使用websockect与服务器建立长链接,当有数据变化后会同时把最新的数据推送给扫描了这个二维码的所有websocket链接。其实这也不难,不是什么高深的技术,websocket是早就有的技术,由于很多低版本的浏览器不支持它,所以在PC上应用得不是很广泛。

不过H5天生就是支持websocket的,那么今天我就使用java和websocket实现简单的群聊天功能,采用spring mvc做基础页面,测试使用chrome浏览器。

一、入口Controller

首先,我们要创建一个controller返回发送消息的页面

/**
 * @com.lanhusoft.controllers.WebSocketController
 * @Description 类描述
 *
 * @Author lanhu
 * @Version 2017/9/7  
 * @Copyright 蓝狐软件工作室
 **/
package com.lanhusoft.controllers;

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

@Controller
@RequestMapping("test")
public class WebSocketController {

    @RequestMapping("websocket")
    public String say(){
        return "web-sockect";
    }
}


二、发送消息页面

编写发送消息页面web-socket.jsp


<%@ page language="java" pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
    <title>Testing websockets</title>
</head>
<body>
<div>
    <label>消息:</label><input type="text" id="msg">
</div>
<div>
    <input type="submit" value="发送" onclick="start()" />
</div>
<div id="messages"></div>
<script type="text/javascript">
    var webSocket =
            new WebSocket('ws://localhost:8083/Sharding/websocketTest');
    webSocket.onerror = function(event) {
        onError(event)
    };

    webSocket.onopen = function(event) {
        onOpen(event)
    };

    webSocket.onmessage = function(event) {
        onMessage(event)
    };

    function onMessage(event) {
        document.getElementById('messages').innerHTML
                += '<br />' + event.data;
    }

    function onOpen(event) {
        document.getElementById('messages').innerHTML
                = 'Connection established';
    }

    function onError(event) {
        alert(event.data);
    }

    function start() {
        var msg=document.getElementById("msg").value;
        webSocket.send(msg);
        return false;
    }
</script>
</body>
</html>
其中,ws://localhost:8083/Sharding/websocketTest是websoscket的服务器地址。


三、修改pom.xml

写websocket服务端代码需要websocket相关的jar包,在pom.xml中加入websocket的maven依赖。


<dependency>
	<groupId>javax.websocket</groupId>
	<artifactId>javax.websocket-api</artifactId>
	<version>1.1</version>
	<scope>provided</scope>
</dependency>


四、websocket接口编写

package com.lanhusoft.websocket;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CopyOnWriteArraySet;

@ServerEndpoint("/websocketTest")
public class WebSocketTest {
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
    private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>();
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    /**
     * 连接建立成功调用的方法
     * @param session  可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(Session session){
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
    }

    @OnMessage
    public void onMessage(String message, Session session) throws IOException, InterruptedException {

        Date currentTime = new Date();
        SimpleDateFormat formatter=new SimpleDateFormat("MM-dd HH:mm:ss");

        String msg=String.format("%s:来自客户[%d]端的消息:  %s",formatter.format(currentTime), Thread.currentThread().getId(), message);
        System.out.println(msg);

        //群发消息
        for(WebSocketTest item: webSocketSet){
            try {
                item.sendMessage(msg);
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }

       /* // Print the client message for testing purposes
        System.out.println("Received: " + message);

        // Send the first message to the client
        session.getBasicRemote().sendText("This is the first server message");

        // Send 3 messages to the client every 5 seconds
        int sentMessages = 0;
        while (sentMessages < 3) {
            Thread.sleep(5000);
            session.getBasicRemote().sendText("This is an intermediate server message. Count: " + sentMessages);
            sentMessages++;
        }

        // Send a final message to the client
        session.getBasicRemote().sendText("This is the last server message");*/
    }

    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException{
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }

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

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

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


五、运行测试

周时打开两个tab或者用两个浏览器打开网页分别输入要发送的内容发送,切换tab可以到不用刷新页面自动就把新消息显示到页面上了。

如下图:

六、总结

以上就实现了一个基于websocket简单的群聊天的功能。

小编蓝狐