基于WebSocket的网页端即时通讯

基于WebSocket的网页端即时通讯

最近项目中需要用到一些即时通讯的相关技术,查阅了一些资料后发现有些示例不是让人很满意,所以博主写了一个demo,就怕以后会忘掉,也方便博友查看。

由于博主用的是SSM(springMVC+spring+MyBatis)框架,所以肯定要首选spring自带的WebSocket了。

我们先看一下最终实现的效果。
基于WebSocket的网页端即时通讯_第1张图片

1、这里两个用户用的接口分别是:http://localhost:8090/myProject/demo/webSocketTest/user001
表示user001用户,http://localhost:8090/myProject/demo/webSocketTest/user002表示user002用户。下面的controller里会讲到模拟登录。

基于WebSocket的网页端即时通讯_第2张图片

2、当user002用户断开连接时,再想user002用户发送消息时显示user002用户不在线!

基于WebSocket的网页端即时通讯_第3张图片

3、当user002用户再次连接时,两个用户又可以进行通讯了。


代码实现

1、初始页面的controller

package com.jh.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * webSocketDemo
 * @author JiHao
 *
 */
@Controller
@RequestMapping(value = "/demo")
public class MyWebSocketController {

    /**
     * @param userId 模拟登录,登录的用户名称
     */
    @RequestMapping(value = "/webSocketTest/{userId}")
    public String test(@PathVariable("userId") String userId, Model model) throws Exception {
        //把登录名称传给jsp页面
        model.addAttribute("userId", userId);
        return "demo/test";
    }

}

2、初始页面的jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>



  
    

    My JSP 'index.jsp' starting page

    
    
        
    
    

    
  

  
        webSocket Demo---- ${userId} 
发送给谁:
发送内容:

这里我把自己写的jsp代码全都复制了过来,仅供参考。
浏览器加载时调用的几个重要的WebSocket API示例
if(‘WebSocket’ in window){
websocket = new WebSocket(“ws://localhost:8090/myProject/websocketTest/” + ‘${userId}’);
console.log(“link success”);
}else{
alert(‘Not support websocket’);
}
websocket.onopen = function(){setMessageInnerHTML(“open”);};
websocket.onmessage = function(evt){setMessageInnerHTML(event.data);};
websocket.onclose = function(){setMessageInnerHTML(“close”);};
websocket.onerror = function(){setMessageInnerHTML(“error”);};
window.onbeforeunload = function(){websocket.close();};
浏览器中用到的几个重要的方法
1、function send(){} 发送消息的方法
2、function closeWebSocket(){} 关闭连接的方法
3、function connect(){} 重新建立连接的方法

3、创建@ServerEndpoint注解类

package com.jh.util.websocket;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

/**
 * MyWebSocket
 * @author JiHao
 *
 */
@ServerEndpoint(value = "/websocketTest/{userId}")
public class MyWebSocket {

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。但为了实现服务端与单一客户端通信,用Map来存放,其中Key可以为用户标识
    private static CopyOnWriteArraySet> mapSocket = new CopyOnWriteArraySet>();
    private static CopyOnWriteArraySet> mapSession = new CopyOnWriteArraySet>();

    //当前用户
    private String userId;

    /**
     * 连接时执行
     * @param userId 当前用户名称
     * @param session 当前用户session
     * @throws Exception
     */
    @OnOpen
    public void onOpen(@PathParam("userId") String userId,Session session) throws Exception {
        //存放当前用户的MyWebSocket对象
        Map wsSocket = new HashMap();
        //用map建立用户和当前用户的MyWebSocket对象关系
        wsSocket.put(userId, this);
        mapSocket.add(wsSocket);
        //存放当前用户的session对象
        Map wsSession = new HashMap();
        //用map建立用户和当前用户的session对象关系
        wsSession.put(userId, session);
        mapSession.add(wsSession);
        //在线数加1
        addOnlineCount();           
        this.userId = userId;
        System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
    }

    /**
     * 关闭时执行
     */
    @OnClose
    public void onClose(){
        //删除当前用户的MyWebSocket对象和session对象,并且人数减1
        removeCurrentUser();
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到消息时执行
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        System.out.println("来自客户端的消息:" + message);
        String[] messages = message.split("-f,t-");
        //接收者名称
        String toName = messages[0].trim();
        //发送给接收者的信息
        String toMessage = 1 >= messages.length ? "" : messages[1];
        //用户判断接收者是否存在
        boolean flag = false;
        //发消息
        for(Map item: mapSocket){             
            try {
                for (String key : item.keySet()) {
                    if(toName.equals(key)){
                        flag = true;
                        MyWebSocket myWebSocket = item.get(key);
                        myWebSocket.sendMessage(key, toMessage);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
        if(!flag){
            session.getBasicRemote().sendText(toName + "用户不在线!"); //回复用户
        }
//        //这里注释掉的内容是群发消息
//        for(Map item: mapSocket){             
//            try {
//              for (String key : item.keySet()) {
//                  MyWebSocket myWebSocket = item.get(key);
//                  myWebSocket.sendMessage(key, toMessage);
//              }
//            } catch (IOException e) {
//                e.printStackTrace();
//                continue;
//            }
//        }
    }

    /**
     * 发送消息。
     * @param message
     * @throws IOException
     */
    public void sendMessage(String toName, String toMessage) throws IOException{
        for(Map item: mapSession){             
            try {
                for (String string : item.keySet()) {
                    if(toName.equals(string)){
                        Session toSession = item.get(string);
                        toSession.getBasicRemote().sendText(toMessage);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    /**
     * 删除当前用户的MyWebSocket对象和session对象,并且人数减1
     */
    public void removeCurrentUser(){
        //删除当前用户的WebSocket
        for(Map item: mapSocket){             
            for (String string : item.keySet()) {
                if(userId.equals(string)){
                    mapSocket.remove(item);
                }
            }
         }
        //删除当前用户的session
        for(Map item: mapSession){             
            for (String string : item.keySet()) {
                if(userId.equals(string)){
                    mapSession.remove(item);
                }
            }
         }
        //在线数量减1
        subOnlineCount();
    }

    /**
     * 连接错误时执行
     */
    @OnError
    public void onError(Session session, Throwable error){
        System.out.println("用户id为:{}的连接发送错误" + this.userId);
        error.printStackTrace();
    }

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

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

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

}

这些代码可以复制直接使用,博主已经调试过了,代码里也做了相应的注释,博友可以仔细查看。
@ServerEndpoint注解类中主要使用的几个注解方法的监听函数
1、@OnOpen 当网络连接建立时触发该事件
2、@OnMessage 接收到服务器发来的消息的时触发的事件,也是通信中最重要的一个监听事件。
3、@OnClose 当websocket被关闭时触发该事件
4、@OnError 当websocket当网络发生错误时触发该事件

博主的这篇demo博文写的比较简单,如果有博友看的不是很清楚的,这里提供一个参考地址:
https://www.cnblogs.com/davidwang456/p/4786636.html

你可能感兴趣的:(Java/webSocket)