网上聊天室设计思路
平时经常性的使用聊天软件如QQ,微信,或是游戏里各个区的公共频道,于是突发奇想,想要自己搞一个简易版聊天项目,所以现在开搞起来。
需求分析
1.想要进行聊天,势必需要有人才能聊起来,因此需要用户,且用户可以注册,登录(注册用户,打开主页,看到登录页面)
2.登录成功之后,就可以进入主页面
3.主页面显示现在用户关注的频道列表,即关注之后,才可以在此频道发言。
4.点击其中一个频道之后,就可以在此频道发消息
5.在频道中还可以看到其他人的发言,即公共消息群内人均可以看到
6.当用户退出,在他下一次登录之后,可以查阅在他不在的这段时间当中,大家都发了生命消息。
协议选择
要想实现网页聊天,则需要一个应用层协议进行数据传输
1号备选协议HTTP:
- http可以实现消息推送√
但http适合网页聊天吗?
答案是,可以实现,但耗费资源较为多,且效率低。
http是典型的一问一答模式,即如果客户端没有主动向服务器请求,服务器是不会给予响应的。
但http中又有轮询(规定一定周期:1s一次询问,让客户端向服务器发送请求),这样看起来就像是服务器主动给客户端推送消息一样,但本质上还是一问一答,只不过问的那一步被隐藏了起来。
但这种消息推送效率相当低,因为没有用户会不间断的发消息,客户端大部分情况都是没有新消息的,所以在没有消息发送时,问服务器 就是在做无用功。且如果采用短轮询连接成本也相当大。
因此http不适合用于聊天系统。
- http适合消息推送×
2号备选协议WebSocket:
- websocket可以实现消息推送√
那websocket适合网页聊天吗?
首先websocket是一个应用层协议,其次websocket可以完成不问就答 。
websocket允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
- websocket适合消息推送√
好啦~ 协议选择完毕~~
如何使用 WebSocket
问题一,怎么让应用层使用WebSocket协议?
WebSocket 是独立的、创建在 TCP 上的协议。
Websocket 是基于Http协议.HTML5
Websocket 通过HTTP/1.1 协议的101状态码进行握手。
为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)。
由于以上原因可以得出,WebSocket是需要通过升级http协议才能进行使用。
升级协议:客户端尝试针对服务器发送ws://这样的url请求时,此时发的仍然是一个HTTP请求,但是请求中会带有Upgrade字段来请求升级协议,服务器端如果同意升级,就会返回一个101状态码的响应报文。
此时websocket连接建立完毕,就可以传输数据。
客户端使用websocket也需要url来指定服务器的位置
这个url是以ws://开头,ws是websocket的缩写
问题二,TCP socket API主要操作和web socket API主要操作有什么区别?
TCP socket API主要操作 | web socket API主要操作 |
---|---|
connect | connect |
close | close |
write | send |
read | receive |
- websocket api类似于原生
- 本质上是给浏览器提供了更加灵活的网络传输机制
(本来在网页中只能操作HTTP协议)- websocket给网页更多的操作网络的方式
- websocket想象成一个类似于tcp这样的一个基础协议,在这个基础协议上,用户可以构件更复杂的网络通信程序=>例如实现消息推送
问题三,WebSocket的配置?
在使用WebSocket
第一步就是要引入第三方依赖,使用maven进行管理
配置 :
<!-- https://mvnrepository.com/artifact/javax.websocket/javax.websocket-api -->
<dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.1</version><scope>provided</scope><!--表示开发时需要使用,部署到Tomcat上的时候,就不需要带此jar包(tomcat内置这个jar包)-->
</dependency>
第二步使用注解**@ServletEndpoint**
在web开发中,可以使用@ServletEndpoint注解修饰指定的类,同时在注解中使用value参数来指定一个具体的路径
后续html页面就可以通过这个路径来访问到服务器
问题四,webSocket中的方法以及应用
服务器(java)
方法 以及作用 | 对应注解(不需要程序员主动调用) |
---|---|
a> onOpen,建立连接时使用 | @OnOpen |
b> onClose,关闭连接时使用 | @OnClose |
c> onMessage,收到消息时调用 | @OnMessage |
d> onError,触发异常错误时调用 | @OnError |
客户端(JS)
方法 以及作用 |
---|
a)onpen,建立连接时调用 |
b)onclose,关闭连接时使用 |
c) onmessage,收到消息时调用 |
d) onerror,触发异常错误时调用 |
如何让服务端主动发消息
1.设计客户端和服务端
2.服务器如何给客户端返回消息
- 服务器端收到客户端发的消息,就会触发onMessage函数,调用sendText就把数据返回给了浏览器
- 浏览器收到服务器的响应之后,就会触发onmessage函数,从而打印收到的内容
session.getBasicRemote().sendText("");
//把一个文本数据写回到浏览器
//需要程序员主动调用,调用这个方法就能写回数据,无需客户端先发送请求
websocket.send()给服务器发送数据
数据库设计思路
1.围绕软件需求,分析实体
实体有:
User(用户信息)
Channel(频道)
Massage(消息)
2.分析实体之间的关系(一对一,一对多,多对多)
User和Channel:多对多(但由于还未实现关注频道功能,暂时的将其简化为任何用户都可进入所有频道)
User和Message:一对多
Channel和Message:一对多(未实现消息群发)
3.画E-R图
4.创建表结构
create database chatroom;
use chatroom;
drop table if exists user;
create table user(userId int primary key auto_increment,name varchar (50) unique ,password varchar (50), nickname varchar (50), lastLogout Datetime --lastLogout 上次退出的时间,用来实现历史记录功能);
drop table if exists channel;
create table channel( channelId int primary key auto_increment, channelName varchar (50) );
drop table if exists message;
create table message(messageId int primary key auto_increment,userId int,--谁发送的消息channelId int,context text,--消息正文 sendTime datetime --消息发送时间)
5.连接数据库,并实现数据库中的相关代码
- 创建DBUtil类管理连接
public static DataSource getDataSource() {// 获取到这个单例的 DataSource 实例.if (dataSource == null) {synchronized (DBUtil.class) {if (dataSource == null) {dataSource = new MysqlDataSource();// 必须要告诉代码, 数据库是谁, 以啥样的方式登陆上去.((MysqlDataSource)dataSource).setURL(URL);((MysqlDataSource)dataSource).setUser(USERNAME);((MysqlDataSource)dataSource).setPassword(PASSWORD);}}}return dataSource;}
public static Connection getConnection () {try {return getDataSource().getConnection();} catch (SQLException e) {e.printStackTrace();}return null;}
- 在IDEA中创建实体类
设计API(个人所设API仅供参考)
确定客户端都能给服务端发哪些种类的请求。
服务器收到请求之后该返回什么样的响应。
1. 用户注册
请求格式
POST /register
{
name:xxx,
password:xxx,
nickName:xxx
}
响应格式
HTTP/1.1 200 OK
{
ok:1, //1表示成功,0表示失败(此处约定方式只是常见的方式之一当然也可以约定成其他格式)
reason:xxx //如果失败,reason就是一个具体的失败原因
}
2. 登录
请求格式
POST /login
{
name:xxx,
password:xxx
}
响应格式
HTTP/1.1 200 OK
{
ok:1,
reason:xxx,
userId:1,
name:xxx,
nickName:xxxx
}
3. 检测登录状态
请求格式
GET / login
Cookie:JSESSION=XXXXX
响应格式
HTTP /1.1 200 OK
{
ok:1,
reason:xxx,
userId:1,
name:xxx,
nickName:xxx
}
4.注销功能
未实现!
5.新增频道
请求格式
POST /channel
{
channelName:xxx
}
响应格式
HTTP/1.1 200 OK
{
ok:1,
reason:xxxx
}
6.获取频道列表
请求格式
GET /channel
响应格式
HTTP/1.1 200 OK
[
{
channelID;1,
channelName:“ASRM”
},
{
channelID;2,
channelName:“ASMR”
}
]
7.删除频道
请求格式
DELETE /channel?channlId=1
响应格式
HTTP/1.1 200 ok{
}
8.建立websocket链接(传输消息的准备工作)
这就不是HTTP协议了
ws://[ip]:[port]/message/{userId}
每个登录中的用户,都需要有自己的连接,把连接之间使用userId来区分。
9.发送/接收消息格式
为了简单起见,把发送和接受格式统一成一样的了(按理说应该不一样)
JSON:当前这个message是通过websocket来传输,而不是HTTP协议了。
{
userId:1,//消息是谁发的
nickName:xxx,//消息发送者的昵称
channelId:xxx,//消息要往哪个频道发送
content:“123asd”,//消息内容
}
!待补充!!!
实现接口
测试接口
网上聊天室测试