websocket實現多房間多人在線聊天室


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM