javaweb与websocket实现在线聊天功能总结

技术从一开始ajax轮询后来改成websocket 碰到的一些问题的处理:

websocket的pom依赖

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>4.0.5.RELEASE</version>
        </dependency>

首先是配置处理器

import javax.annotation.Resource;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 * WebScoket配置处理器
 * @author Goofy
 * @Date 2015年6月11日 下午1:15:09
 */
@Component
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {

	@Resource
	MyWebSocketHandler handler;

	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		registry.addHandler(handler, "/ws").addInterceptors(new HandShake());

		registry.addHandler(handler, "/ws/sockjs").addInterceptors(new HandShake()).withSockJS();
	}

}

2.请求所经过的握手拦截器 主要用来将ServerHttpRequest里的session的用户信息存放在attributes里 到处理的handle中 会自动存入websocketsession的attribute里

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

/**
 * Socket建立连接(握手)和断开
 *
 * @author Goofy
 * @Date 2015年6月11日 下午2:23:09
 */
public class HandShake implements HandshakeInterceptor {

	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
		System.out.println("Websocket:用户[ID:" + ((ServletServerHttpRequest) request).getServletRequest().getSession(false).getAttribute("uid") + "]已经建立连接");
		if (request instanceof ServletServerHttpRequest) {
			ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
			HttpSession session = servletRequest.getServletRequest().getSession(false);
			// 标记用户
			Long uid = (Long) session.getAttribute("uid");
			if(uid!=null){
				attributes.put("uid", uid);
			}else{
				return false;
			}
		}
		return true;
	}

	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
	}

}

Socket处理器 处理连接通信 或错误 关闭等

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.xdemo.example.websocket.entity.Message;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
 * Socket处理器
 *
 * @author Goofy
 * @Date 2015年6月11日 下午1:19:50
 */
@Component
public class MyWebSocketHandler implements WebSocketHandler {
	public static final Map<Long, WebSocketSession> userSocketSessionMap;

	static {
		userSocketSessionMap = new HashMap<Long, WebSocketSession>();
	}

	/**
	 * 建立连接后
	 */
	public void afterConnectionEstablished(WebSocketSession session)
			throws Exception {
		Long uid = (Long) session.getAttributes().get("uid");
		if (userSocketSessionMap.get(uid) == null) {
			userSocketSessionMap.put(uid, session);
		}
	}

	/**
	 * 消息处理,在客户端通过Websocket API发送的消息会经过这里,然后进行相应的处理
	 */
	public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
			if(message.getPayloadLength()==0)return;
			Message msg=new Gson().fromJson(message.getPayload().toString(),Message.class);
			msg.setDate(new Date());
			sendMessageToUser(msg.getTo(), new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
	}

	/**
	 * 消息传输错误处理
	 */
	public void handleTransportError(WebSocketSession session,
			Throwable exception) throws Exception {
		if (session.isOpen()) {
			session.close();
		}
		Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap
				.entrySet().iterator();
		// 移除Socket会话
		while (it.hasNext()) {
			Entry<Long, WebSocketSession> entry = it.next();
			if (entry.getValue().getId().equals(session.getId())) {
				userSocketSessionMap.remove(entry.getKey());
				System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
				break;
			}
		}
	}

	/**
	 * 关闭连接后
	 */
	public void afterConnectionClosed(WebSocketSession session,
			CloseStatus closeStatus) throws Exception {
		System.out.println("Websocket:" + session.getId() + "已经关闭");
		Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap
				.entrySet().iterator();
		// 移除Socket会话
		while (it.hasNext()) {
			Entry<Long, WebSocketSession> entry = it.next();
			if (entry.getValue().getId().equals(session.getId())) {
				userSocketSessionMap.remove(entry.getKey());
				System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
				break;
			}
		}
	}

	public boolean supportsPartialMessages() {
		return false;
	}

	/**
	 * 给所有在线用户发送消息
	 *
	 * @param message
	 * @throws IOException
	 */
	public void broadcast(final TextMessage message) throws IOException {
		Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap
				.entrySet().iterator();

		// 多线程群发
		while (it.hasNext()) {

			final Entry<Long, WebSocketSession> entry = it.next();

			if (entry.getValue().isOpen()) {
				// entry.getValue().sendMessage(message);
				new Thread(new Runnable() {

					public void run() {
						try {
							if (entry.getValue().isOpen()) {
								entry.getValue().sendMessage(message);
							}
						} catch (IOException e) {
							e.printStackTrace();
						}
					}

				}).start();
			}

		}
	}

	/**
	 * 给某个用户发送消息
	 *
	 * @param userName
	 * @param message
	 * @throws IOException
	 */
	public void sendMessageToUser(Long uid, TextMessage message)
			throws IOException {
		WebSocketSession session = userSocketSessionMap.get(uid);
		if (session != null && session.isOpen()) {
			session.sendMessage(message);
		}
	}

}

  页面

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getServerName() + ":"
			+ request.getServerPort() + path + "/";
	String basePath2 = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<script type="text/javascript" src="<%=basePath2%>resources/jquery.js"></script>
<style>
textarea {
	height: 300px;
	width: 100%;
	resize: none;
	outline: none;
}

input[type=button] {
	float: right;
	margin: 5px;
	width: 50px;
	height: 35px;
	border: none;
	color: white;
	font-weight: bold;
	outline: none;
}

.clear {
	background: red;
}

.send {
	background: green;
}

.clear:active {
	background: yellow;
}

.send:active {
	background: yellow;
}

.msg {
	width: 100%;
	height: 25px;
	outline: none;
}

#content {
	border: 1px solid gray;
	width: 100%;
	height: 400px;
	overflow-y: scroll;
}

.from {
	background-color: green;
	width: 80%;
	border-radius: 10px;
	height: 30px;
	line-height: 30px;
	margin: 5px;
	float: left;
	color: white;
	padding: 5px;
	font-size: 22px;
}

.to {
	background-color: gray;
	width: 80%;
	border-radius: 10px;
	height: 30px;
	line-height: 30px;
	margin: 5px;
	float: right;
	color: white;
	padding: 5px;
	font-size: 22px;
}

.name {
	color: gray;
	font-size: 12px;
}

.tmsg_text {
	color: white;
	background-color: rgb(47, 47, 47);
	font-size: 18px;
	border-radius: 5px;
	padding: 2px;
}

.fmsg_text {
	color: white;
	background-color: rgb(66, 138, 140);
	font-size: 18px;
	border-radius: 5px;
	padding: 2px;
}

.sfmsg_text {
	color: white;
	background-color: rgb(148, 16, 16);
	font-size: 18px;
	border-radius: 5px;
	padding: 2px;
}

.tmsg {
	clear: both;
	float: right;
	width: 80%;
	text-align: right;
}

.fmsg {
	clear: both;
	float: left;
	width: 80%;
}
</style>
<script>
		var path = ‘<%=basePath%>‘;
		var uid=${uid eq null?-1:uid};
		if(uid==-1){
			location.href="<%=basePath2%>";
		}
		var from=uid;
		var fromName=‘${name}‘;
		var to=uid==1?2:1;

		var websocket;
		if (‘WebSocket‘ in window) {
			websocket = new WebSocket("ws://" + path + "/ws?uid="+uid);
		} else if (‘MozWebSocket‘ in window) {
			websocket = new MozWebSocket("ws://" + path + "/ws"+uid);
		} else {
			websocket = new SockJS("http://" + path + "/ws/sockjs"+uid);
		}
		websocket.onopen = function(event) {
			console.log("WebSocket:已连接");
			console.log(event);
		};
		websocket.onmessage = function(event) {
			var data=JSON.parse(event.data);
			console.log("WebSocket:收到一条消息",data);
			var textCss=data.from==-1?"sfmsg_text":"fmsg_text";
			$("#content").append("<div class=‘fmsg‘><label class=‘name‘>"+data.fromName+" "+data.date+"</label><div class=‘"+textCss+"‘>"+data.text+"</div></div>");
			scrollToBottom();
		};
		websocket.onerror = function(event) {
			console.log("WebSocket:发生错误 ");
			console.log(event);
		};
		websocket.onclose = function(event) {
			console.log("WebSocket:已关闭");
			console.log(event);
		}
			function sendMsg(){
				var v=$("#msg").val();
				if(v==""){
					return;
				}else{
					var data={};
					data["from"]=from;
					data["fromName"]=fromName;
					data["to"]=to;
					data["text"]=v;
					websocket.send(JSON.stringify(data));
					$("#content").append("<div class=‘tmsg‘><label class=‘name‘>我 "+new Date().Format("yyyy-MM-dd hh:mm:ss")+"</label><div class=‘tmsg_text‘>"+data.text+"</div></div>");
					scrollToBottom();
					$("#msg").val("");
				}
			}

			function scrollToBottom(){
				var div = document.getElementById(‘content‘);
				div.scrollTop = div.scrollHeight;
			}

			Date.prototype.Format = function (fmt) { //author: meizz
			    var o = {
			        "M+": this.getMonth() + 1, //月份
			        "d+": this.getDate(), //日
			        "h+": this.getHours(), //小时
			        "m+": this.getMinutes(), //分
			        "s+": this.getSeconds(), //秒
			        "q+": Math.floor((this.getMonth() + 3) / 3), //季度
			        "S": this.getMilliseconds() //毫秒
			    };
			    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
			    for (var k in o)
			    if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
			    return fmt;
			}

			function send(event){
				var code;
				 if(window.event){
					 code = window.event.keyCode; // IE
				 }else{
					 code = e.which; // Firefox
				 }
				if(code==13){
					sendMsg();
				}
			}

			function clearAll(){
				$("#content").empty();
			}
		</script>
</head>
<body>
	欢迎:${sessionScope.name }
	<div id="content"></div>
	<input type="text" placeholder="请输入要发送的信息" id="msg" class="msg" onkeydown="send(event)">
	<input type="button" value="发送" class="send" onclick="sendMsg()" >
	<input type="button" value="清空" class="clear" onclick="clearAll()">
</body>
</html>

  

  碰到的一些问题 以及处理的想法

1.之前在拦截器的请求里面得不到session   使用从页面传进来的get方式得到参数uid(后来也没改什么 调试的时候发现可以得到session)

2.客服防止多处登陆的处理:

每次登陆获取loginusermap 如果没有该用户 新增有就更新 然后替换map到session中
登陆拦截器校验登陆的用户的sessionid和map里的是否一致 不一致说明被挤 重新跳转
loginUserMap.put(user.getFid(), sessionId);
request.getSession().getServletContext().setAttribute("loginUserMap", loginUserMap);

3.

按钮嵌在右侧的工具栏 点击固定只弹出一个聊天网页
var op;
op = window.open("${webRoot}customerChat.json","newWin1");
op.focus();

4.

兼容ios(看了请求头里的sec-websocket-extensions不一致  就想的馊主意改头  结果可用)
if(request.getHeaders().get("sec-websocket-extensions").contains("x-webkit-deflate-frame")){
List<String> list = new ArrayList<String>();
list.add("permessage-deflate");
request.getHeaders().remove("sec-websocket-extensions");
request.getHeaders().put("sec-websocket-extensions",list);
}

兼容nginx
upstream tjxm.com{ 
server 127.0.0.1:8080;
}
server {
listen 80;
server_name localhost; 
location / {
proxy_pass http://tjxm.com/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

哈哈 第一次写博客  有点菜鸡 websokcet部分也是从网上找的demo

时间: 2024-11-12 13:25:43

javaweb与websocket实现在线聊天功能总结的相关文章

SpringBoot+Vue+WebSocket 实现在线聊天

一.前言 本文将基于 SpringBoot + Vue + WebSocket 实现一个简单的在线聊天功能 页面如下: 在线体验地址:http://www.zhengqingya.com:8101 二.SpringBoot + Vue + WebSocket 实现在线聊天 1.引入websocket依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-bo

在线聊天功能 实现中俄双语实时翻译 原创 加精!!

要做一个在线实时聊天系统,而且还要实时双语翻译(中.俄).头疼 先找了一个php在线客服系统 带源码的.看懂源码后在上面加翻译功能,一共耗时4天弄出来的. 代码如下: if(mb_detect_encoding($fdata,"UTF-8, GBK") != "UTF-8"){     //$fdata = mb_detect_encoding($fdata,"UTF-8, GBK")     $fdata = mb_convert_encodi

SSH 项目中 使用websocket 实现网页聊天功能

参考文章  :java使用websocket,并且获取HttpSession,源码分析    http://www.cnblogs.com/zhuxiaojie/p/6238826.html 1.在项目中引入依赖 websocket遵循了javaee规范,所以需要引入javaee的包 1 <dependency> 2 <groupId>javax.websocket</groupId> 3 <artifactId>javax.websocket-api<

Django + WebSocket + Redis 在线聊天室题文章

话不多说先上效果图演示 项目:http://112.74.164.107:9990/ 1.安装组建 redis: yum install redis/apt install redis 2.创建虚拟化环境并进入 python3/python -m venv venv source venv\bin\active 3.安装第三方库pip install -r requirements.txt 4.初始化python manage.py makemigrations python manage.py

.NET Core 基于Websocket的在线聊天室

什么是Websocket 我们在传统的客户端程序要实现实时双工通讯第一想到的技术就是socket通讯,但是在web体系是用不了socket通讯技术的,因为http被设计成无状态,每次跟服务器通讯完成后就会断开连接. 在没有websocket之前web系统如果要做双工通讯往往使用http long polling技术.http long polling 每次往服务器发送请求后,服务端不会立刻返回信息来结束请求,而是一直挂着直到有数据需要返回,或者等待超时了才会返回.客户端在结束上一次请求后立刻再发

JavaWeb个人博客支持插入源码和在线聊天(含源码)

原文地址:http://www.cnblogs.com/liaoyu/p/uudisk.html 源码地址:https://github.com/liaoyu/uublogs 用JavaWeb写了一个简单的博客系统,支持在线插入源码和图片,编辑器为KindEditor,为了练习JQuery,加入在线聊天功能.用得还是很古老的JSP+Servlet,由于做到后来,兴趣减少及时间的原因,还有很多功能没有完成.. 开发环境:myeclipse mysql5 jdk6 主页: 留言: 在线聊天: 搭配项

在线聊天系统设计(原理+思路+源码)

这周项目要做一个在线聊天系统,感觉不是特别困难,原理也很简单,分享给大家. 技术: Java(Spring)+Mysql+MemCache Spring做的是事件驱动模型,所有DB,更新缓存操作改成异步的. MemCache存放缓存,每个用户的聊天记录缓存,好友关系维护. 需求: 用户分为虚拟用户,普通用户,高级用户(在线经理人),管理员用户(客服). 虚拟,普通用户有一个好友列表,好友列表保存着用户的好友,对于虚拟,普通用户来说,他们的好友列表只有高级用户+管理员用户. 高级用户,管理员用户来

java Socket实现简单在线聊天(一)

出处:http://blog.csdn.net/tuzongxun 最近的项目有一个在线网页交流的需求,由于很久以前做过的demo已经忘记的差不多了,因此便重新学习一下. 我计划的大致实现步骤分这样几大步: 1.使用awt组件和socket实现简单的单客户端向服务端持续发送消息: 2.结合线程,实现多客户端连接服务端发送消息: 3.实现服务端转发客户端消息至所有客户端,同时在客户端显示: 4.把awt组件生成的窗口界面改成前端jsp或者html展示的界面,java socket实现的客户端改为前

javaweb webSocket 实现简单的点对点聊天功能

本文依据 http://redstarofsleep.iteye.com/blog/1488639?page=4  内容修改完成,实现点对点聊天 需要 jdk 7 , tomcat需要支持websocket的版本 1.InitServlet 该类主要是用来初始化构造将来存储用户身份信息的map仓库,利用其初始化方法Init 初始化仓库, 利用其静态方法getSocketList 获得对应的用户身份信息. webSocket ,我认为MessageInbound 用来识别登录人的信息,用它来找到对