# WebSocketChat
**Repository Path**: jollyfly/WebSocketChat
## Basic Information
- **Project Name**: WebSocketChat
- **Description**: 使用WebSocket协议实现的即时通讯聊天室,可作为学习DEMO使用,需要使用JavaEE7和Tomcat8才能正常运行,使用支持HTML5的浏览器,比如新版的FireFox或Chrome,视频教程与相关资源:https://yunpan.cn/cqMPVjvvbuTsM  访问密码 b122
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 29
- **Created**: 2017-11-22
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Java和WebSocket开发网页即时聊天室 #
----------
# 一、项目说明及简介 #
### 1. 项目简介 ###
WebSocket 是HTML5一种新的协议,它实现了浏览器与服务器全双工通信,这里就将使用WebSocket来开发网页聊天室,前端框架会使用AmazeUI,后台使用Java,编辑器使用UMEditor。
使用WebSocket协议实现的即时通讯聊天室,可作为学习DEMO使用,需要使用JavaEE7和Tomcat8才能正常运行,使用支持HTML5的浏览器,比如新版的FireFox或Chrome,视频教程与相关资源:[https://yunpan.cn/cqMPVjvvbuTsM](https://yunpan.cn/cqMPVjvvbuTsM) 访问密码 b122
### 2. 涉及知识点 ###
	网页前端(HTML5 + CSS3 + JS)和 JavaEE。
### 3. 软件环境 ###
	
	Tomcat8
	JavaEE7
	JDK7
	Eclipse-JavaEE 或 MyEclipse
	支持HTML5的浏览器
### 4. 相关框架 ###
	jQuery—1.X
	妹子UI(AmazeUI-2.5.2)
	百度富文本编辑器(UMeditor1_2_2)
### 5. 效果预览 ###

# 二、新建空项目 #
打开Eclipse JavaEE,新建一个名为Chat的Dynamic Web Project,
然后导入处理JSON格式字符串所需要的包,
	commons-beanutils-1.8.0.jar
	commons-collections-3.2.1.jar
	commons-lang-2.5.jar
	commons-logging-1.1.1.jar
	ezmorph-1.0.6.jar
	json-lib-2.4-jdk15.jar
把这几个包放在/WEB-INF/lib目录下,最后把项目发布到Tomcat8服务器上,到此空项目就搭建完成了。
Eclipse的默认项目根目录叫WebContent,
MyEclipse默认项目根目录叫WebRoot,
在本篇文档中,我们接下来用WebRoot作为项目根目录。
# 三、编写前端页面 #
这里使用了AmazeUI框架,它是一个跨屏自适应的前端框架
消息输入框使用了UMEditor,它是一个富文本在线编辑器,能让我们的消息内容多姿多彩。
在WebContent目录下新建一个名为index.jsp的页面,
首先从AmazeUI官网下载压缩包,然后解压把assets文件夹拷贝到WebRoot目录下,这样我们就能使用AmazeUI了。
再从UEditer官网下载Mini版的JSP版本压缩包,解压后把整个目录拷贝到WebRoot目录下,
接下来就可以编写前端代码了,代码如下(可按照自己的审美去编写):
	<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
	<%
	String path = request.getContextPath();
	String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
	%>
	
	
	
		
		Landing Page | Amaze UI Example
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
	
	
	
	
	
	
	
	
	
	
编写完成之后启动Tomcat服务器,然后访问index.jsp ,会看到设计好的页面效果。
上面的实例代码里有两条示例消息,一条别人的消息,一条自己的消息。
# 四、消息流转 #
聊天室的核心就是把一个人的消息广播给所有人,
我们这里使用WebSocket技术让所有人的浏览器都与服务器连在一起,
当一个人发送消息到服务器后,服务器把消息转发给所有人,包括自己,
这样每个人的浏览器就都能看到这条消息,
出现一条新消息时必备的3个属性:【发言人昵称】,【消息内容】和【发送时间】,
为了区别消息是否为自己,我们还需要在消息中加入一项属性:【是否自己】,
其中【发送时间】和【是否自己】可以由服务器进行处理,
那么发言人只需要告诉服务器【昵称】与【消息内容】即可:
	//浏览器发给服务器的数据:
	{
		nickname:"风清扬",
		content:"HelloWorld"
	}
	//浏览器接收的数据:
	{
		nickname:"风清扬",
		content:"HelloWorld",
		date:"2016.04.01 14:05:22",
		isSelf:true
	}
# 五、JS添加新消息 #
页面样式已经设计好,而真正聊天的时候肯定需要不断展示新的消息,
为了方便控制,我们使用JavaScript统一消息的创建过程。
页面中代表消息的li元素只保留一份,把它作为消息的模板,默认隐藏,
每当新消息到来,我们就复制一份模板,修改其中的属性,追加到列表中去展示,
给几个元素加上独有的标记:
【昵称】:ff="nickname"
【内容】:ff="content"
【时间】:ff="msgdate"
	
使用JS克隆一份模板,设置克隆体里面的数据,最终追加到列表中:
	//向聊天窗口加入一条消息
	function addMessage(msg){
	
		var box = $("#msgtmp").clone(); 	//复制一份模板,取名为box
		box.show();							//设置box状态为显示
		box.appendTo("#chatContent");		//把box追加到聊天面板中
		box.find('[ff="nickname"]').html(msg.nickname); //在box中设置昵称
		box.find('[ff="msgdate"]').html(msg.date); 		//在box中设置时间
		box.find('[ff="content"]').html(msg.content); 	//在box中设置内容
		box.addClass(msg.isSelf? 'am-comment-flip':'');	//右侧显示
		box.addClass(msg.isSelf? 'am-comment-warning':'am-comment-success');//颜色
		box.css((msg.isSelf? 'margin-left':'margin-right'),"20%");//外边距
		
		$("#ChatBox div:eq(0)").scrollTop(999999); 	//滚动条移动至最底部
	}
# 六、编写后台代码 #
新建一个com.zhenzhigu.chat的包,在包中创建一个名为ChatServer的类,
从JavaEE 7开始就统一了WebSocket的API,因此无论是什么服务器,用Java写的代码都是一样的,代码如下:
	
	package com.zhenzhigu.chat;
	
	import java.text.SimpleDateFormat;
	import java.util.Date;
	import java.util.Vector;
	
	import javax.websocket.OnClose;
	import javax.websocket.OnError;
	import javax.websocket.OnMessage;
	import javax.websocket.OnOpen;
	import javax.websocket.Session;
	import javax.websocket.server.ServerEndpoint;
	
	import net.sf.json.JSONObject;
	
	@ServerEndpoint("/websocket")
	public class ChatServer {
	
		private static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		private static Vector room = new Vector();
		
		/**
		 * 用户接入
		 * @param session 可选
		 */
		@OnOpen
		public void onOpen(Session session){
			room.addElement(session);
		}
		
		/**
		 * 接收到来自用户的消息
		 * @param message
		 * @param session
		 */
		@OnMessage
		public void onMessage(String message,Session session){
			//把用户发来的消息解析为JSON对象
			JSONObject obj = JSONObject.fromObject(message);
			//向JSON对象中添加发送时间
			obj.put("date", df.format(new Date()));
			//遍历聊天室中的所有会话
			for(Session se : room){
				//设置消息是否为自己的
				obj.put("isSelf", se.equals(session));
				//发送消息给远程用户
				se.getAsyncRemote().sendText(obj.toString());
			}
		}
		
		/**
		 * 用户断开
		 * @param session
		 */
		@OnClose
		public void onClose(Session session){
			room.remove(session);
		}
		
		/**
		 * 用户连接异常
		 * @param t
		 */
		@OnError
		public void onError(Throwable t){
			
		}
	}
# 七、前端后端交互 #
后台写完了,前台要用WebSocket连接后台,需要新建一个WebSocket对象,然后就可以和服务器端进行交互,从浏览器发送消息给服务器端,同时要验证输入框的内容是否为空,然后接受服务端发送的消息,把它们动态地添加到聊天内容框中。
记得写到文档就绪函数中
	
	//自己的昵称
	var nickname = "风清扬"+Math.random();
	//建立一条与服务器之间的连接
	var socket = new WebSocket("ws://${pageContext.request.getServerName()}:${pageContext.request.getServerPort()}${pageContext.request.contextPath}/websocket");
    
	//接收服务器的消息
    socket.onmessage=function(ev){
    	var obj = eval(   '('+ev.data+')'   );
    	addMessage(obj)
    }
    //发送按钮被点击时
    $("#send").click(function(){
    	if (!um.hasContents()) {  // 判断消息输入框是否为空
            // 消息输入框获取焦点
            um.focus();
            // 添加抖动效果
            $('.edui-container').addClass('am-animation-shake');
            setTimeout("$('.edui-container').removeClass('am-animation-shake')", 1000);
        } else {
        	//获取输入框的内容
        	var txt = um.getContent();
        	//构建一个标准格式的JSON对象
        	var obj = JSON.stringify({
	    		nickname:nickname,
	    		content:txt
	    	});
            // 发送消息
            socket.send(obj);
            // 清空消息输入框
            um.setContent('');
            // 消息输入框获取焦点
            um.focus();
        }
    
    });
到这步,简单的网页聊天室就完成了,你可以多开几个窗口或在局域网中邀请小伙伴们来一起测试。
# 八、思考 #
本次项目只是简单的实现群体聊天功能,还有很多功能可以增加,例如头像上传、一对一聊天等,请考虑如何完善我们的聊天室。