前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot入门建站全系列(二十七)WebSocket做简单的聊天室

SpringBoot入门建站全系列(二十七)WebSocket做简单的聊天室

作者头像
品茗IT
发布2020-05-28 16:07:57
9620
发布2020-05-28 16:07:57
举报
文章被收录于专栏:品茗IT品茗IT

SpringBoot入门建站全系列(二十七)WebSocket做简单的聊天室

一、概述

WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。

这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。

这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步JavaScript和XML(AJAX)请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。

WebSocket复用了HTTP的握手通道,使用ws或wss的统一资源标志符,类似于HTTPS。其中wss表示使用了TLS的Websocket。如:ws://example.com/wsapi或者wss://secure.example.com/wsapi

代码可以在SpringBoot组件化构建https://www.pomit.cn/java/spring/springboot.html中的WebSocket组件中查看,并下载。

**如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以<a

href="https://jq.qq.com/?_wv=1027&k=52sgH1J"

target="_blank">

加入我们的java学习圈,点击即可加入

</a>

,共同学习,节约学习时间,减少很多在学习中遇到的难题。**

二、配置

本文假设你已经引入spring-boot-starter-web。已经是个SpringBoot项目了,如果不会搭建,可以打开这篇文章看一看《SpringBoot入门建站全系列(一)项目建立》

2.1 Maven依赖

使用WebSocket需要引入spring-boot-starter-websocket。本文使用fastjson做json数据的传输。

代码语言:javascript
复制
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
</dependency>
2.2 配置文件

无需额外配置。

三、WebSocket配置

3.1 WebSocket处理相关配置

Springboot整合WebSocket需要配置websocket的监听url、配置WebSocketInterceptor(连接握手配置)、webSocketHandler(连接成功配置)。

SpringWebSocketConfig:

代码语言:javascript
复制
package com.cff.springbootwork.websocket.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

import com.cff.springbootwork.websocket.handler.WebSocketHandler;
import com.cff.springbootwork.websocket.handler.WebSocketInterceptor;

@Configuration
@EnableWebSocket
public class SpringWebSocketConfig implements WebSocketConfigurer {
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		registry.addHandler(webSocketHandler(), "/websocket").addInterceptors(new WebSocketInterceptor());
		registry.addHandler(webSocketHandler(), "/sockjs").addInterceptors(new WebSocketInterceptor()).withSockJS();
	}

	@Bean
	public WebSocketHandler webSocketHandler() {
		return new WebSocketHandler();
	}

}
3.2 WebSocket的session拦截处理

WebSocketInterceptor对WebSocket的连接进行过滤,可以对连接前和连接后自定义处理。

WebSocketInterceptor:

代码语言:javascript
复制
package com.cff.springbootwork.websocket.handler;

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

/**
 * websocket 握手处理器
 * @author fufei
 *
 */
@Component
public class WebSocketInterceptor extends HttpSessionHandshakeInterceptor {
	private Logger log = LoggerFactory.getLogger(this.getClass());

	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Map<String, Object> attributes) throws Exception {
		log.info("收到握手请求。");
		if (request instanceof ServletServerHttpRequest) {
			ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
			HttpSession session = servletRequest.getServletRequest().getSession(false);
			if (session == null) {
				return false;
			}
			// 使用userName区分WebSocketHandler,以便定向发送消息
			String userName = (String) session.getAttribute("userName");
			if (StringUtils.isEmpty(userName)) {
				return false;
			}
			log.info("获取到用户信息:{}", userName);
			attributes.put("userId", userName);
		}
		return super.beforeHandshake(request, response, wsHandler, attributes);
	}

	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Exception ex) {
		log.info("成功握手。");
		super.afterHandshake(request, response, wsHandler, ex);
	}

}
3.3 WebSocket消息处理

WebsocketHandler负责处理消息发送接收的逻辑:

代码语言:javascript
复制
package com.cff.springbootwork.websocket.handler;

import java.io.IOException;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.AbstractWebSocketMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.alibaba.fastjson.JSONObject;
import com.cff.springbootwork.websocket.dto.MessageDTO;
import com.cff.springbootwork.websocket.memory.WebSocketUser;

/**
 * websocket消息处理器
 * 
 * @author fufei
 *
 */
public class WebSocketHandler extends TextWebSocketHandler {
	protected Logger logger = LoggerFactory.getLogger(getClass());
	public static WebSocketUser users = new WebSocketUser();

	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		logger.info("收到websocket消息:{}", message.toString());
		super.handleTextMessage(session, message);
		String msg = message.getPayload();
		logger.info("收到websocket消息的消息体:{}", msg);
		if (!StringUtils.isEmpty(msg)) {
			MessageDTO messageDTO = JSONObject.parseObject(msg, MessageDTO.class);
			sendMessageToUser(messageDTO.getTargetUserName(), message);
		}
	}

	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		String userName = (String) session.getAttributes().get("userName");
		if (StringUtils.isEmpty(userName)) {
			logger.error("用户不能为空!");
		}

		WebSocketUser.add(userName, session);
		MessageDTO messageDTO = new MessageDTO();
		messageDTO.setFromUserName("");
		messageDTO.setTargetUserName(userName);
		messageDTO.setMessageType(MessageDTO.Type.TYPE_NEW.getMessageType());
		messageDTO.setMessage("欢迎您!");
		session.sendMessage(new TextMessage(JSONObject.toJSONString(messageDTO)));
	}

	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		String userName = (String) session.getAttributes().get("userName");
		if (StringUtils.isEmpty(userName)) {
			logger.error("用户不能为空!");
		}
		WebSocketUser.removeWebSocketSession(userName, session);
		super.afterConnectionClosed(session, status);
	}

	/**
	 * 给某个用户发送消息
	 *
	 * @param userName
	 * @param message
	 */
	public void sendMessageToUser(String userName, AbstractWebSocketMessage<?> message) {
		List<WebSocketSession> webUsers = WebSocketUser.getSessionByUserName(userName);
		if (webUsers == null || webUsers.size() == 0) {
			logger.error("发送给{},当前无session", userName);
			return;
		}
		logger.info("发送给{},当前session个数为:{}", userName, webUsers.size());

		for (int i = 0; i < webUsers.size(); i++) {
			WebSocketSession session = webUsers.get(i);
			try {
				if (!session.isOpen()) {
					WebSocketUser.removeWebSocketSession(userName, session);
				}
				session.sendMessage(message);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
3.4 用户与WebSocketSession对应

WebSocketUser是使用内存来存储用户:

代码语言:javascript
复制
package com.cff.springbootwork.websocket.memory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.util.CollectionUtils;
import org.springframework.web.socket.WebSocketSession;

public class WebSocketUser {
	private static Map<String, List<WebSocketSession>> userNameWebsession = new ConcurrentHashMap<>();

	public static void add(String userName, WebSocketSession webSocketSession) {
		userNameWebsession.computeIfAbsent(userName, v -> new ArrayList<WebSocketSession>()).add(webSocketSession);
	}

	/**
	 * 根据昵称拿WebSocketSession
	 * 
	 * @param nickName
	 * @return
	 */
	public static List<WebSocketSession> getSessionByUserName(String userName) {
		return userNameWebsession.get(userName);
	}

	/**
	 * 移除失效的WebSocketSession
	 * 
	 * @param webSocketSession
	 */
	public static void removeWebSocketSession(String userName, WebSocketSession webSocketSession) {
		if (webSocketSession == null)
			return;
		List<WebSocketSession> webSessoin = userNameWebsession.get(userName);
		if (webSessoin == null || CollectionUtils.isEmpty(webSessoin))
			return;
		webSessoin.remove(webSocketSession);
	}
	
	public static Set<String> getUserList(){
		return userNameWebsession.keySet();
	}
}

四、用户生成、查询及文件发送

如果我们想发送文件,需要上传文件后转为二进制数据并使用websocket发送。另外,这里使用setUser来产生用户,并提供获取当前用户,用户列表的接口。

代码语言:javascript
复制
package com.cff.springbootwork.websocket.web;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.socket.BinaryMessage;

import com.cff.springbootwork.websocket.dto.ResultModel;
import com.cff.springbootwork.websocket.handler.WebSocketHandler;
import com.cff.springbootwork.websocket.memory.WebSocketUser;

@RestController
@RequestMapping("/im")
public class WebSocketController {
	protected Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private WebSocketHandler websocketHandler;

	@RequestMapping(value = "/fileUpload")
	public ResultModel fileUpload(@RequestParam("userName") String userName, @RequestParam MultipartFile[] myfiles,
			HttpServletRequest request) {
		logger.info("收到发往用户[{}]的文件上传请求;文件数量:{}", userName, myfiles.length);

		int count = 0;
		for (MultipartFile myfile : myfiles) {
			if (myfile.isEmpty()) {
				count++;
			}
			logger.info("文件原名:{};文件类型:", myfile.getOriginalFilename(), myfile.getContentType());
			try (ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
					InputStream is = myfile.getInputStream();) {
				byte[] buff = new byte[100]; // buff用于存放循环读取的临时数据
				int rc = 0;
				while ((rc = is.read(buff, 0, 100)) > 0) {
					swapStream.write(buff, 0, rc);
				}
				byte[] in_b = swapStream.toByteArray(); // in_b为转换之后的结果
				logger.info("正在发送文件: ");
				websocketHandler.sendMessageToUser(userName, new BinaryMessage(in_b));
			} catch (IOException e) {
				logger.error("文件原名:{}", myfile.getOriginalFilename(), e);
				e.printStackTrace();
				count++;
				continue;
			}
		}
		return ResultModel.ok(count);
	}

	@RequestMapping(value = "/setUser")
	public ResultModel setUser(@RequestParam("userName") String userName, HttpServletRequest request) {
		logger.info("设置用户[{}]", userName);

		request.getSession().setAttribute("userName", userName);
		return ResultModel.ok();
	}
	
	@RequestMapping(value = "/user")
	public ResultModel user(HttpServletRequest request) {
		Object userName = request.getSession().getAttribute("userName");
		if(userName == null)return ResultModel.error("无用户");
		return ResultModel.ok(userName);
	}
	
	@RequestMapping(value = "/userList")
	public ResultModel userList() {
		return ResultModel.ok(WebSocketUser.getUserList());
	}
}

五、聊天室页面

为了实现我们的简单聊天功能,我们需要前端进行配合。

chat.html实现了简单的聊天室,支持文字、表情、文件等:

该html需要很多js配合,下面贴出html和websocket.js,其他js都是很普遍的js,如果需要我发送,加入群聊向群主索要。

代码语言:javascript
复制
<!DOCTYPE html>
<html>

<head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>品茗IT-WebSocket测试</title>

    <!-- CSS  -->
    <link href="https://lib.baomitu.com/material-design-icons/3.0.1/iconfont/material-icons.min.css" rel="stylesheet">
    <link href="https://lib.baomitu.com/materialize/0.100.2/css/materialize.min.css" type="text/css" rel="stylesheet" media="screen,projection"/>

  <style>
body { text-align:left; margin:0; font:normal 12px Verdana, Arial;
background:#FFEEFF } form { margin:0; font:normal 12px Verdana,
Arial; } table,input { font:normal 12px Verdana, Arial; }
a:link,a:visited{ text-decoration:none; color:#333333; } a:hover{
text-decoration:none; color:#FF6600 } #main { width:400px;
position:absolute; left:600px; top:100px; background:#EFEFFF;
text-align:left; filter:Alpha(opacity=90) } #ChatHead {
text-align:right; padding:3px; border:1px solid #003399;
background:#DCDCFF; font-size:20px; color:#3366FF; cursor:move; }
#ChatHead a:link,#ChatHead a:visited, { font-size:14px;
font-weight:bold; padding:0 3px } #ChatBody { border:1px solid
#003399; border-top:none; padding:2px; } #ChatContent {
height:200px; padding:6px; overflow-y:scroll; word-break: break-all
}#ChatBtn { border-top:1px solid #003399; padding:2px }
		
  </style>
</head>
<script type="text/javascript">
var ws = null;
var curUser=null;
var chatUser = null;
var imgName = null;
var fileImgSize = 0;
window.onbeforeunload = function()
{
	disconnect(ws);
}

function gs(d) {
	var t = document.getElementById(d);
	if (t) {
		return t.style;
	} else {
		return null;
	}
}
function gs2(d, a) {
	if (d.currentStyle) {
		var curVal = d.currentStyle[a]
	} else {
		var curVal = document.defaultView
				.getComputedStyle(d, null)[a]
	}
	return curVal;
}
function ChatHidden() {
	gs("ChatBody").display = "none";
}
function ChatShow() {
	gs("ChatBody").display = "";
}
function ChatClose() {
	gs("main").display = "none";
}
function ChatNew(userId) {
	gs("main").display = "";
	chatUser = userId;
	$("#ChatUsers").html(chatUser);
	$('.emotion').qqFace({

		id : 'facebox', 

		assign:'saytext', 

		path: './img/arclist/'	//表情存放的路径

	});
}
function ChatClear(obj) {
	$("#ChatContent").html("");
}

function ChatRead() {
	if(document.getElementById(chatUser)){
		document.getElementById(chatUser).setAttribute('src', './img/users.png');
	}
}

function ChatSend(obj) {
	var o = obj.ChatValue;
	var msg = replace_em(o.value);
	if (o.value.length > 0) {
		$("#ChatContent").append(
				"<p align=\"right\"><strong>" + curUser + "(我) :</strong>" + msg
						+ "</p>");
		var number = $("#ChatContent").scrollTop();
    	number += 16;
    	$("#ChatContent").scrollTop(number);
    	if(ws!=null){
    		var json={"fromUserName":curUser,"targetUserName":chatUser,"message":o.value,"messageType":"1000"};
    		// encodeURI(o.value)
    		console.log(json);
    		ws.send(JSON.stringify(json));
    	}
		o.value = '';
	}
	
	var img = obj.ChatFile;
	if (img.value.length > 0){
		$("#ChatContent").append(
				"<p align=\"right\"><strong>" + nickName + "(我) :</strong>" + img.value
						+ "</p><br/>");

		imgName = nickName+'(我)';
		fileImgSize = img.files.length;
		//alert(fileImgSize);
		$.ajaxFileUpload({
			//处理文件上传操作的服务器端地址(可以传参数,已亲测可用)
			url:'im/fileUpload?userId='+muserId,
			secureuri:true,                       //是否启用安全提交,默认为false 
			fileElementId:'ChatFile',           //文件选择框的id属性
			dataType:'text',                       //服务器返回的格式,可以是json或xml等
			success:function(data, status){        //服务器响应成功时的处理函数
				//$("#ChatContent").append("<p align=\"right\">" + data + "</p><br/>");
			},
			error:function(data, status, e){ //服务器响应失败时的处理函数
				$("#ChatContent").append('<p align=\"right\">图片上传失败,请重试!!</p><br/>');
				imgName = msgUser;
			}
		});
	}
}
	if (document.getElementById) {
		(function() {
			if (window.opera) {
				document.write("<input type='hidden' id='Q' value=' '>");
			}

			var n = 500;
			var dragok = false;
			var y, x, d, dy, dx;

			function move(e) {
				if (!e)
					e = window.event;
				if (dragok) {
					d.style.left = dx + e.clientX - x + "px";
					d.style.top = dy + e.clientY - y + "px";
					return false;
				}
			}

			function down(e) {
				if (!e)
					e = window.event;
				var temp = (typeof e.target != "undefined") ? e.target
						: e.srcElement;
				if (temp.tagName != "HTML" | "BODY"
						&& temp.className != "dragclass") {
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
				}
				if ('TR' == temp.tagName) {
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
				}

				if (temp.className == "dragclass") {
					if (window.opera) {
						document.getElementById("Q").focus();
					}
					dragok = true;
					temp.style.zIndex = n++;
					d = temp;
					dx = parseInt(gs2(temp, "left")) | 0;
					dy = parseInt(gs2(temp, "top")) | 0;
					x = e.clientX;
					y = e.clientY;
					document.onmousemove = move;
					return false;
				}
			}

			function up() {
				dragok = false;
				document.onmousemove = null;
			}

			document.onmousedown = down;
			document.onmouseup = up;

		})();
	}
	
	function toIndex(){
		window.location.href= contextPath + "/index.jsp";
	}

</script>
<body>
<div id="main" class="dragclass" onclick="ChatRead()" style="left: 400px; top: 200px;">
		<div id="ChatUsers" style="width:100px; padding:3px; font-size:15px;float:left; display:inline"></div>
		<div id="ChatHead">
			<a href="#" onclick="ChatHidden();">-</a> <a href="#"
				onclick="ChatShow();">+</a> <a href="#" onclick="ChatClose();">x</a>
		</div>
		<div id="ChatBody">
			<div id="ChatContent"></div>
			<div id="ChatBtn">
				<form action="" name="chat" method="post">
					<textarea name="ChatValue" id="saytext" rows="3" style="width: 350px"></textarea>
					<input name="Submit" type="button" value="发送"
						onclick="ChatSend(this.form);" />
					<input name="ClearMsg" type="button" value="清空记录"
						onclick="ChatClear(this.form);" />
					<input type="button" class="emotion" value="表情">
					<input id="ChatFile" type="file" name="myfiles"  multiple>   
				</form>
			</div>
		</div>
</div>
<div id="modalAddUser" class="modal modal-fixed-footer" style="max-width:400px;max-height:400px">
    <div class="modal-content">
      <h4>生成用户名</h4>
      	<div class="row center">
	     	<input class="browser-default searchInput" placeholder="请输入用户名" style="margin-top:50px;margin-left:20px;max-width:300px" id="catoryAddText" type="text" >
	     	
	 	</div>
	 	<div class="row center">				          		
     		<a class="waves-effect waves-light btn" id="userAddBtn" style="color:white;"><i class="material-icons" style="font-size:1.1rem">添用户</i></a>
     	</div>
    </div>
    <div class="modal-footer">
      <a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">关闭</a>
    </div>
</div>
<div align="left" style="margin-top: 50px;margin-left: 20px;">
	<p>欢迎您,
		<span id="userName">匿名用户</span>
	</p>
	<a id="addUser" class="btn waves-effect waves-light white cyan-text" style="border-radius: 40px;">添加用户</a>
	<p id="content"></p>
</div>
<script src="https://lib.baomitu.com/jquery/3.3.0/jquery.min.js"></script>
<script src="https://lib.baomitu.com/materialize/0.100.2/js/materialize.min.js"></script>
<script src="./js/websocket.js"></script>
<script src="./js/ajaxfileupload.js"></script>
<script src="./js/jquery-browser.js"></script>
<script src="./js/jquery.qqFace.js"></script>
<script>
function getUser(){
	$.ajax({
		type : "get",
		url : "im/user",
		dataType : "json",
		data : {} ,
		success : function(data) {
			if(data.errorCode == "0000"){
				$("#userName").html(data.data);
				curUser = data.data;
			}
		},
		error : function(XMLHttpRequest, textStatus, errorThrown) {
			alert(errorThrown);
		}
	});
}	
function addUser(userName){
	$.ajax({
		type : "post",
		url : "im/setUser",
		dataType : "json",
		data : {"userName":userName} ,
		success : function(data) {
			if(data.errorCode == "0000"){
				$("#userName").html(userName);
				curUser = data.data;
			}
		},
		error : function(XMLHttpRequest, textStatus, errorThrown) {
			alert(errorThrown);
		}
	});
}
function userList(){
	$.ajax({
		type : "get",
		url : "im/userList",
		dataType : "json",
		data : {} ,
		success : function(data) {
			if(data.errorCode == "0000"){
				var content = "";
				for(var i =0;i<data.data.length;i++){
					var userId = data.data[i];
					content += "<img src=\"./img/msgget.gif\" id=\""
						+ userId
						+ "\" alt=\"\" style=\"cursor: pointer\" width='40px' "
						+ "onclick=\"ChatNew('"+userId+"')\" />"
						+ userId
						+ "<br><br>";
				}
		    	$("#content").append(content);
			}
		},
		error : function(XMLHttpRequest, textStatus, errorThrown) {
			alert(errorThrown);
		}
	});
}

window.onbeforeunload = function(){
	disconnect(ws);
}
$(function () {
	$('.modal').modal({
		dismissible: true, // 点击模态外面模态消失关闭
		opacity: 0.1, // 相对于背景的不透明度
		in_duration: 300, // 显示特效的时间
		out_duration: 200, // 消失特效时间
		starting_top: '80%', // 启动时的样式属性
		ending_top: '20%', // 结束时的样式属性
		ready: function(modal, trigger) { // 模态加载完成触发事件
			
		},
		complete: function() { 
			
		} // 关闭时触发的事件
	});
	getUser();
	$("#addUser").click(function() {
    	$('#modalAddUser').modal('open');	
	});
	
	$("#userAddBtn").click(function() {
		var catory = $('#catoryAddText').val();
		addUser(catory);
	});
	userList();
	if (ws == null) {
		var url = getUrl();
	    //alert("url:"+url);  
	    if (!url) {  
	        return;  
	    }  
		console.log(url);
	    ws = new WebSocket(url);  
		connect(ws);
		ChatClose();
	}
});
</script>
</body>
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?e553ae2bb23494dee9b6f43415a1ce5a";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>

</html>

这个html需要websocket.js配合:

代码语言:javascript
复制
function getUrl() {     
	var urlPath = "/websocket";
	if (window.location.protocol == 'http:') {  
		url = 'ws://' + window.location.host + urlPath;  
	} else {  
		url = 'wss://' + window.location.host + urlPath;  
	}   
	return url;
} 

function disconnect(ws) {  
    if (ws != null) {  
        ws.close();  
        ws = null;  
    }  
}  

function replace_em(str){
	str = str.replace(/\[em_([0-9]*)\]/g,'<img src="../../img/arclist/$1.gif" border="0" />');
	return str;
}

function connect(ws) { 
    ws.onopen = function () {  
    	$("#ChatContent").append("<small>连接成功。。。</small><br>");
    };  
    ws.onmessage = function (event) {
    	if(typeof(event.data)=="string"){  
	    	var dataAll = event.data;
	    	var jsonData = JSON.parse(dataAll); 
	    	console.log(jsonData);
	    	var msgType = jsonData.messageType;
	    	if(msgType == "0000"){
	    		$("#ChatContent").append("<strong>系统消息:</strong>" + jsonData.message + "<br>");
	    	}else{
	    		var data = jsonData.message;
		    	var userId = jsonData.fromUserName;
		    	var msg = jsonData.message;
		    	var result = replace_em(msg);
		    	if(document.getElementById(userId)){
		    		document.getElementById(userId).setAttribute('src', './img/msgget.gif');
			    	var number = $("#ChatContent").scrollTop();
		    		//var number = $("#ChatContent").height();
			    	number += 15;
			    	$("#ChatContent").scrollTop(number);
			    	$("#ChatContent").append("<strong>"+userId+" :</strong>" + result + "<br>");
		    	}else{
		    		var content = "<img src=\"./img/msgget.gif\" id=\""
						+ userId
						+ "\" alt=\"\" style=\"cursor: pointer\" width='40px' "
						+ "onclick=\"ChatNew()\" />"
						+ userId
						+ "<br><br>";
			    	$("#content").append(content);
			    	$("#ChatContent").append("<strong>"+userId+" :</strong>" + result + "<br>");
		    	}
		    	
	    	}
	    }else{  
    	  var reader = new FileReader();  
    	  reader.onload = function(event){  
    	       if(event.target.readyState == FileReader.DONE){  
    	            var url = event.target.result;  
    	            if (imgName != msgUser){
    	            	$("#ChatContent").append("<p align=\"right\"><strong>"+imgName+" :</strong>"+"<img src = "+url+" width='100px'/></p><br>");  
    	       		}else{
    	            	$("#ChatContent").append("<strong>"+imgName+" :</strong>"+"<img src = "+url+" width='100px'/><br>");  
    	       		}
    	            if (fileImgSize != 0){
    	            	fileImgSize = fileImgSize - 1;
    	            }else{
    	            	imgName = msgUser;
    	            }
    	       }
    	   }  
	      reader.readAsDataURL(event.data);  
	    }  
    };  
    ws.onclose = function (event) {  
        //alert('网络连接失败!');  
    };  
} 

聊天室界面如下:

在这里插入图片描述

六、过程中用到的其他实体

MessageDTO:

代码语言:javascript
复制
package com.cff.springbootwork.websocket.dto;

public class MessageDTO {
	private String fromUserName;
	private String targetUserName;
	private String message;
	private String messageType;

	public String getFromUserName() {
		return fromUserName;
	}

	public void setFromUserName(String fromUserName) {
		this.fromUserName = fromUserName;
	}

	public String getTargetUserName() {
		return targetUserName;
	}

	public void setTargetUserName(String targetUserName) {
		this.targetUserName = targetUserName;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public String getMessageType() {
		return messageType;
	}

	public void setMessageType(String messageType) {
		this.messageType = messageType;
	}

	public static enum Type {
		TYPE_NEW("0000"), TYPE_TEXT("1000"), TYPE_BYTE("1001");
		private String messageType;

		Type(String messageType) {
			this.messageType = messageType;
		}

		public String getMessageType() {
			return messageType;
		}

		public void setMessageType(String messageType) {
			this.messageType = messageType;
		}

	}
}

ResultModel:

代码语言:javascript
复制
package com.cff.springbootwork.websocket.dto;

/**
 * @author cff
 */
public class ResultModel {

	private String errorCode;
	private String message;
	private Object data;

	public ResultModel() {

	}

	public ResultModel(String errorCode, String message) {
		this.errorCode = errorCode;
		this.message = message;
	}

	public ResultModel(String errorCode, String message, Object data) {
		this.errorCode = errorCode;
		this.message = message;
		this.data = data;
	}

	public String geterrorCode() {
		return errorCode;
	}

	public void seterrorCode(String errorCode) {
		this.errorCode = errorCode;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public Object getData() {
		return data;
	}

	public void setData(Object data) {
		this.data = data;
	}

	public static ResultModel ok() {
		return new ResultModel("0000","成功");
	}

	public static ResultModel ok(Object data) {
		return new ResultModel("0000","成功", data);
	}

	public static ResultModel error() {
		return new ResultModel("1111","失败");
	}

	public static ResultModel error(String msg) {
		return new ResultModel("1111","失败", msg);
	}

	public static ResultModel error(String msg, Object data) {
		return new ResultModel("1111", msg, data);
	}
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SpringBoot入门建站全系列(二十七)WebSocket做简单的聊天室
  • 一、概述
  • 二、配置
    • 2.1 Maven依赖
      • 2.2 配置文件
      • 三、WebSocket配置
        • 3.1 WebSocket处理相关配置
          • 3.2 WebSocket的session拦截处理
            • 3.3 WebSocket消息处理
              • 3.4 用户与WebSocketSession对应
              • 四、用户生成、查询及文件发送
              • 五、聊天室页面
              • 六、过程中用到的其他实体
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档