ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

基于javaweb项目的SSM即时在线聊天简易系统.rar

2021-11-30 09:30:00  阅读:207  来源: 互联网

标签:entry javaweb webSocketSession 用户 SSM rar loginUser msg new


本项目基于SSM开发的即时在线聊天室系统,主要实习功能:登录记入会话信息以及登录后,记录在线人数、消息信息、记录时间,是一个比较简易的聊天室系统,对于想学习了解聊天室框架结构的,有很大的帮助,这个项目可以直接用idea或者eclipse开发工具直接打开,没有接入数据库,数据都存储在session中,所以不需要配置数据库文件,拿来就可以操作,如想深入学些,可事后配上数据源,对数据进行永久操作。

聊天登录主界面:
在这里插入图片描述
主要代码:

// 登录进入聊天主页面
	@RequestMapping(value = "login", method = RequestMethod.POST)
	public ModelAndView login(User loginUser, HttpServletRequest request) {
		HttpSession session = request.getSession();
		// 登录操作
		// 判断是否是一个已经登录的用户,没有则登录
		if (null != session.getAttribute("loginUser")) {
			// 清除旧的用户
			session.removeAttribute("loginUser");
		}
		// 新登录,需要构建一个用户
		// 随机生成一个用户
		String id = UUID.randomUUID().toString();
		loginUser.setId(id);
		// 将用户放入session
		session.setAttribute("loginUser", loginUser);

		// 将登录信息放入数据库,便于协查跟踪聊天者
		System.out.println("新用户诞生了:" + loginUser);
		return new ModelAndView("redirect:mainpage");
	}

聊天室界面:
在这里插入图片描述
代码如下:

// 跳转到聊天室页面
	@RequestMapping(value = "mainpage", method = RequestMethod.GET)
	public ModelAndView mainpage(HttpServletRequest request) {
		//判断,如果没有session,则跳到登录页面
		HttpSession session = request.getSession();
		if(null==session.getAttribute("loginUser")){
			return new ModelAndView("login");
		}else{
			return new ModelAndView("main");
		}
	}

所用到的工具类:
ChatHandshakeInterceptor工具类建立链接操作

/**
 * websocket的链接建立是基于http握手协议,我们可以添加一个拦截器处理握手之前和握手之后过程
 * @author BoBo
 *
 */
@Component
public class ChatHandshakeInterceptor implements HandshakeInterceptor{

	/**
     * 握手之前,若返回false,则不建立链接
     */
	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Map<String, Object> attributes) throws Exception {
		if (request instanceof ServletServerHttpRequest) {
			ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
			HttpSession session = servletRequest.getServletRequest().getSession(false);
			//如果用户已经登录,允许聊天
			if(session.getAttribute("loginUser")!=null){
				//获取登录的用户
				User loginUser=(User)session.getAttribute("loginUser") ;
				//将用户放入socket处理器的会话(WebSocketSession)中
				attributes.put("loginUser", loginUser);
				System.out.println("Websocket:用户[ID:" + (loginUser.getId() + ",Name:"+loginUser.getNickname()+"]要建立连接"));
			}else{
				//用户没有登录,拒绝聊天
				//握手失败!
				System.out.println("--------------握手已失败...");
				return false;
			}
		}
		System.out.println("--------------握手开始...");
		return true;
	}

	/**
     * 握手之后
     */
	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Exception exception) {
		System.out.println("--------------握手成功啦...");
	}

}

ChatWebSocketHandler工具类WebSocket处理器

/**
 * 
 * 说明:WebSocket处理器
 */
@Component("chatWebSocketHandler")
public class ChatWebSocketHandler implements WebSocketHandler {
	
	//在线用户的SOCKETsession(存储了所有的通信通道)
	public static final Map<String, WebSocketSession> USER_SOCKETSESSION_MAP;
	
	//存储所有的在线用户
	static {
		USER_SOCKETSESSION_MAP = new HashMap<String, WebSocketSession>();
	}
	
	/**
	 * webscoket建立好链接之后的处理函数--连接建立后的准备工作
	 */
	@Override
	public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
		//将当前的连接的用户会话放入MAP,key是用户编号
		User loginUser=(User) webSocketSession.getAttributes().get("loginUser");
		USER_SOCKETSESSION_MAP.put(loginUser.getId(), webSocketSession);
		
		//群发消息告知大家
		Message msg = new Message();
		msg.setText("欢迎【"+loginUser.getNickname()+"】上线!");
		msg.setDate(new Date());
		//获取所有在线的WebSocketSession对象集合
		Set<Entry<String, WebSocketSession>> entrySet = USER_SOCKETSESSION_MAP.entrySet();
		//将最新的所有的在线人列表放入消息对象的list集合中,用于页面显示
		for (Entry<String, WebSocketSession> entry : entrySet) {
			msg.getUserList().add((User)entry.getValue().getAttributes().get("loginUser"));
		}
		
		//将消息转换为json
		TextMessage message = new TextMessage(GsonUtils.toJson(msg));
		//群发消息
		sendMessageToAll(message);
		
	}

	@Override
	/**
     * 客户端发送服务器的消息时的处理函数,在这里收到消息之后可以分发消息
     */
	//处理消息:当一个新的WebSocket到达的时候,会被调用(在客户端通过Websocket API发送的消息会经过这里,然后进行相应的处理)
	public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> message) throws Exception {
		//如果消息没有任何内容,则直接返回
		if(message.getPayloadLength()==0)return;
		//反序列化服务端收到的json消息
		Message msg = GsonUtils.fromJson(message.getPayload().toString(), Message.class);
		msg.setDate(new Date());
		//处理html的字符,转义:
		String text = msg.getText();
		//转换为HTML转义字符表示
		String htmlEscapeText = HtmlUtils.htmlEscape(text);
		msg.setText(htmlEscapeText);
		System.out.println("消息(可存数据库作为历史记录):"+message.getPayload().toString());
		//判断是群发还是单发
		if(msg.getTo()==null||msg.getTo().equals("-1")){
			//群发
			sendMessageToAll(new TextMessage(GsonUtils.toJson(msg)));
		}else{
			//单发
			sendMessageToUser(msg.getTo(), new TextMessage(GsonUtils.toJson(msg)));
		}
	}

	@Override
	/**
     * 消息传输过程中出现的异常处理函数
     * 处理传输错误:处理由底层WebSocket消息传输过程中发生的异常
     */
	public void handleTransportError(WebSocketSession webSocketSession, Throwable exception) throws Exception {
		// 记录日志,准备关闭连接
		System.out.println("Websocket异常断开:" + webSocketSession.getId() + "已经关闭");
		//一旦发生异常,强制用户下线,关闭session
		if (webSocketSession.isOpen()) {
			webSocketSession.close();
		}
		
		//群发消息告知大家
		Message msg = new Message();
		msg.setDate(new Date());
		
		//获取异常的用户的会话中的用户编号
		User loginUser=(User)webSocketSession.getAttributes().get("loginUser");
		//获取所有的用户的会话
		Set<Entry<String, WebSocketSession>> entrySet = USER_SOCKETSESSION_MAP.entrySet();
		//并查找出在线用户的WebSocketSession(会话),将其移除(不再对其发消息了。。)
		for (Entry<String, WebSocketSession> entry : entrySet) {
			if(entry.getKey().equals(loginUser.getId())){
				msg.setText("万众瞩目的【"+loginUser.getNickname()+"】已经退出。。。!");
				//清除在线会话
				USER_SOCKETSESSION_MAP.remove(entry.getKey());
				//记录日志:
				System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
				break;
			}
		}
		
		//并查找出在线用户的WebSocketSession(会话),将其移除(不再对其发消息了。。)
		for (Entry<String, WebSocketSession> entry : entrySet) {
			msg.getUserList().add((User)entry.getValue().getAttributes().get("loginUser"));
		}
		
		TextMessage message = new TextMessage(GsonUtils.toJson(msg));
		sendMessageToAll(message);
		
	}

	@Override
	/**
     * websocket链接关闭的回调
     * 连接关闭后:一般是回收资源等
     */
	public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
		// 记录日志,准备关闭连接
		System.out.println("Websocket正常断开:" + webSocketSession.getId() + "已经关闭");
		
		//群发消息告知大家
		Message msg = new Message();
		msg.setDate(new Date());
		
		//获取异常的用户的会话中的用户编号
		User loginUser=(User)webSocketSession.getAttributes().get("loginUser");
		Set<Entry<String, WebSocketSession>> entrySet = USER_SOCKETSESSION_MAP.entrySet();
		//并查找出在线用户的WebSocketSession(会话),将其移除(不再对其发消息了。。)
		for (Entry<String, WebSocketSession> entry : entrySet) {
			if(entry.getKey().equals(loginUser.getId())){
				//群发消息告知大家
				msg.setText("万众瞩目的【"+loginUser.getNickname()+"】已经有事先走了,大家继续聊...");
				//清除在线会话
				USER_SOCKETSESSION_MAP.remove(entry.getKey());
				//记录日志:
				System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
				break;
			}
		}
		
		//并查找出在线用户的WebSocketSession(会话),将其移除(不再对其发消息了。。)
		for (Entry<String, WebSocketSession> entry : entrySet) {
			msg.getUserList().add((User)entry.getValue().getAttributes().get("loginUser"));
		}
		
		TextMessage message = new TextMessage(GsonUtils.toJson(msg));
		sendMessageToAll(message);
	}

	@Override
	 /**
     * 是否支持处理拆分消息,返回true返回拆分消息
     */
	//是否支持部分消息:如果设置为true,那么一个大的或未知尺寸的消息将会被分割,并会收到多次消息(会通过多次调用方法handleMessage(WebSocketSession, WebSocketMessage). )
	//如果分为多条消息,那么可以通过一个api:org.springframework.web.socket.WebSocketMessage.isLast() 是否是某条消息的最后一部分。
	//默认一般为false,消息不分割
	public boolean supportsPartialMessages() {
		return false;
	}

	/**
	 * 
	 * 说明:给某个人发信息
	 * @param id
	 * @param message
	 * @author 传智.BoBo老师
	 * @throws IOException 
	 * @time:2016年10月27日 下午10:40:52
	 */
	private void sendMessageToUser(String id, TextMessage message) throws IOException{
		//获取到要接收消息的用户的session
		WebSocketSession webSocketSession = USER_SOCKETSESSION_MAP.get(id);
		if (webSocketSession != null && webSocketSession.isOpen()) {
			//发送消息
			webSocketSession.sendMessage(message);
		}
	}
	
	/**
	 * 
	 * 说明:群发信息:给所有在线用户发送消息
	 * @author 传智.BoBo老师
	 * @time:2016年10月27日 下午10:40:07
	 */
	private void sendMessageToAll(final TextMessage message){
		//对用户发送的消息内容进行转义
		
		//获取到所有在线用户的SocketSession对象
		Set<Entry<String, WebSocketSession>> entrySet = USER_SOCKETSESSION_MAP.entrySet();
		for (Entry<String, WebSocketSession> entry : entrySet) {
			//某用户的WebSocketSession
			final WebSocketSession webSocketSession = entry.getValue();
			//判断连接是否仍然打开的
			if(webSocketSession.isOpen()){
				//开启多线程发送消息(效率高)
				new Thread(new Runnable() {
					public void run() {
						try {
							if (webSocketSession.isOpen()) {
								webSocketSession.sendMessage(message);
							}
						} catch (IOException e) {
							e.printStackTrace();
						}
					}

				}).start();
				
			}
		}
	}
	
}

WebSocketConfig 工具类WebScoket配置处理器

/**
 * 
 * 说明:WebScoket配置处理器
 * 把处理器和拦截器注册到spring websocket中
 */
@Component("webSocketConfig")
//配置开启WebSocket服务用来接收ws请求
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

	//注入处理器
	@Autowired
	private ChatWebSocketHandler webSocketHandler;
	@Autowired
	private ChatHandshakeInterceptor chatHandshakeInterceptor;

	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		//添加一个处理器还有定义处理器的处理路径
		registry.addHandler(webSocketHandler, "/ws").addInterceptors(chatHandshakeInterceptor);
		/*
		 * 在这里我们用到.withSockJS(),SockJS是spring用来处理浏览器对websocket的兼容性,
		 * SockJS能根据浏览器能否支持websocket来提供三种方式用于websocket请求,
		 * 三种方式分别是 WebSocket, HTTP Streaming以及 HTTP Long Polling
		 */
		registry.addHandler(webSocketHandler, "/ws/sockjs").addInterceptors(chatHandshakeInterceptor).withSockJS();
	}
	

}

在这里插入图片描述

项目源码下载地址:请点击》》》

标签:entry,javaweb,webSocketSession,用户,SSM,rar,loginUser,msg,new
来源: https://blog.csdn.net/weixin_40228600/article/details/121624580

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有