JavaWeb–使用Websocket实现在线聊天功能

JavaWeb–使用Websocket实现在线聊天功能首先简单介绍下 WebSocket WebSocket 是 HTML5 中内容 是基于 TCP 的一种新的网络协议 它支持全双工 长连接的通信 在它出现之前 实时消息发送与接收通过轮询实现 但是频繁与服务器建立连接十分消耗资源 因此 WebSocket 出现了 在不断开连接的情况下 处于连接的用户可以任意发送消息 从而实现了在线聊天的功能

首先简单介绍下WebSocket,WebSocket是HTML5中内容,是基于TCP的一种新的网络协议,它支持全双工、长连接的通信。在它出现之前,实时消息发送与接收通过轮询实现,但是频繁与服务器建立连接十分消耗资源。因此WebSocket出现了,在不断开连接的情况下,处于连接的用户可以任意发送消息,从而实现了在线聊天的功能。

因为我们JavaWeb课程作业是实现在线聊天功能,看到这题目我内心是崩溃的,不停百度,偶然发现Tomcat中自带了一个WebSocket的小例子, 有点小激动,运行一看,它竟然是群聊的。。。没办法,只好查看他的源代码,经过几天的推敲,终于看懂他的代码,并改写成一对一私聊。下面上代码 

var Chat = {}; Chat.socket = null; // 创建一个websocket实例 Chat.connect = (function(host) { if ('WebSocket' in window) { Chat.socket = new WebSocket(host); } else if ('MozWebSocket' in window) { Chat.socket = new MozWebSocket(host); } else { Console.log('Console.log:你的浏览器不支持WebSocket'); return; } Chat.socket.onopen = function(){ Console.log('Console.log:WebSocket链接打开'); //按下回车键发送消息 document.getElementById('chat').onkeydown = function(event) { if (event.keyCode == 13) { Chat.sendMessage(); } }; }; Chat.socket.onclose = function () { document.getElementById('chat').onkeydown = null; Console.log('Console.log:WebSocket前端链接关闭'); }; Chat.socket.onmessage = function (message) { Console.log(message.data); }; }); Chat.initialize = function() { //链接地址选择聊天页面的URL if (window.location.protocol == 'http:') { Chat.connect("ws://" + window.location.host + "/onLineChat/index.jsp"); } else { Chat.connect("wss://" + window.location.host + "/onLineChat/index.jsp"); } }; //发送消息函数,后面动态添加了发送好友的唯一ID Chat.sendMessage = (function() { var fid = $("#hidden-fid").val(); var messageContain = document.getElementById('chat').value; var message = messageContain +"-"+fid; if(fid==""){ alert("未选择发送消息的好友!"); return; }else{ if (messageContain != "") { Chat.socket.send(message); document.getElementById('chat').value = ''; }else{ alert("发送消息不能为空!"); } } }); var Console = {}; Console.log = (function(message) { var console = document.getElementById('console'); var p = document.createElement('p'); p.style.wordWrap = 'break-word'; p.innerHTML = message; console.appendChild(p); console.scrollTop = console.scrollHeight; }); //初始化函数 Chat.initialize(); document.addEventListener("DOMContentLoaded", function() { var noscripts = document.getElementsByClassName("noscript"); for (var i = 0; i < noscripts.length; i++) { noscripts[i].parentNode.removeChild(noscripts[i]); } }, false);

这是前端的JS代码,初始化WebSocket对象,监听对象的开启、关闭、发送消息等时间,将部分参数传给后台

package websocket.chat; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpSession; import javax.websocket.EndpointConfig; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import com.chat.common.HTMLFilter; import com.chat.controller.UserDAOImpl; import com.chat.entity.User; @ServerEndpoint(value = "/index.jsp",configurator=GetHttpSessionConfigurator.class) public class ChatAnnotation { private static final Log log = LogFactory.getLog(ChatAnnotation.class); private static final Map 
    
      connection = new HashMap 
     
       (); private String userId; private String nickname; private Session session; public ChatAnnotation() { } @OnOpen public void start(Session session,EndpointConfig config) { //获取HttpSession对象 HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName()); User user = (User)httpSession.getAttribute("user"); String uid = user.getId(); this.nickname = user.getUsername(); this.userId = uid; this.session = session; /*判断链接是否已经在链接队列中,存在就不加*/ if(!connection.containsKey(uid)){ connection.put(uid, this); String message = "* 你已经链接"; toConfirmFriend(message,userId); } } @OnClose public void end() { connection.remove(this.userId); } @OnMessage public void incoming(String message){ String[] all = message.split("-"); //要接收消息好友的ID String uid = all[1]; //判断好友的连接是否存在,不存在就提示好友掉线 if(connection.containsKey(uid)){ String filteredMessage = String.format("%s: %s", nickname, HTMLFilter.filter(all[0].toString())); sendCommonRecord(filteredMessage,uid,this.userId); }else{ ChatAnnotation client = connection.get(uid); String mes = String.format("* %s %s", client.nickname, "已经掉线!"); //将掉线信息发给自己 toConfirmFriend(mes,this.userId); } } @OnError public void onError(Throwable t) throws Throwable { log.error("Chat Error: " + t.toString(), t); } //发送给指定的好友 private static void toConfirmFriend(String msg,String uid) { ChatAnnotation client = connection.get(uid); try { synchronized (client) { client.session.getBasicRemote().sendText(msg); } } catch (IOException e) { log.debug("Chat Error: Failed to send message to "+client.nickname, e); try { client.session.close(); } catch (IOException e1) { // Ignore } } } //给聊天的双发发共同消息 private static void sendCommonRecord(String msg,String sid,String gid) { ChatAnnotation client_send = connection.get(sid); ChatAnnotation client_get = connection.get(gid); try { synchronized (client_send) { client_send.session.getBasicRemote().sendText(msg); } //将这条聊天记录保存在数据库 UserDAOImpl userDAOImpl = new UserDAOImpl(); userDAOImpl.saveCharRecord(msg, sid, gid); } catch (IOException e) { log.debug("Chat Error: Failed to send message to "+client_send.nickname, e); connection.remove(sid); try { client_send.session.close(); }catch (IOException e1) { // Ignore } } try { synchronized (client_get) { client_get.session.getBasicRemote().sendText(msg); } } catch (IOException e) { log.debug("Chat Error: Failed to send message to "+client_get.nickname, e); connection.remove(gid); try { client_get.session.close(); } catch (IOException e1) { // Ignore } } } } 
      
    

这是后台代码,使用注解方式监听前台的事件

重点:
1)Tomcat中connection是一个set,此处改写成了map,键名保存用户的唯一ID,键值保存用户连接对象,所以只要确定了用户ID,就能向该用户发送消息。创建连接向map中添加,断开链接从map中移除。
2)如何将用户ID从前台传到后台,这里有两种方法,借鉴了这篇博客 http://blog.csdn.net/_/article/details/

采用HttpSession的方法在ChatAnnotation中访问session的属性


3)前台发送消息,动态将发送消息的接受者ID加在消息内容结尾,后台接收到数据,将消息内容和用户ID分离开
4)改写发送消息的方法,实现向特定好友发送消息,方法是根据已经传过来的好友ID,从Map中获取指定的连接对象,调用该对象的sendText(msg);方法实现私聊。
5)因为有个导出聊天记录的功能,所以,每发送成功一条消息,这条消息将被存进数据库,永久记录下来。
//
这个是项目的完成图,已经实现了,前端是模仿网页版微信的。。。。
JavaWeb--使用Websocket实现在线聊天功能

——————————————下载地址——————————————————

源码下载:百度网盘 请输入提取码

提取码: xgck

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/176363.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午10:07
下一篇 2026年3月26日 下午10:07


相关推荐

  • 想入行3D游戏建模,看完这个你还敢想吗?

    想入行3D游戏建模,看完这个你还敢想吗?所有行业都是一样的,没有什么容易的,只不过这一行是偏向于技术的,一个有好的建模师月薪10k+是很常见的,这个需要有自己刻苦学习的成果。游戏建模前景在游戏模型行业,你基本不用担心找不到工作,因为游戏模型师人才缺口非常大。举个例子:游戏制作公司的人员配比大多数是这样的:比如100人的三维制作组,可能有60人在做模型贴图,10个人在K动画。只要你保证技能在手,一定是抢手的人才。在几年前游戏建模这个行业不仅仅缺人才,甚至连新手都非常稀缺,那个时候公司愿意招聘实习生,培养他们然后给公司干活,但是工资一定不会给开.

    2022年5月12日
    44
  • 【为什么需要FabricPath】FabricPath是思科 Nexus交换机上的一项技术特性,其目标是在保证二层环境的前提下,提高性能。来看看为什么数据中心需要FabricPath?

    【为什么需要FabricPath】FabricPath是思科 Nexus交换机上的一项技术特性,其目标是在保证二层环境的前提下,提高性能。来看看为什么数据中心需要FabricPath?为什么需要 FabricPath FabricPath 是思科 Nexus 交换机上的一项技术特性 其目标是在保证二层环境的前提下 提高性能 来看看为什么数据中心需要 FabricPath

    2026年3月19日
    2
  • android系统中toast是什么_android studio toast不显示

    android系统中toast是什么_android studio toast不显示Toast控件介绍Toast是Android系统提供的轻量级信息提醒机制,用于向用户提示即时消息,它显示在应用程序界面的最上层,显示一段时间后自动消失不会打断当前操作,也不获得焦点。使用Toast提示信息的实例代码:Toast.makeText(Context,Text,Time),show();这段代码首先调用了Toast的makeText方法用来设置提示信息,Context:表示应用程序环境的信息,就是当前组件的上下文环境,如果在Activity中使用的话,那么该参数可设置为”Activi

    2025年11月9日
    5
  • A站、B站、C站、D站、E站、F站、G站、H站、I站、J站、K站、L站、M站、N站、T站…Z站 ?

    A站、B站、C站、D站、E站、F站、G站、H站、I站、J站、K站、L站、M站、N站、T站…Z站 ?A站AcFun弹幕视频网,简称“A站”,成立于2007年6月,取意于AnimeComicFun,是中国大陆第一家弹幕视频网站。A站以视频为载体,逐步发展出基于原生内容二次创作的完整生态,拥有高质量互动弹幕,是中国弹幕文化的发源地;拥有大量超粘性的用户群体,产生输出了金坷垃、鬼畜全明星、我的滑板鞋、小苹果等大量网络流行文化,也是中国二次元文化的发源地。B站全称“哔哩哔哩(bilibili)”,是一家弹幕视频网站,前身是Mikufans,Miku也就是初音未来。主要是以鬼畜、动漫、.

    2022年8月23日
    28
  • 点击table的td单元格出现dialog弹窗,获取值后将值放回td单元格

    点击table的td单元格出现dialog弹窗,获取值后将值放回td单元格

    2021年7月15日
    104
  • java VM option

    -Xms256m-Xmx256m-XX:MaxNewSize=256m-XX:MaxPermSize=256m

    2022年4月9日
    58

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号