使用WebSocket制作简单的聊天室
一、功能需求
①第一次进入聊天室,输入用户的昵称,用于聊天消息显示名称
②用户进入系统后,系统需要提示所有用户,有新用户进入系统了
③用户发送消息时要实时的显示在所有用户的聊天记录屏上
④用户退出系统或者关闭页面,系统需要提示所有用户有用户退出系统了。
传统开发方式的实现思路:
1.用户的昵称需要保存到数据库或者其他存储介质
2.所有浏览器客户端要定时的去轮询服务器获取新进入的用户信息和他们发送的消息。
3.所有浏览器客户端要定时的去轮询服务器获取新进入的用户信息和他们发送的消息。
4.从数据库或者其他存储介质删除删掉用户记录,客户端还需要定时的去轮询服务器端,获取新的消息。
二、WebSocket实现直播聊天室
- JavaWeb项目,前后台通信是基于Http协议
- http协议只是客户端去请求服务器,服务器基于请求来响应客户端返回数据(ajax轮询)(服务端不能主动的向客户端发送消息)
使用websocket:
- websocket是html5新增加的一个通信协议,目前所有的主流浏览器都支持。
- websocket协议是基于TCP的一种新的网络协议,他实现了浏览器与服务器的一个全双工(full-duplex)通信(长连接)(允许服务器端主动发送消息给客户端)。
全双工:客户端可以请求服务端并向服务端发送消息,服务端也可以往客户端发送消息。
三、代码实现
服务端主要代码:ChatRoomServer:
package com.hcx.chat;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/chatRoomServer") // 声明websocket某个服务端的地址
public class ChatRoomServer { // 多例
private boolean firstFlag = true;
private Session session;
private String userName;
// 记录此次聊天室的服务端有多少个连接:有用户发送消息,需要把所有连接都开通,把此消息发送给其他所有在线的用户
// key代表此次客户端的session,value代表此次连接对象
private static final HashMap<String, Object> connectMap = new HashMap<String, Object>();
// 保存所有的用户昵称信息
// key是sessionId,value:用户名
private static final HashMap<String, String> userMap = new HashMap<String, String>();
// 服务端收到客户端连接请求,连接成功后会执行此方法
@OnOpen
public void start(Session session) {
// 保存好当前的连接
this.session = session;
// 把当前连接放入connectMap中
connectMap.put(session.getId(), this);
}
// 接收客户端发送的消息
@OnMessage
public void chat(String clientMessage, Session session) {
ChatRoomServer client = null;
// 判断客户端是不是第一次传值
if (firstFlag) {
this.userName = clientMessage;
// 将新进来的用户保存到用户map中:
userMap.put(session.getId(), userName);
// 往客户端发送消息:构造发送给客户端的提示信息
String message = htmlMessage("系统消息", userName + "进入了聊天室");
// 将消息广播给所有的用户(所有的用户都在userMap中)
for (String connectKey : connectMap.keySet()) {
client = (ChatRoomServer) connectMap.get(connectKey);
// 给对应的web端发送一个文本信息message
try {
client.session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
// 输入昵称以后,就代表firstFlag = false;
firstFlag = false;
} else {
// 往客户端发送消息:构造发送给客户端的提示信息
String message = htmlMessage(userMap.get(session.getId()), clientMessage);
// 将消息广播给所有的用户(所有的用户都在userMap中)
for (String connectKey : connectMap.keySet()) {
client = (ChatRoomServer) connectMap.get(connectKey);
// 给对应的web端发送一个文本信息message
try {
client.session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// ws.clos事件,会触发后台的标注OnClose的方法
@OnClose
public void close(Session session) {
// 当某个用户退出时,对其他用户进行广播
String message = htmlMessage("系统消息", userMap.get(session.getId()) + "退出了聊天室");
userMap.remove(session.getId());
connectMap.remove(session.getId());
ChatRoomServer client = null;
// 将消息广播给所有的用户
for (String connectKey : connectMap.keySet()) {
client = (ChatRoomServer) connectMap.get(connectKey);
// 给对应的web短发送一个文本信息(message)
try {
client.session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String htmlMessage(String userName, String message) {
StringBuffer messageBuffer = new StringBuffer();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
messageBuffer.append("<div class='record_item'>");
messageBuffer.append("<p class='record_item_time'>");
messageBuffer.append("<span>" + sf.format(new Date()) + "</span>");
messageBuffer.append("</p>");
messageBuffer.append("<div class='record_item_txt'>");
messageBuffer.append("<span class='avatar'>" + userName + "</span>");
messageBuffer.append("<p>");
messageBuffer.append("<span class='txt'>" + message + "</span>");
messageBuffer.append("</p>");
messageBuffer.append("</div>");
messageBuffer.append("</div>");
return messageBuffer.toString();
}
}
前端主要代码:chat.html
<body>
<canvas id="dot"></canvas>
<div class="wrap">
<div class="header">
<span>我的聊天室</span> <a href="javascript:void(0)" id="close_btn"></a>
</div>
<div class="main">
<div class="right">
<div class="time">
<i class="time_bg"
style="background: url(img/clock.png) no-repeat;"></i> <span>聊天记录</span>
</div>
<!-- 聊天记录 -->
<div class="right_record" id="outputMessage"></div>
</div>
</div>
<!-- content tool/txt -->
<div class="content">
<div class="content_tool">
<div class="tool_font">
<i class="bg" style="background: url(img/font.png) no-repeat;"></i>
</div>
<div class="tool_emjo">
<i class="bg" style="background: url(img/emjo.png) no-repeat;"></i>
</div>
</div>
<div class="content_text" contenteditable="true" id="inputMessage">
</div>
<div class="content_btn">
<div class="content_btn_text">
<i>自己的聊天室</i>
</div>
<div class="btns">
<div class="send btn_item" onclick="getMessage();">
<span>发送</span><span>(S)</span>
</div>
<div class="close btn_item">
<span>关闭</span><span>(C)</span>
</div>
</div>
</div>
</div>
</div>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.js"></script>
<script src="js/dot.js"></script>
<script type="text/javascript">
Dot("dot", {
cW : window.innerWidth,
cH : window.innerHeight,
dotColor : '#DE0D59'
});
// websocket实现聊天,客户端需要做的事情,总结起来,其实就是如下几件
// 获取连接 new WebSocket() (获取服务端连接)
//服务端地址和请求类型
var wsUrl = "ws://localhost:8080/chat/chatRoomServer";
//客户端与服务端建立连接 ,建立连接以后,他会出发一个ws.onopen事件
var ws = new WebSocket(wsUrl);
//连接成功后 ,提示浏览器客户端输入昵称
ws.onopen = function() {
var userName = prompt("请给自己取一个名字 : ");
//将userName发送给服务端
ws.send(userName);
}
//客户端收到服务器发送的消息
ws.onmessage = function(message) {
//alert(message.data);
//message.data
//获取以后,在客户端显示
outputMessage.innerHTML = outputMessage.innerHTML + message.data;
//解决滚动条问题
var msg = document.getElementById("outputMessage");
//差值 溢出的高度减掉可视的高度
var distance = msg.scrollHeight - msg.offsetHeight;
//将差值赋给滚动条的高度
msg.scrollTop = distance;
}
//获取某个用户输入的聊天内容,并发送服务端,让服务端广播给所有人
function getMessage() {
var inputMessage = document.getElementById("inputMessage").innerText;
//alert(inputMessage);
if (typeof (inputMessage) == "undefined") {
alert("请输入您要发送的消息");
} else {
//获取消息以后,要发送给服务端,然后广播给所有用户
ws.send(inputMessage);
//清空文本框
document.getElementById("inputMessage").innerText = "";
}
}
//当关闭页面或者用户退出时,会执行一个ws.close()方法
window.onbeforeunload = function() {
ws.close();//该方法会触发后台的标注了OnClose的方法
}
//按回车发送消息
document.onkeyup = function(e) {
if (e.keyCode == 13) {
getMessage();
}
}
</script>
</body>