如何用WebSocket實現一個簡單的聊天室以及單聊功能
阿新 • • 發佈:2019-01-09
百度百科中這樣定義WebSocket:WebSocket協議是基於TCP的一種新的網路協議。它實現了瀏覽器與伺服器全雙工(full-duplex)通訊——允許伺服器主動傳送資訊給客戶端。簡單的說,WebSocket協議之前,雙工通訊是通過多個http連結來實現,這導致了效率低下,而WebSocket解決了這個問題。
1.1 思考: 傳統web的請求和響應模式中, 我們如何實現實時資訊傳輸, 如何實現伺服器反推資料? 在瀏覽器中通過http僅能實現單向的通訊,comet可以一定程度上模擬雙向通訊,但效率較低,並需要伺服器有較好的支援; flash中的socket和xmlsocket可以實現真正的雙向通訊,通過 flex ajax bridge,可以在javascript中使用這兩項功能. 可以預見,如果websocket一旦在瀏覽器中得到實現,將會替代上面兩項技術,得到廣泛的使用.面對這種狀況,HTML5定義了WebSocket協議,能更好的節省伺服器資源和頻寬並達到實時通訊。在JavaEE7中也實現了WebSocket協議。
1.2 WebSocket的目標:**打破傳統的web請求響應模型, 實現管道式的實時通訊。開啟一個瀏覽器和伺服器的通訊通道,持續連線! 伺服器給瀏覽器推送資料 非常方便!web的實時訊息通訊: 聊天,股票,遊戲,監控等等。** 1.3 軟體需求 (1). 安裝jdk7 或更高版本 (2). 下載tomcat7 兩者保持一致(32、64位) 2.1 實現一個webSocket應用程式,我們要學會幾個基本操作。 (1). 開啟連線 (2). 客戶端給伺服器端傳送資料 (3). 伺服器端接收資料 (4). 伺服器端給客戶端傳送資料 (5). 客戶端接收資料 (6). 監聽三類基本事件: onopen,onmessage,onclose 提示: onmessage 是傳送資料時的響應事件。onopen是開啟連線時的響應事件。onclose是關閉連線時的響應事件。
3.1 接下來正式實現聊天室功能,首先寫一個登入頁面
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Insert title here</title> <script type="text/javascript" src="jquery-1.4.4.min.js"></script> </head> <body> <form name="ff" action="LoginServlet" method="post" > 使用者名稱:<input name="username" /><br/> <input type="submit" value="登入"/> </form> </body> </html>
3.2 在web.xml配置LoginServlet
<servlet> <description></description> <display-name>LoginServlet</display-name> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.bjsxt.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/LoginServlet</url-pattern> </servlet-mapping>
3.3 新增LoginServlet,用於處理登入請求以及跳轉到聊天室介面
package com.bjsxt.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException,ServletException{ } public void doPost(HttpServletRequest request, HttpServletResponse response)throws IOException,ServletException{ String username = request.getParameter("username"); System.out.println("doPost當前登入使用者為"+username); request.getSession().setAttribute("username",username); //這裡只是簡單地模擬登入,登陸之後直接跳轉到聊天頁面 response.sendRedirect("chat.jsp"); } }
3.4 新增聊天介面
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Insert title here</title> <script type="text/javascript" src="jquery-1.4.4.min.js"></script> <script type="text/javascript"> var ws; var userName = ${sessionScope.username}; //通過URL請求服務端(chat為專案名稱) var url = "ws://localhost:8080/chat/chatSocket?username=${sessionScope.username}"; //進入聊天頁面就是一個通訊管道 window.onload = function() { if ('WebSocket' in window) { ws = new WebSocket(url); } else if ('MozWebSocket' in window) { ws = new MozWebSocket(url); } else { alert('WebSocket is not supported by this browser.'); return; } //監聽伺服器傳送過來的所有資訊 ws.onmessage = function(event) { eval("var result=" + event.data); //如果後臺發過來的alert不為空就顯示出來 if (result.alert != undefined) { $("#content").append(result.alert + "<br/>"); } //如果使用者列表不為空就顯示 if (result.names != undefined) { //重新整理使用者列表之前清空一下列表,免得會重複,因為後臺只是單純的新增 $("#userList").html(""); $(result.names).each( function() { $("#userList").append( "<input type=checkbox value='"+this+"'/>" + this + "<br/>"); }); } //將使用者名稱字和當前時間以及傳送的資訊顯示在頁面上 if (result.from != undefined) { $("#content").append( result.from + " " + result.date + " 說:<br/>" + result.sendMsg + "<br/>"); } }; }; //將訊息傳送給後臺伺服器 function send() { //拿到需要單聊的使用者名稱 //alert("當前登入使用者為"+userName); var ss = $("#userList :checked"); //alert("群聊還是私聊"+ss.size()); var to = $('#userList :checked').val(); if (to == userName) { alert("你不能給自己傳送訊息啊"); return; } //根據勾選的人數確定是群聊還是單聊 var value = $("#msg").val(); //alert("訊息內容為"+value); var object = null; if (ss.size() == 0) { object = { msg : value, type : 1, //1 廣播 2單聊 }; } else { object = { to : to, msg : value, type : 2, //1 廣播 2單聊 }; } //將object轉成json字串傳送給服務端 var json = JSON.stringify(object); //alert("str="+json); ws.send(json); //訊息傳送後將訊息欄清空 $("#msg").val(""); } </script> </head> <body> <h3>歡迎 ${sessionScope.username }使用本聊天系統!!</h3> <div id="content" style="border: 1px solid black; width: 400px; height: 300px; float: left; color: #7f3f00;"></div> <div id="userList" style="border: 1px solid black; width: 120px; height: 300px; float: left; color: #00ff00;"></div> <div style="clear: both;" style="color:#00ff00"> <input id="msg" /> <button onclick="send();">傳送訊息</button> </div> </body> </html>
3.5 新增啟動類
package com.bjsxt.init; import java.util.Set; import javax.websocket.Endpoint; import javax.websocket.server.ServerApplicationConfig; import javax.websocket.server.ServerEndpointConfig; /** * 專案啟動時會自動啟動,類似與ContextListener. * 是webSocket的核心配置類。 * @author xiongzichao * */ public class ServerConfig implements ServerApplicationConfig { //掃描src下所有類@ServerEndPoint註解的類。 @Override public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scan) { System.out.println("掃描到"+scan.size()+"個服務端程式"); return scan; } //獲取所有以介面方式配置的webSocket類。 @Override public Set<ServerEndpointConfig> getEndpointConfigs( Set<Class<? extends Endpoint>> point) { System.out.println("實現EndPoint介面的類數量:"+point.size()); return null; } }
3.6 新增客戶端傳送給服務端訊息實體
package com.bjsxt.vo; /** * 客戶端傳送給服務端訊息實體 * @author xiongzichao * */ public class ContentVo { private String to; private String msg; private Integer type; public String getTo() { return to; } public void setTo(String to) { this.to = to; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } }
3.7 新增服務端傳送給客戶端訊息實體
package com.bjsxt.vo; import java.util.Date; import java.util.List; /** * 服務端傳送給客戶端訊息實體 * @author xiongzichao * */ public class Message { private String alert; // private List<String> names; private String sendMsg; private String from; private String date; public String getDate() { return date; } public void setDate(String date) { this.date = date; } public String getSendMsg() { return sendMsg; } public void setSendMsg(String sendMsg) { this.sendMsg = sendMsg; } public String getFrom() { return from; } public void setFrom(String from) { this.from = from; } public String getAlert() { return alert; } public void setAlert(String alert) { this.alert = alert; } public List<String> getNames() { return names; } public void setNames(List<String> names) { this.names = names; } public Message() { super(); } }
3.8 新增服務端程式
package com.bjsxt.server; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import com.bjsxt.vo.ContentVo; import com.bjsxt.vo.Message; import com.google.gson.Gson; /** * 總通訊管道 * @author xiongzichao * */ @ServerEndpoint("/chatSocket") public class ChatSocket { //定義一個全域性變數集合sockets,使用者存放每個登入使用者的通訊管道 private static Set<ChatSocket> sockets=new HashSet<ChatSocket>(); //定義一個全域性變數Session,用於存放登入使用者的使用者名稱 private Session session; //定義一個全域性變數map,key為使用者名稱,該使用者對應的session為value private static Map<String, Session> map=new HashMap<String, Session>(); //定義一個數組,用於存放所有的登入使用者,顯示在聊天頁面的使用者列表欄中 private static List<String>names=new ArrayList<String>(); private String username; private Gson gson=new Gson(); /* * 監聽使用者登入 */ @OnOpen public void open(Session session){ this.session = session; //將當前連線上的使用者session資訊全部存到scokets中 sockets.add(this); //拿到URL路徑後面所有的引數資訊 String queryString = session.getQueryString(); System.out.println(); //擷取=後面的引數資訊(使用者名稱),將引數資訊賦值給全域性的使用者名稱 this.username = queryString.substring(queryString.indexOf("=")+1); //每登入一個使用者,就將該使用者名稱存入到names陣列中,用於重新整理好友列表 names.add(this.username); //將當前登入使用者以及對應的session存入到map中 this.map.put(this.username, this.session); System.out.println("使用者"+this.username+"進入聊天室"); Message message = new Message(); message.setAlert("使用者"+this.username+"進入聊天室"); //將當前所有登入使用者存入到message中,用於廣播發送到聊天頁面 message.setNames(names); //將聊天資訊廣播給所有通訊管道(sockets) broadcast(sockets, gson.toJson(message) ); } /* * 退出登入 */ @OnClose public void close(Session session){ //移除退出登入使用者的通訊管道 sockets.remove(this); //將使用者名稱從names中剔除,用於重新整理好友列表 names.remove(this.username); Message message = new Message(); System.out.println("使用者"+this.username+"退出聊天室"); message.setAlert(this.username+"退出當前聊天室!!!"); //重新整理好友列表 message.setNames(names); broadcast(sockets, gson.toJson(message)); } /* * 接收客戶端傳送過來的訊息,然後判斷是廣播還是單聊 */ @OnMessage public void receive(Session session,String msg) throws IOException{ //將客戶端訊息轉成json物件 ContentVo vo = gson.fromJson(msg, ContentVo.class); //如果是群聊,就像訊息廣播給所有人 if(vo.getType()==1){ Message message = new Message(); message.setDate(new Date().toLocaleString()); message.setFrom(this.username); message.setSendMsg(vo.getMsg()); broadcast(sockets, gson.toJson(message)); }else{ Message message = new Message(); message.setDate(new Date().toLocaleString()); message.setFrom(this.username); message.setAlert(vo.getMsg()); message.setSendMsg("<font color=red>正在私聊你:</font>"+vo.getMsg()); String to = vo.getTo(); //根據單聊物件的名稱拿到要單聊物件的Session Session to_session = this.map.get(to); //如果是單聊,就將訊息傳送給對方 to_session.getBasicRemote().sendText(gson.toJson(message)); } } /* * 廣播訊息 */ public void broadcast(Set<ChatSocket>sockets ,String msg){ //遍歷當前所有的連線管道,將通知資訊傳送給每一個管道 for(ChatSocket socket : sockets){ try { //通過session傳送資訊 socket.session.getBasicRemote().sendText(msg); } catch (IOException e) { e.printStackTrace(); } } } }
4.0 實現介面
注意
這裡一共需要三個jar包,分別是WebSocket-api.jar這個定義webSocket應用程式開發介面!tomcat7-webSocket.jar tomcat伺服器對於webSocket介面的實現!!還有一個gson.jar用於序列化實體類資訊。前面兩個jar包在tomcat7的lib目錄裡面可以找到。