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