携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情 >>
前言
websocket是一种网络通信协议。我们更多使用的是HTTP协议,http是一个无状态的、无连接、单向应用层协议。只能客户端向服务端发请求,而当服务器想向客户端发请求时,大多采用轮询的访问方式,轮询耗资源、浪费时间。而websocket是一个双向通讯的协议,服务端可以向客户端发请求,客户端也可以给服务器发请求。
websocket协议
协议分两部分
- 握手:基于http协议
- 数据交互
客户端的实现
1.创建一个websocket对象
var ws = new WebSocket("ws://localhost:8088/chat");
- 注意:当是https协议时,ws需改为wss
2.websocket事件
- open:建立连接时触发 ex:
ws.onopen = function () {
$('#chatMeu').html('<p>用户:' + username + "<span style='float: right;color: greenyellow; height: 20px'>在线</span></p>")
};
- 当连接建立完成后需要在客户端页面上渲染XXX用户上线了
- message:客户端接受服务端消息的时候触发
ex:
ws.onmessage = function (evt) {
//获取数据
var dataStr = evt.data;
//将dataStr转为json对象
//jsonData 格式举例(需要判断是否是系统消息):{“systemMsgFlag”: false, "fromName": "YYJ", "message": “你在哪里呀?”}
var jsonData = JSON.parse(dataStr);
//判断是否是系统消息
if (jsonData.systemMsgFlag) {
//是系统消息,处理
//1.好友列表处理
var friendsList = "";
var systemMsg = "";
var allNames = jsonData.message;
for (var name of allNames) {
if (username !== name) {
friendsList += "<li><a style='text-decoration: underline' onclick='chatWith(""+ name +"")'>"+name+"</a></li>";
systemMsg += "<li>"+name+" 上线啦!</li>";
}
}
//渲染页面
$("#friendsList").html(friendsList);
$("#systemMsg").html(systemMsg);
}else {
//不是系统消息,是发送给指定用户的消息,示例值:{“systemMsgFlag”: false, "fromName": "YYJ", "message": “你在哪里呀?”}
var data = jsonData.message;
var cnt = "<div class="atalk"><span id="asay">"+data+"</span></div>"
if (toName === jsonData.fromName) {
$("#chatCnt").append(cnt);
}
//sessionStorage
var chatData = sessionStorage.getItem(jsonData.fromName);
if (chatData != null) {
cnt = chatData + cnt;
}
sessionStorage.setItem(jsonData.fromName ,cnt);
}
};
- 当收到服务端发来的消息时,需要去渲染到客户端,注意此处需要做不同的用户之间的隔离,否则他人发消息全局都可以看到了。
- close:关闭连接时触发
//关闭连接触发
ws.onclose = function () {
$('#chatMeu').html('<p>用户' + username + "<span style='float: right;color: #d50a0a; height: 20px'>离线</span></p>")
};
- 关闭连接后需要将用户下线
- error:发生错误时触发
3.方法
- send()方法
ex:
//发送按钮点击
$("#submit").click(function () {
//获取发送输入框中的内容
var data = $("#tex_content").val();
//点击发送后,清空输入内容框架
$("#tex_content").val("");
var sendJson = {"toName": toName, "message": data};
//聊天框显示发送内容
var cnt = "<div class="btalk"><span id="bsay">" + data+ "</span></div>";
$("#chatCnt").append(cnt);
var chatData = sessionStorage.getItem(toName);
if (chatData != null) {
cnt = chatData + cnt;
}
sessionStorage.setItem(toName,cnt);
//发送数据给服务端
ws.send(JSON.stringify(sendJson));
});
服务端实现
- tomcat7.05版本就支持了websocket,Java中websocket应用由一系列websocket Endpoint组成。
我们有两种方式定义websocket
- 编程方式:继承类javax.websocket.Endpoint且实现其方法。
- 注解方式:定义一个类,添加ServerEndpoint注解
Endpoint在websocket握手时创建,websocket方法(注解方式)
- @onClose:当会话关闭后调用
- @onOpen:当开启一个新的会话时调用,该方法就是当客户端与服务端连接成功后的调用方法
- @onError:当连接过程中遇到异常调用
代码展示(文章末尾会放上源码):
- 建立session
@OnOpen
// 连接建立时被调用
public void onOpen(Session session, EndpointConfig config) {
// 将局部的session赋值给成员session
this.session=session;
// 获取httpsession对象
HttpSession httpsession = (HttpSession)config.getUserProperties().get(HttpSession.class.getName());
this.httpSession=httpsession;
// 从httpsession对象中获取用户名
String username = (String) httpSession.getAttribute("username");
// 将当前对象存到这个容器中 所有在线对象
onlineUsers.put(username,this);
// 将当前在线用户的用户名推送给所有客户端
// 1.获取消息
String message = MessageUtils.getMessage(true, null, getNames());
// 2.调用方法进行系统消息的推送
broadcastAllUsers(message);
}
- 收发消息:
@OnMessage
// 接收到客户端发送的数据时
public void onMessage(String message,Session session){
ObjectMapper mapper = new ObjectMapper();
try {
// 将message转换成message对象
Message mess = mapper.readValue(message, Message.class);
// 获取要将数据发送给的用户
String toName = mess.getToName();
// 获取消息数据
String data = mess.getMessage();
// 获取当前登录的用户
String userName=(String)httpSession.getAttribute("username");
// 获取推送给指定用户的消息格式数据
String resultMessage = MessageUtils.getMessage(false, userName, data);
// 发送数据
onlineUsers.get(toName).session.getBasicRemote().sendText(resultMessage);
}
catch (Exception e){
e.printStackTrace();
}
}
- 关闭连接
@OnClose
// 连接关闭
public void onClose(Session session){
String username = (String) httpSession.getAttribute("username");
//从容器中删除指定的用户
onlineUsers.remove(username);
String message = MessageUtils.getMessage(true, null, getNames());
broadcastAllUsers(message);
}
- 以上代码可以得知,用户会话都是存在session中的,通过session识别是哪个用户。