当前位置:   article > 正文

JAVA利用websocket实现多人聊天室、私信(附源码)_java实现聊天室框架图 有房间概念

java实现聊天室框架图 有房间概念

声明:此文为原创,转载请声明出处!

小编曾在毕业设计中用到了聊天室这个功能,现在稍作整理分享一下,希望能对大家有所帮助,有不足之处请指出

在学习websocket前,首先得知道它的一些基本操作,可参考此链接:

http://www.runoob.com/html/html5-websocket.html

为了方便演示,小编搭建了一个springmvc框架:


图1.1 聊天室首页

为了方便源码分享,小编舍弃了数据库部分,因此没有做创建房间的功能,直接写死了两个房间。


   图1.2 聊天室房间页


图1.3项目结构图

下面来看下代码:

如代码片段1 ,首先通过http://localhost:8080/Chatroom/home/list.do请求,访问index.jsp(图1.1 聊天室首页)

代码片段1:

  1. package com.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.ui.ModelMap;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.SessionAttributes;
  6. @Controller
  7. @RequestMapping("/home")
  8. @SessionAttributes("uname")
  9. public class ViewController {
  10. @RequestMapping("/list")
  11. public String cc(ModelMap model){
  12. return "index";
  13. }
  14. @RequestMapping("/room")
  15. public String h(ModelMap model,String uname,String roomid){
  16. model.put("uname",uname);
  17. model.put("roomid", roomid);
  18. return "room";
  19. }
  20. }

代码片段2:

  1. <%@ page language="java" contentType="text/html; charset=UTF-8"
  2. pageEncoding="UTF-8"%>
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  4. <html>
  5. <head>
  6. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  7. <script type="text/javascript" src="../js/jquery-3.2.1.min.js"></script>
  8. <title>Insert title here</title>
  9. </head>
  10. <script type="text/javascript">
  11. $(function() {
  12. $("span").click(function(){
  13. var uname = $("input").val();
  14. if(uname == ""){
  15. alert("请先输入用户名");
  16. }else {
  17. var roomid = $(this).html()
  18. location.href="/Chatroom/home/room.do?uname="+uname+"&roomid="+roomid;
  19. }
  20. })
  21. })
  22. </script>
  23. <style>
  24. span:HOVER{
  25. color: red;
  26. }
  27. span{
  28. cursor:pointer;
  29. }
  30. </style>
  31. <body>
  32. 用户名:<input type="text"> /*注:请先输入用户名,且保证用户名唯一,再点击下面的房间加入房间
  33. <h1><span>room1</span></h1>
  34. <h1><span>room2</span></h1>
  35. </body>
  36. </html>

代码片段3和4代码量较多,中间有详细的注解,不再做更多的解释

代码片段3:

  1. <%@ page language="java" contentType="text/html; charset=UTF-8"
  2. pageEncoding="UTF-8"%>
  3. <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  4. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  5. <html>
  6. <head>
  7. <%
  8. String path = request.getContextPath();
  9. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
  10. %>
  11. <base href="<%=basePath%>" />
  12. <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  13. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  14. <script type="text/javascript" src="js/jquery-3.2.1.min.js"></script>
  15. <link rel="stylesheet" type="text/css" href="css/chat.css" />
  16. <title>聊天室</title>
  17. </head>
  18. <script type="text/javascript">
  19. $(function(){
  20. var roomid=$(".roomid").html();//房间名
  21. var nickname = $(".uname").html();//自己的昵称
  22. var flag = "join";
  23. var info = flag + "|" +roomid + "|" +nickname;
  24. //建立一条与服务器之间的连接
  25. var socket = new WebSocket("ws://${pageContext.request.getServerName()}:${pageContext.request.getServerPort()}${pageContext.request.contextPath}/websocket/"+info);
  26. var text = "";
  27. var welcome = JSON.stringify({ //加入房间时的欢迎消息
  28. nickname:nickname, //用户名
  29. content:text, //消息内容
  30. target:roomid, //推送到目标房间
  31. flag:"chatroom"}); //推送标识
  32. var exitroom = JSON.stringify({ //退出房间
  33. nickname:nickname,
  34. flag:"exitroom",
  35. roomid:roomid
  36. })
  37. //接收服务器的消息
  38. socket.onmessage=function(ev){
  39. var obj = eval( '('+ev.data+')' );
  40. addMessage(obj)
  41. };
  42. //当服务端执行onopen后触发此方法
  43. socket.onopen = function(){
  44. socket.send(welcome);
  45. };
  46. //发送按钮被点击时
  47. $(".ensure button").click(function(){
  48. ensure();
  49. });
  50. $("body").keyup(function (event) {//监听回车键
  51. if (event.keyCode == "13") {//keyCode=13是回车键
  52. $(".ensure button").trigger("click");
  53. }
  54. });
  55. function ensure(){
  56. //获取输入框的内容
  57. var txt = $(".center-input").val()
  58. if(txt==''){
  59. alert("不能发送空内容")
  60. }else{
  61. //构建一个标准格式的JSON对象
  62. var obj = JSON.stringify({
  63. nickname:nickname, //用户名
  64. content:txt, //消息内容
  65. flag:'chatroom', //标识--chatroom代表是聊天室的消息
  66. target:roomid //消息推送的目的地
  67. });
  68. // 向服务器发送消息
  69. socket.send(obj);
  70. // 清空消息输入框
  71. $(".center-input").val("")
  72. // 消息输入框获取焦点
  73. $(".center-input").focus();
  74. }
  75. }
  76. function addMessage(msg){
  77. if(msg.isSelf&&msg.content==""){ //该消息是自己发送的,并且内容为空
  78. $(".center-info").append("<div class='welcome'>欢迎你加入群聊</div>");
  79. refreshMember(msg.uname); //刷新成员
  80. }
  81. if(!msg.isSelf&&msg.content==""){//该消息是别人发送的,并且内容为空
  82. $(".center-info").append("<div class='welcome'>欢迎"+msg.nickname+"加入群聊</div>");
  83. //刷新成员列表
  84. refreshMember(msg.uname)
  85. }
  86. if(!msg.content==""){ //内容不为空时
  87. var align;
  88. if(msg.isSelf){
  89. align = "right";
  90. }else{
  91. align = "left";
  92. }
  93. $(".center-info").append(
  94. "<div class='basicInfo' style=float:"+align+">"+
  95. "<div class='basicInfo-left' style=float:"+align+">"+
  96. "<img src='img/touxiang.jpg'>"+
  97. "</div>"+
  98. "<div class='basicInfo-right' style=float:"+align+">"+
  99. "<div class='username' style=text-align:"+align+">"+
  100. "<span>"+msg.nickname+"</span> "+
  101. "<span>"+msg.date+"</span>"+
  102. "</div>"+
  103. "<div class='context'>"+
  104. "<span>"+
  105. msg.content+
  106. "</span>"+
  107. "</div>"+
  108. "</div>"+
  109. "</div>"
  110. );
  111. }
  112. if(msg.flag == "exitroom"){ //退出房间
  113. $(".center-info").append("<div class='welcome'>"+msg.message+"</div>");
  114. //刷新成员列表
  115. refreshMember(msg.uname)
  116. }
  117. $(".center-info").scrollTop(999999); //让滚动条始终保持在最下
  118. }
  119. $(".exitroom").click(function(){ //退出房间
  120. socket.send(exitroom); //向服务器发送退出房间的信号
  121. location.href="/Chatroom/home/list.do"; //跳转到前一个页面
  122. })
  123. function refreshMember(data){
  124. $(".member").html("");
  125. for(var i=0;i<data.length;i++){
  126. $(".member").append(
  127. "<div class='memberInfo'>"+
  128. "<div class='userpic'>"+
  129. "<img src='img/touxiang.jpg'>"+
  130. "</div>"+
  131. "<span class='username'>"+data[i]+"</span>"+
  132. "</div>"
  133. )
  134. }
  135. }
  136. })
  137. </script>
  138. <body>
  139. <div class="body-left">
  140. <div class="left-info">
  141. <div class="exitroom">
  142. <--退出房间
  143. </div>
  144. <div class="roomname">
  145. 欢迎来到:<h1 style="display: inline-block;" class="roomid">${roomid }</h1>
  146. </div>
  147. <div class="member">
  148. <c:forEach items="${requestScope.memberlist }" var="member">
  149. <div class="memberInfo">
  150. <div class="userpic">
  151. <img src="img/touxiang.jpg">
  152. </div>
  153. <span class="username">${member.username }</span>
  154. <span style = "display:none">${member.userid }</span>
  155. </div>
  156. </c:forEach>
  157. </div>
  158. </div>
  159. </div>
  160. <div class="body-center">
  161. <div class="center-info">
  162. </div>
  163. <textarea class="center-input"></textarea>
  164. <div class="ensure">
  165. <button>发送</button>
  166. </div>
  167. </div>
  168. <div class="body-right">
  169. </div>
  170. <span class="uname" style="display:none">${sessionScope.uname }</span>
  171. </body>
  172. </html>

代码片段4:

  1. package com.controller;
  2. import java.io.IOException;
  3. import java.text.SimpleDateFormat;
  4. import java.util.ArrayList;
  5. import java.util.Date;
  6. import java.util.List;
  7. import java.util.concurrent.ConcurrentHashMap;
  8. import javax.websocket.OnClose;
  9. import javax.websocket.OnError;
  10. import javax.websocket.OnMessage;
  11. import javax.websocket.OnOpen;
  12. import javax.websocket.Session;
  13. import javax.websocket.server.PathParam;
  14. import javax.websocket.server.ServerEndpoint;
  15. import net.sf.json.JSONObject;
  16. @ServerEndpoint("/websocket/{info}")
  17. public class WebSocketService {
  18. private static SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");//创建时间格式对象
  19. //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketService对象。
  20. //创建一个房间的集合,用来存放房间
  21. private static ConcurrentHashMap<String,ConcurrentHashMap<String, WebSocketService>> roomList = new ConcurrentHashMap<String,ConcurrentHashMap<String, WebSocketService>>();
  22. //与某个客户端的连接会话,需要通过它来给客户端发送数据
  23. private Session session;
  24. //重新加入房间的标示;
  25. private int rejoin = 0;
  26. static {
  27. roomList.put("room1", new ConcurrentHashMap<String, WebSocketService>());
  28. roomList.put("room2", new ConcurrentHashMap<String, WebSocketService>());
  29. }
  30. /**
  31. * 用户接入
  32. * @param session
  33. */
  34. @OnOpen
  35. public void onOpen(@PathParam(value = "info") String param,Session session){
  36. this.session = session;
  37. String flag = param.split("[|]")[0]; //标识
  38. String member = param.split("[|]")[1]; //成员名
  39. if(flag.equals("join")){
  40. String user = param.split("[|]")[2];
  41. joinRoom(member,user);
  42. }
  43. }
  44. //加入房间
  45. public void joinRoom(String member,String user){
  46. ConcurrentHashMap<String, WebSocketService> r = roomList.get(member);
  47. if(r.get(user) != null){ //该用户有没有出
  48. this.rejoin = 1;
  49. }
  50. r.put(user, this);//将此用户加入房间中
  51. }
  52. public void sendMessage(String message) throws IOException {
  53. this.session.getBasicRemote().sendText(message);
  54. }
  55. /**
  56. * 接收到来自用户的消息
  57. * @param message
  58. * @param session
  59. * @throws IOException
  60. */
  61. @OnMessage
  62. public void onMessage(String message,Session session) throws IOException{
  63. //把用户发来的消息解析为JSON对象
  64. JSONObject obj = JSONObject.fromObject(message);
  65. if(obj.get("flag").toString().equals("exitroom")){ //退出房间操作
  66. String roomid = obj.get("roomid").toString();
  67. //将用户从聊天室中移除
  68. int f2 = 1;
  69. roomList.get(roomid).remove(obj.get("nickname").toString());//将用户直接移除
  70. if(roomList.get(roomid).size() == 0){//判断房间该房间是否还有用户,如果没有,则将此房间也移除
  71. f2 = 2;
  72. }
  73. if(f2 == 1){ //证明该房间还有其它成员,则通知其它成员更新列表
  74. obj.put("flag","exitroom");
  75. String m = obj.get("nickname").toString()+" 退出了房间";
  76. obj.put("message", m);
  77. ConcurrentHashMap<String, WebSocketService> r =roomList.get(roomid);
  78. List<String> uname = new ArrayList<String>();
  79. for(String u:r.keySet()){
  80. uname.add(u);
  81. }
  82. obj.put("uname", uname.toArray());
  83. for(String i:r.keySet()){ //遍历该房间
  84. r.get(i).sendMessage(obj.toString());//调用方法 将消息推送
  85. }
  86. }
  87. }else if(obj.get("flag").toString().equals("chatroom")){ //聊天室的消息 加入房间/发送消息
  88. //向JSON对象中添加发送时间
  89. obj.put("date", df.format(new Date()));
  90. //获取客户端发送的数据中的内容---房间号 用于区别该消息是来自于哪个房间
  91. String roomid = obj.get("target").toString();
  92. //获取客户端发送的数据中的内容---用户
  93. String username = obj.get("nickname").toString();
  94. //从房间列表中定位到该房间
  95. ConcurrentHashMap<String, WebSocketService> r =roomList.get(roomid);
  96. List<String> uname = new ArrayList<String>();
  97. for(String u:r.keySet()){
  98. uname.add(u);
  99. }
  100. obj.put("uname", uname.toArray());
  101. if(r.get(username).rejoin == 0){ //证明不是退出重连
  102. for(String i:r.keySet()){ //遍历该房间
  103. obj.put("isSelf", username.equals(i));//设置消息是否为自己的
  104. r.get(i).sendMessage(obj.toString());//调用方法 将消息推送
  105. }
  106. }else{
  107. obj.put("isSelf", true);
  108. r.get(username).sendMessage(obj.toString());
  109. }
  110. r.get(username).rejoin = 0;
  111. }
  112. }
  113. /**
  114. * 用户断开
  115. * @param session
  116. */
  117. @OnClose
  118. public void onClose(Session session){
  119. }
  120. /**
  121. * 用户连接异常
  122. * @param t
  123. */
  124. @OnError
  125. public void onError(Throwable t){
  126. }
  127. }

在代码片段4中,可能存在比较难理解的地方,小编通过自己的理解来画图,希望能够帮到大家。


图1.3 websocket执行流程


图1.4 用户加入房间


图1.5消息推送

私信、即时通信都可以利用websocket完成,原理跟聊天室是完全一样的。在此不再做更多的阐述~

小编的编辑经验尚浅,所以此文比较粗糙。不足之处请各位看官指出,不胜感激。

到此,聊天室的功能就演示完了,另附上项目源码

链接:https://pan.baidu.com/s/1NUgzDfU8IEhVHHn8BhddwA 密码:yk7o

环境:eclispe+jdk1.8+tomcat8.

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号