原文:http://baike.xsoftlab.net/view/656.html
Git地址
http://git.oschina.net/loopcc/WebSocketChat
概要:
WebSocket是HTML5一種新的協議,它實現了瀏覽器與服務器全雙工通信,這里就將使用WebSocket來開發網頁聊天室,前端框架會使用AmazeUI,后台使用Java,編輯器使用UMEditor。
| |目錄
Java和WebSocket開發網頁即時聊天室
1一、項目說明及簡介
1. 項目簡介
WebSocket是HTML5一種新的協議,它實現了瀏覽器與服務器全雙工通信,這里就將使用WebSocket來開發網頁聊天室,前端框架會使用AmazeUI,后台使用Java,編輯器使用UMEditor。
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. 效果預覽
6.Git地址
http://git.oschina.net/loopcc/WebSocketChat
2二、新建空項目
打開Eclipse JavaEE,新建一個名為Chat的Dynamic Web Project,然后導入處理JSON格式字符串所需要的包,
1
2
3
4
5
6
|
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作為項目根目錄。
3三、編寫前端頁面
這里使用了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+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta charset="UTF-8"> <title>Landing Page | Amaze UI Example</title> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="format-detection" content="telephone=no"> <meta name="renderer" content="webkit"> <meta http-equiv="Cache-Control" content="no-siteapp"/> <script src="assets/js/jquery.min.js"></script> <!-- 妹子UI相關資源 --> <link rel="alternate icon" type="image/png" href="assets/i/favicon.png"> <link rel="stylesheet" href="assets/css/amazeui.min.css"/> <script src="assets/js/amazeui.min.js"></script> <!-- UM相關資源 --> <link href="assets/umeditor/themes/default/css/umeditor.css" type="text/css" rel="stylesheet"> <script type="text/javascript" charset="utf-8" src="assets/umeditor/umeditor.config.js"></script> <script type="text/javascript" charset="utf-8" src="assets/umeditor/umeditor.min.js"></script> <script type="text/javascript" src="assets/umeditor/lang/zh-cn/zh-cn.js"></script> </head> <body> <header class="am-topbar am-topbar-fixed-top"> <div class="am-container"> <h1 class="am-topbar-brand"> <a href="#">聊天室</a> </h1> <div class="am-collapse am-topbar-collapse" id="collapse-head"> <ul class="am-nav am-nav-pills am-topbar-nav"> <li class="am-active"><a href="#">首頁</a></li> <li><a href="#">項目</a></li> </ul> <div class="am-topbar-right"> <button class="am-btn am-btn-secondary am-topbar-btn am-btn-sm"><span class="am-icon-pencil"></span> 注冊</button> </div> <div class="am-topbar-right"> <button class="am-btn am-btn-primary am-topbar-btn am-btn-sm"><span class="am-icon-user"></span> 登錄</button> </div> </div> </div> </header> <div id="main"> <!-- 聊天內容展示區域 --> <div id="ChatBox" class="am-g am-g-fixed" > <div class="am-u-lg-12" style="height:400px;border:1px solid #999;overflow-y:scroll;"> <ul id="chatContent" class="am-comments-list am-comments-list-flip"> <li class="am-comment am-comment-success" style="margin-right:20%;"> <a href=""> <img class="am-comment-avatar" src="assets/images/other.jpg" alt=""/> </a> <div class="am-comment-main" > <header class="am-comment-hd"> <div class="am-comment-meta"> <a href="#link-to-user" class="am-comment-author">某人</a> <time datetime="" title="">2014-7-12 15:30</time> </div> </header> <div class="am-comment-bd">此處是消息內容</div> </div> </li> <li class="am-comment am-comment-flip am-comment-warning" style="margin-left:20%;"> <a href=""> <img class="am-comment-avatar" src="assets/images/xia.jpg" alt=""/> </a> <div class="am-comment-main" > <header class="am-comment-hd"> <div class="am-comment-meta"> <a href="#link-to-user" class="am-comment-author">某人</a> <time datetime="" title="">2014-7-12 15:30</time> </div> </header> <div class="am-comment-bd">此處是消息內容</div> </div> </li> </ul> </div> </div> <!-- 聊天內容發送區域 --> <div id="EditBox" class="am-g am-g-fixed"> <!--style給定寬度可以影響編輯器的最終寬度--> <script type="text/plain" id="myEditor" style="width:100%;height:140px;"></script> <button id="send" type="button" class="am-btn am-btn-primary am-btn-block">發送</button> </div></div><script type="text/javascript">$(function(){ //窗口大小變化時,調整顯示效果 $("#ChatBox div:eq(0)").height($(this).height()-290); $(window).resize(function(){ $("#ChatBox div:eq(0)").height($(this).height()-290); }); //實例化編輯器 var um = UM.getEditor('myEditor',{ initialContent:"請輸入聊天信息...", autoHeightEnabled:false, toolbar:[ 'source | undo redo | bold italic underline strikethrough | superscript subscript | forecolor backcolor | removeformat |', 'insertorderedlist insertunorderedlist | selectall cleardoc paragraph | fontfamily fontsize' , '| justifyleft justifycenter justifyright justifyjustify |', 'link unlink | emotion image video | map' ] });}); </script> </body> </html>
編寫完成之后啟動Tomcat服務器,然后訪問index.jsp ,會看到設計好的頁面效果。
上面的實例代碼里有兩條示例消息,一條別人的消息,一條自己的消息。
4四、消息流轉
聊天室的核心就是把一個人的消息廣播給所有人,我們這里使用WebSocket技術讓所有人的瀏覽器都與服務器連在一起,當一個人發送消息到服務器后,服務器把消息轉發給所有人,包括自己,
這樣每個人的瀏覽器就都能看到這條消息。
出現一條新消息時必備的3個屬性:【發言人昵稱】,【消息內容】和【發送時間】。
為了區別消息是否為自己,我們還需要在消息中加入一項屬性:【是否自己】。
其中【發送時間】和【是否自己】可以由服務器進行處理,那么發言人只需要告訴服務器【昵稱】與【消息內容】即可:
//瀏覽器發給服務器的數據:
{
nickname:"風清揚",
content:"HelloWorld"
}
//瀏覽器接收的數據:
{
nickname:"風清揚",
content:"HelloWorld",
date:"2016.04.01 14:05:22",
isSelf:true
}
5五、JS添加新消息
頁面樣式已經設計好,而真正聊天的時候肯定需要不斷展示新的消息,為了方便控制,我們使用JavaScript統一消息的創建過程。
頁面中代表消息的li元素只保留一份,把它作為消息的模板,默認隱藏,每當新消息到來,我們就復制一份模板,修改其中的屬性,追加到列表中去展示,給幾個元素加上獨有的標記:
【昵稱】:ff="nickname" 【內容】:ff="content" 【時間】:ff="msgdate"
<li id="msgtmp" class="am-comment" style="display:none;"> <a href=""> <img class="am-comment-avatar" src="assets/images/other.jpg" alt=""/> </a> <div class="am-comment-main" > <header class="am-comment-hd"> <div class="am-comment-meta"> <a ff="nickname" href="#link-to-user" class="am-comment-author">某人</a> <time ff="msgdate" datetime="" title="">2014-7-12 15:30</time> </div> </header> <div ff="content" class="am-comment-bd">此處是消息內容</div> </div> </li>
使用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); //滾動條移動至最底部
}
6六、編寫后台代碼
新建一個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<Session> room = new Vector<Session>(); /** * 用戶接入 * @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){ } }
7七、前端后端交互
后台寫完了,前台要用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();
}
});
到這步,簡單的網頁聊天室就完成了,你可以多開幾個窗口或在局域網中邀請小伙伴們來一起測試。
8八、思考
本次項目只是簡單的實現群體聊天功能,還有很多功能可以增加,例如頭像上傳、一對一聊天等,請考慮如何完善我們的聊天室。