目录
一、需求分析
1.登录:同一个浏览器,即使多个标签页,保持相同的session。
2.对应消息频道可以收发消息,不同的消息频道不能收发。
3.历史记录查看,新登陆用户可以查看退出登陆后的消息
二、业务背景
1.张三要发消息给李四
实现
1.客户端点到点发消息
2.服务端转发消息(本项目方法)
使用http协议是不可以的因为
服务端的IP是开放在公网的,所有人都能访问
客户端的IP是不开放的
2.WebSocket实现消息推送流程
一.客户端和服务器端建立连接,只能客户发起请求,双方建立链接。
//客户端js代码
websocket = new WebSocket("ws://localhost:8080/java_chatroom/test/1");
1.1双方建立连接(双方通知打开连接的回调函数执行)
1.2客户端建立连接回调
//连接成功建立的回调方法
websocket.onopen = function(event){
setMessageInnerHTML("open");
}
1.3服务端建立连接回调,同时建立长连接Session
@OnOpen
public void onOpen(@PathParam("userId") String userId, Session session) {
this.userId = userId;
System.out.println("打开连接: " + userId);
}
二、双方的通信都是全双工,客户端和服务端都可以主动收发消息
2.1客户端发送消息
//发送消息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
2.2服务端发送消息
@OnMessage
public void onMessage(String message, Session session) throws IOException {
System.out.println("收到消息! " + userId + ": " + message);
session.getBasicRemote().sendText(message);
}
建立连接以后,就会有的会话对象,只要有sesiion引用就可以发消息,区别于Servlet的session
三、双方接收消息:被动接受(事件驱动的异步回调)
3.1:客户端接收消息
//接收到消息的回调方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
3.2:服务端接收消息
@OnMessage
public void onMessage(String message, Session session) throws IOException {
System.out.println("收到消息! " + userId + ": " + message);
//session.getBasicRemote().sendText(message);
}
三、前后端接口和数据库系统设计
1.用户相关的接口
1.注册
请求:
POST /register
{
name: xxx,
password: xxx,
nickName: "蔡徐坤",
signature: "我擅长唱跳rap篮球", }
响应:
HTTP/1.1 200 OK
{
ok: 1,
reason: xxx
}
2.登录 输入账号密码,点击登录按钮,调用的接口
请求:
POST /login
{
name: xxx,
password: xxx
}
响应:
HTTP/1.1 200 OK
{
ok: 1,
reason: xxx,
userId: xxx,
name: xxx,
nickName: xxx,
signature: xxx
}
3.检查登陆状态 页面初始化,调用的接口
请求:
GET /login
响应:
响应:
HTTP/1.1 200 OK
{
ok: 1,
userId: xxx,
name: xxx,
nickName: xxx,
signature: xxx
}
注销
请求:
GET /logout
响应:
HTTP/1.1 200 OK
{
ok: 1,
reason: xxx
}
2.频道相关接口
查找频道
请求:
GET /channel
响应:
HTTP/1.1 200 OK
[
{
channelId: 1,
channelName: xxx
},
{
channelId: 2,
channelName: xxx
}
]
3.数据库表的设计
user表
create table user (
userId int primary key auto_increment,
name varchar(50) unique,
password varchar(50),
nickName varchar(50), -- 昵称
iconPath varchar(2048), -- 头像路径
signature varchar(100),
lastLogout DateTime -- 上次登录时间
); -- 个性签名
频道表
create table channel (channelId int primary key auto_increment,
channelName varchar(50)
);
insert into channel values(null, '体坛赛事');
insert into channel values(null, '娱乐八卦');
insert into channel values(null, '时事新闻');
insert into channel values(null, '午夜情感');
信息表
create table message (messageId int primary key auto_increment,
userId int, -- 谁发的
channelId int, -- 发到哪个频道中
content text, -- 消息内容
sendTime DateTime default now() -- 发送时间
);
insert into message values (null, 1, 1, 'hehe1', now());
insert into message values (null, 1, 1, 'hehe2', now());
insert into message values (null, 1, 1, 'hehe3', now());
四、功能交互实现原理及代码展示
1.输入url访问主页
2.调用检查登陆状态接口
2.1参数ok:true时显示登录信息
3.响应ok:false不包含用户信息显示未登录界面
4.点击登录按钮显示登录框
5.输入账号密码,点击登录按钮调用login接口,创建session
请求:
POST /login
{
name: xxx,
password: xxx
}
响应:
HTTP/1.1 200 OK
{
ok: 1,
reason: xxx,
userId: xxx,
name: xxx,
nickName: xxx,
signature: xxx
}
6.参数ok:true表示登陆成功跳转至频道列表页面
7.查找频道信息初始化websocket
app.getChannels();
app.initWebSocket();
7.1查找频道接口
请求:
GET /channel
响应:
HTTP/1.1 200 OK
[
{
channelId: 1,
channelName: xxx
},
{
channelId: 2,
channelName: xxx
}
]
7.2调用初始化websocket方法,筛选频道
if('WebSocket' in window){
this.websocket = new WebSocket("ws://localhost:8080/java_chatroom/message/" + this.user.userId);
console.log("link success")
}else{
alert('Not support websocket')
}
//连接发生错误的回调方法
this.websocket.onerror = function () {
alert("连接发生错误!");
};
//连接成功建立的回调方法
this.websocket.onopen = function (event) {
console.log("连接建立成功");
}
//接收到消息的回调方法
this.websocket.onmessage = function (event) {
let message = JSON.parse(event.data);
app.messages.push(message);
//筛选频道
app.curChannelMessages = app.messages.filter((message, i) => {
if (message.channelId ==