基于WebSocket的网页端即时通讯
基于WebSocket的网页端即时通讯
最近项目中需要用到一些即时通讯的相关技术,查阅了一些资料后发现有些示例不是让人很满意,所以博主写了一个demo,就怕以后会忘掉,也方便博友查看。
由于博主用的是SSM(springMVC+spring+MyBatis)框架,所以肯定要首选spring自带的WebSocket了。
我们先看一下最终实现的效果。
1、这里两个用户用的接口分别是:http://localhost:8090/myProject/demo/webSocketTest/user001
表示user001用户,http://localhost:8090/myProject/demo/webSocketTest/user002表示user002用户。下面的controller里会讲到模拟登录。
2、当user002用户断开连接时,再想user002用户发送消息时显示user002用户不在线!
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+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<script type="text/javascript">
//判断当前浏览器是否支持WebSocket
if('WebSocket' in window){
websocket = new WebSocket("ws://localhost:8090/myProject/websocketTest/" + '${userId}');
console.log("link success");
}else{
alert('Not support websocket');
}
//连接发生错误的回调方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
//连接成功建立的回调方法
websocket.onopen = function(event){
setMessageInnerHTML("open");
};
//接收到消息的回调方法
websocket.onmessage = function(event){
console.log(event.data);
setMessageInnerHTML(event.data);
};
//连接关闭的回调方法
websocket.onclose = function(){
setMessageInnerHTML("close");
};
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
websocket.close();
};
//将消息显示在网页上
function setMessageInnerHTML(innerHTML){
document.getElementById('returnMessage').innerHTML += innerHTML + '<br/>';
}
//关闭连接
function closeWebSocket(){
websocket.close();
document.getElementById('send').disabled = true;
document.getElementById('close').disabled = true;
document.getElementById('connect').disabled = false;
}
//发送消息
function send(){
//接收者名称
var toName = document.getElementById('toName').value;
if('' == toName){
alert("请填写接收者");
return;
}
//发送的消息
var message = document.getElementById('message').value;
if('' == message){
alert("请填写发送信息");
return;
}
websocket.send(toName+"-f,t-"+message);
}
function connect() {
//判断当前浏览器是否支持WebSocket
if('WebSocket' in window){
websocket = new WebSocket("ws://localhost:8090/myProject/websocketTest/" + '${userId}');
console.log("link success");
document.getElementById('send').disabled = false;
document.getElementById('close').disabled = false;
document.getElementById('connect').disabled = true;
}else{
alert('Not support websocket');
}
//连接发生错误的回调方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
//连接成功建立的回调方法
websocket.onopen = function(event){
setMessageInnerHTML("open");
};
//接收到消息的回调方法
websocket.onmessage = function(event){
console.log(event.data);
setMessageInnerHTML(event.data);
};
//连接关闭的回调方法
websocket.onclose = function(){
setMessageInnerHTML("close");
};
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
websocket.close();
};
}
</script>
</head>
<body>
webSocket Demo---- ${userId} <br />
发送给谁:<input id="toName" type="text" /><br>
发送内容:<input id="message" type="text" /><br>
<button id="send" onclick="send()"> Send </button>
<button id="close" onclick="closeWebSocket()"> Close </button>
<button id="connect" onclick="connect();" disabled="disabled">Connect</button>
<div id="returnMessage"></div>
</body>
</html>
这里我把自己写的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<Map<String, MyWebSocket>> mapSocket = new CopyOnWriteArraySet<Map<String, MyWebSocket>>();
private static CopyOnWriteArraySet<Map<String, Session>> mapSession = new CopyOnWriteArraySet<Map<String, Session>>();
//当前用户
private String userId;
/**
* 连接时执行
* @param userId 当前用户名称
* @param session 当前用户session
* @throws Exception
*/
@OnOpen
public void onOpen(@PathParam("userId") String userId,Session session) throws Exception {
//存放当前用户的MyWebSocket对象
Map<String, MyWebSocket> wsSocket = new HashMap<String, MyWebSocket>();
//用map建立用户和当前用户的MyWebSocket对象关系
wsSocket.put(userId, this);
mapSocket.add(wsSocket);
//存放当前用户的session对象
Map<String, Session> wsSession = new HashMap<String, Session>();
//用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<String, MyWebSocket> 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<String, MyWebSocket> 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<String, Session> 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<String, MyWebSocket> item: mapSocket){
for (String string : item.keySet()) {
if(userId.equals(string)){
mapSocket.remove(item);
}
}
}
//删除当前用户的session
for(Map<String, Session> 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
上一篇: centos7 安装obs studio
下一篇: unity的血条显示