package com.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.SessionAttributes; @Controller @RequestMapping("/home") @SessionAttributes("uname") public class ViewController { @RequestMapping("/list") public String cc(ModelMap model){ return "index"; } @RequestMapping("/room") public String h(ModelMap model,String uname,String roomid){ model.put("uname",uname); model.put("roomid", roomid); return "room"; } }
package com.controller; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import net.sf.json.JSONObject; @ServerEndpoint("/websocket/{info}") public class WebSocketService { private static SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");//創建時間格式對象 //concurrent包的線程安全Set,用來存放每個客戶端對應的WebSocketService對象。 //創建一個房間的集合,用來存放房間 private static ConcurrentHashMap<String,ConcurrentHashMap<String, WebSocketService>> roomList = new ConcurrentHashMap<String,ConcurrentHashMap<String, WebSocketService>>(); //與某個客戶端的連接會話,需要通過它來給客戶端發送數據 private Session session; //重新加入房間的標示; private int rejoin = 0; /*static { roomList.put("room1", new ConcurrentHashMap<String, WebSocketService>()); roomList.put("room2", new ConcurrentHashMap<String, WebSocketService>()); }*/ /** * 用戶接入 * @param param 連接websocket服務器時穿的參數 * @param session 會話 */ @OnOpen public void onOpen(@PathParam(value = "info") String param,Session session){ System.err.println("登錄時候穿的參數"+param.toString()); this.session = session; String flag = param.split("[|]")[0]; //標識 String member = param.split("[|]")[1]; //成員名 if(roomList.get(member)==null){//判斷房間列表中是否有此次的房間名稱 roomList.put(member, new ConcurrentHashMap<String, WebSocketService>());//如果沒有將房間添加到房間列表中 } //判斷標志位是不是加入房間 if(flag.equals("join")){ String user = param.split("[|]")[2]; //截取用戶名 //調用加入房間的方法,傳入房間名稱和用戶名稱 joinRoom(member,user); } } /** * 加入房間 * @param member 房間號 * @param user 用戶名 */ public void joinRoom(String member,String user){ //從房間列表中獲取房間 ConcurrentHashMap<String, WebSocketService> r = roomList.get(member); System.out.println(r.get(user)); if(r.get(user) != null){ //該用戶有沒有在房間中 this.rejoin = 1;//重新加入房間標志位1(一旦重新加入房間,以前的頁面用戶將看不到消息) } r.put(user, this);//將此用戶加入房間中 } /** * 發送消息的方法 * @param message 需要發送的消息 * @throws IOException */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * 接收到來自用戶的消息 * @param message 接受的消息 * @param session 回話 * @throws IOException */ @OnMessage public void onMessage(String message,Session session) throws IOException{ //把用戶發來的消息解析為JSON對象 JSONObject obj = JSONObject.fromObject(message); System.out.println(obj.toString()); //判斷接受到的消息的標志位是什么(退出房間和發消息) if(obj.get("flag").toString().equals("exitroom")){ //退出房間操作 String roomid = obj.get("roomid").toString();//取得房間編號 System.out.println("roomid-"+roomid); //將用戶從聊天室中移除 int f2 = 1; roomList.get(roomid).remove(obj.get("nickname").toString());//將用戶直接移除 if(roomList.get(roomid).size() == 0){//判斷房間該房間是否還有用戶,如果沒有,則將此房間也移除 f2 = 2; } if(f2 == 1){ //證明該房間還有其它成員,則通知其它成員更新列表 obj.put("flag","exitroom"); String m = obj.get("nickname").toString()+" 退出了房間"; obj.put("message", m); ConcurrentHashMap<String, WebSocketService> r =roomList.get(roomid); List<String> uname = new ArrayList<String>(); for(String u:r.keySet()){ uname.add(u); } obj.put("uname", uname.toArray()); for(String i:r.keySet()){ //遍歷該房間 r.get(i).sendMessage(obj.toString());//調用方法 將消息推送 } } }else if(obj.get("flag").toString().equals("chatroom")){ //聊天室的消息 加入房間/發送消息 //向JSON對象中添加發送時間 obj.put("date", df.format(new Date())); //獲取客戶端發送的數據中的內容---房間�? 用於區別該消息是來自於哪個房間 String roomid = obj.get("target").toString(); //獲取客戶端發送的數據中的內容---用戶 String username = obj.get("nickname").toString(); //從房間列表中定位到該房間 ConcurrentHashMap<String, WebSocketService> r =roomList.get(roomid); List<String> uname = new ArrayList<String>(); for(String u:r.keySet()){ uname.add(u); } obj.put("uname", uname.toArray()); if(r.get(username).rejoin == 0){ //證明不是退出重連 for(String i:r.keySet()){ //遍歷該房間 obj.put("isSelf", username.equals(i));//設置消息是否為自己的 r.get(i).sendMessage(obj.toString());//調用方法 將消息推送 } }else{ obj.put("isSelf", true); r.get(username).sendMessage(obj.toString()); } r.get(username).rejoin = 0; } } /** * 用戶斷開 * @param session */ @OnClose public void onClose(Session session){ System.out.println("退出聊天室"); } /** * 用戶連接異常 * @param t */ @OnError public void onError(Throwable t){ } }
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:websocket="http://www.springframework.org/schema/websocket" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd"> </beans>
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:websocket="http://www.springframework.org/schema/websocket" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd"> <!-- 開啟注解模式驅動 --> <mvc:annotation-driven></mvc:annotation-driven> <!-- 掃包 --> <context:component-scan base-package="com.*"></context:component-scan> <!-- 靜態資源過濾 --> <!-- <mvc:resources location="/resources/" mapping="/resources/**"></mvc:resources> --> <!-- 視圖渲染 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 制定頁面存放路徑 --> <property name="prefix" value="/WEB-INF/pages/"></property> <!-- 文件的后綴 --> <property name="suffix" value=".jsp"></property> </bean> </beans>
index.jsp
<%@ 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"> <script type="text/javascript" src="../js/jquery-3.2.1.min.js"></script> <title>Insert title here</title> </head> <script type="text/javascript"> $(function() { $("span").click(function(){ var uname = $("input").val(); if(uname == ""){ alert("請先輸入用戶名"); }else { var roomid = $(this).html() console.log("roomid"+roomid); location.href="/Chatroom/home/room.do?uname="+uname+"&roomid="+roomid; } }) }) </script> <style> span:HOVER{ color: red; } span{ cursor:pointer; } </style> <body> 用戶名:<input type="text"> /*注:請先輸入用戶名,且保證用戶名唯一,再點擊下面的房間加入房間 <h1><span>room1</span></h1> <h1><span>room2</span></h1> </body> </html>
room.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <base href="<%=basePath%>" /> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="js/jquery-3.2.1.min.js"></script> <link rel="stylesheet" type="text/css" href="css/chat.css" /> <title>聊天室</title> </head> <script type="text/javascript"> $(function(){ var roomid=$(".roomid").html();//房間名 var nickname = $(".uname").html();//自己的昵稱 var flag = "join";//標志位 var info = flag + "|" +roomid + "|" +nickname;//拼裝websocket傳遞的參數 //建立一條與服務器之間的連接 var socket = new WebSocket("ws://${pageContext.request.getServerName()}:${pageContext.request.getServerPort()}${pageContext.request.contextPath}/websocket/"+info); var text = ""; //console.log(${pageContext.request.getServerName()}+${pageContext.request.getServerPort()}${pageContext.request.contextPath}); console.log(socket); /*定義加入房間時發送的消息內容*/ var welcome = JSON.stringify({ //加入房間時的歡迎消息 nickname:nickname, //用戶名 content:text, //消息內容 target:roomid, //推送到目標房間 flag:"chatroom"}); //推送標識 /*定義退出房間發送的消息內容*/ var exitroom = JSON.stringify({ //退出房間 nickname:nickname, flag:"exitroom", roomid:roomid }); /*接收服務器的消息*/ socket.onmessage=function(ev){ var obj = eval( '('+ev.data+')' ); addMessage(obj) }; /*當服務端執行onopen后觸發此方法*/ socket.onopen = function(){ socket.send(welcome); }; /*發送按鈕被點擊觸發點擊事件*/ $(".ensure button").click(function(){ ensure(); }); /*監聽回車事件,按回車點擊發送按鈕*/ $("body").keyup(function (event) {//監聽回車鍵 if (event.keyCode == "13") {//keyCode=13是回車鍵 $(".ensure button").trigger("click"); } }); /*發送消息的方法*/ function ensure(){ //獲取輸入框的內容 var txt = $(".center-input").val() if(txt==''){ alert("不能發送空內容") }else{ //構建一個標准格式的JSON對象 var obj = JSON.stringify({ nickname:nickname, //用戶名 content:txt, //消息內容 flag:'chatroom', //標識--chatroom代表是聊天室的消息 target:roomid //消息推送的目的地 }); // 向服務器發送消息 socket.send(obj); // 清空消息輸入框 $(".center-input").val("") // 消息輸入框獲取焦點 $(".center-input").focus(); } } /*向消息顯示區域添加消息*/ function addMessage(msg){ if(msg.isSelf&&msg.content==""){ //該消息是自己發送的,並且內容為空 $(".center-info").append("<div class='welcome'>歡迎你加入群聊</div>"); refreshMember(msg.uname); //刷新成員 } if(!msg.isSelf&&msg.content==""){//該消息是別人發送的,並且內容為空 $(".center-info").append("<div class='welcome'>歡迎"+msg.nickname+"加入群聊</div>"); //刷新成員列表 refreshMember(msg.uname) } if(!msg.content==""){ //內容不為空時 var align; if(msg.isSelf){ align = "right"; }else{ align = "left"; } $(".center-info").append( "<div class='basicInfo' style=float:"+align+">"+ "<div class='basicInfo-left' style=float:"+align+">"+ "<img src='img/touxiang.jpg'>"+ "</div>"+ "<div class='basicInfo-right' style=float:"+align+">"+ "<div class='username' style=text-align:"+align+">"+ "<span>"+msg.nickname+"</span> "+ "<span>"+msg.date+"</span>"+ "</div>"+ "<div class='context'>"+ "<span>"+ msg.content+ "</span>"+ "</div>"+ "</div>"+ "</div>" ); } if(msg.flag == "exitroom"){ //退出房間 $(".center-info").append("<div class='welcome'>"+msg.message+"</div>"); //刷新成員列表 refreshMember(msg.uname) } $(".center-info").scrollTop(999999); //讓滾動條始終保持在最下 } /*退出聊天室點擊事件*/ $(".exitroom").click(function(){ //退出房間 socket.send(exitroom); //向服務器發送退出房間的信號 location.href="/Chatroom/home/list.do"; //跳轉到前一個頁面 }) /*頁面關閉時觸發的事件*/ window.onbeforeunload = function(){ socket.send(exitroom); //向服務器發送退出房間的信號(該用戶下線) } /*刷新在線成員的方法*/ function refreshMember(data){ $(".member").html(""); for(var i=0;i<data.length;i++){ $(".member").append( "<div class='memberInfo'>"+ "<div class='userpic'>"+ "<img src='img/touxiang.jpg'>"+ "</div>"+ "<span class='username'>"+data[i]+"</span>"+ "</div>" ) } } }) </script> <body> <div class="body-left"> <div class="left-info"> <div class="exitroom"> <--退出房間 </div> <div class="roomname"> 歡迎來到:<h1 style="display: inline-block;" class="roomid">${roomid }</h1> </div> <div class="member"> <c:forEach items="${requestScope.memberlist }" var="member"> <div class="memberInfo"> <div class="userpic"> <img src="img/touxiang.jpg"> </div> <span class="username">${member.username }</span> <span style = "display:none">${member.userid }</span> </div> </c:forEach> </div> </div> </div> <div class="body-center"> <div class="center-info"> </div> <textarea class="center-input"></textarea> <div class="ensure"> <button>發送</button> </div> </div> <div class="body-right"> </div> <span class="uname" style="display:none">${sessionScope.uname }</span> </body> </html>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>springMVC</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <!-- 配置監聽器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> <!-- 配置過濾器,解決post請求亂碼問題 --> <filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置springmvc分發器,攔截請求 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <!-- <url-pattern>/</url-pattern> --> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- <servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.png</url-pattern> </servlet-mapping> --> </web-app>
源碼下載地址:
鏈接:https://pan.baidu.com/s/1x1uHFTGKcMhtpwTV3Vd7Xg
提取碼:c009