1: webSocket
一般情况Web 应用的交互过程通常是客户端通过浏览器发出一个请求,服务器端接收请求后进行处理并返回结果给客户端,客户端浏览器将信息呈现到浏览器中
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage(mes?d?) 事件来接收服务器返回的数据
Websocket 使用 ws 或 wss 的统一资源标志符,类似于 HTTPS,其中 wss 表示在 TLS 之上的 Websocket
Websocket 使用和 HTTP 相同的 TCP 端口,可以绕过大多数防火墙的限制。默认情况下,Websocket 协议使用 80 端口;运行在 TLS 之上时,默认使用 443 端口。
相对于传统 HTTP 每次请求-应答都需要客户端与服务端建立连接的模式,WebSocket 是类似 Socket 的 TCP 长连接的通讯模式,一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输
客户端(发请求,建立链接):啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端(发请求,建立链接):啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)
ajax轮询 需要服务器有很快的处理速度和资源。(速度)
long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)
2:TCP 协议
面向连接、传输可靠(保证数据正确性)、有序(保证数据顺序)、传输大量数据(流模式)、速度慢、对系统资源的要求多,程序结构较复杂,
3:UDP 协议
(用户数据报协议,User Data Protocol):(类似发短信)
面向非连接 、传输不可靠(可能丢包)、无序、传输少量数据(数据报模式)、速度快,对系统资源的要求少,程序结构较简单 ,
UDP支持一对一,一对多,多对一和多对多的交互通信,
UDP的首部开销小,只有8个字节。
Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。
4:WebSocket与HTTP的关系
1. 都是一样基于TCP的,都是可靠性传输协议。
2. 都是应用层协议
3. WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。
4. WebSocket是需要浏览器和服务器握手进行建立连接的。而http是浏览器发起向服务器的连接,服务器预先并不知道这个
//创建连接
websocket = new WebSocket(webSocketMessageUrl);
//发送数据
建立连接后触发Socket.onopen 可以使用sent使用连接发送数据
//接收数据
并通过 onmessage 事件来接收服务器返回的数据
解决HTTP缺陷
非持久性 同步有延 消耗资源 无状态协议。 被动性
5: WebSocket 配置群聊的基本流程
第一步:导入websocket的依赖
第二步:websocket的配置
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
第三步:建立ServerEndpoint的java类,能够接受客户端发送过来的信息和发送给客户端信息
@ServerEndpoint("/webSocketMessage")
@Component
public class WebSocketMessage {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
private static CopyOnWriteArraySet<WebSocketMessage> webSocketSet = new CopyOnWriteArraySet<WebSocketMessage>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
private static final List<Map<String, Object>> users;
static {
users = new ArrayList<>();
}
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
Map<String, Object> maps = new HashMap<>();
maps.put("session", session);
maps.put("userId","");
maps.put("username","");
users.add(maps);//加入set中
addOnlineCount();//在线数加1
System.out.println("有新连接加入!当前在线人数为:" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
for (Map<String, Object> map : users){
Session session = (Session) map.get("session");
if(session == this.session){
ScoketMessage sm = new ScoketMessage();
sm.setType("close");
sm.setFromUser(map.get("username").toString());
sm.setMessage("用户"+map.get("username").toString()+"离开会议!");
sendMessageAll(JsonUtils.objectToJson(sm));
users.remove(map);
}
}
subOnlineCount();//在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
ScoketMessage sm = JSON.parseObject(message,ScoketMessage.class);
if(sm.getType().equals("ping")){
System.out.println("WebSocketMessage连接正常");
} else if(sm.getType().equals("join")){
for (Map<String, Object> map : users){
if(map.get("session") == session){
map.put("userId", sm.getUserId());
map.put("username", sm.getFromUser());
sendMessageAll(JsonUtils.objectToJson(sm));
System.out.println("用户:【" + sm.getFromUser() + "】连接成功");
}
}
} else {
sendMessageAll(JsonUtils.objectToJson(sm));
System.out.println("来自WebSocket的消息:" + JsonUtils.objectToJson(sm));
}
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 群发自定义消息
*/
public void sendMessageAll(String message){
for (Map<String, Object> map : users){
Session session = (Session) map.get("session");
if(session.isOpen()){
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketMessage.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketMessage.onlineCount--;
}
}
第四部 js 获取WebSocket 请求的代码
var websocket = null;
var lockReconnect = false; //避免ws重复连接
createWebSocket();
function createWebSocket(){
try{
//判断当前浏览器是否支持WebSocket
if('WebSocket' in window){
websocket = new WebSocket(webSocketMessageUrl);
} else if('MozWebSocket' in window){
websocket = new MozWebSocket(webSocketMessageUrl);
} else {
alert('您的浏览器不支持WebSocket');
}
initEventHandle();
}catch(e){
reconnect();
console.log("创建WebSocket异常:"+e);
}
}
function initEventHandle(){
//连接成功建立的回调方法
websocket.onopen = function(event){
var sendTime = getCurrentDate(4);
var msg = {
type: "join",
userId: userId,
fromUser: account,
sendTime: sendTime,
message:"欢迎"+account+"加入会议!"
};
websocket.send(JSON.stringify(msg));
console.log("【WebSocket消息】:"+JSON.stringify(msg));
};
//连接发生错误的回调方法
websocket.onerror = function(){
console.log("【WebSocket消息】:连接错误");
reconnect();
};
//接收到消息的回调方法
websocket.onmessage = function(event){
var data = JSON.parse(event.data);
if(data.type=='join'){
var html = "<div class='history-toolbar'>"+
"<button class='u-btn u-btn-none f-w-140'>"+data.message+"</button>"+
"</div>";
$("#webSocketMsg").append(html);
} else if(data.type=='message'){
if(data.fromUser == account){
var html = "<div class='msg msg-right'>"+
"<div class='avatar'></div>"+
"<div class='msgcontent'>"+
"<div class='nick'>"+data.sendTime+" "+data.fromUser+"</div>"+
"<div class='value'>"+data.message+"</div>"+
"</div>"+
"</div>";
$("#webSocketMsg").append(html);
} else {
var html = "<div class='msg msg-left'>"+
"<div class='avatar'></div>"+
"<div class='msgcontent'>"+
"<div class='nick'>"+data.fromUser+" "+data.sendTime+"</div>"+
"<div class='value'>"+data.message+"</div>"+
"</div>"+
"</div>";
$("#webSocketMsg").append(html);
}
} else if(data.type=='close'){
var html = "<div class='history-toolbar'>"+
"<button class='u-btn u-btn-none f-w-140'>"+data.message+"</button>"+
"</div>";
$("#webSocketMsg").append(html);
}
//收到消息后自动滚动到消息列表底部
var ele = document.getElementById('chat-msg-list');
ele.scrollTop = ele.scrollHeight;
};
//连接关闭的回调方法
websocket.onclose = function(){
console.log("【WebSocket消息】:连接断开");
reconnect();
};
//每隔30秒发送心跳包
setInterval(heartCheck,30000);
}
function reconnect() {
if(lockReconnect) return;
lockReconnect = true;
setTimeout(function () { //没连接上会一直重连,设置延迟避免请求过多
createWebSocket();
lockReconnect = false;
}, 30000);
}
//心跳
function heartCheck(){
var sendTime = getCurrentDate(2);
var msg = {
type: "ping",
sendTime: sendTime,
message:"ping"
};
websocket.send(JSON.stringify(msg));
}
//发送消息
function sendMsg(){
var message = document.getElementById('msgValue').value;
if(message == ''){
layer.msg("请输入需要发送的内容");
return false;
}
var sendTime = getCurrentDate(4);
var value = replace_em(message);
var msg = {
type: "message",
userId: userId,
fromUser:account,
sendTime: sendTime,
message:value
};
websocket.send(JSON.stringify(msg));
document.getElementById('msgValue').value='';
}
function replace_em(str){
str = str.replace(/\</g,'<');
str = str.replace(/\>/g,'>');
str = str.replace(/\n/g,'<br/>');
str = str.replace(/\[em_([0-9]*)\]/g,'<img src="/plugin/qqFace/arclist/$1.gif" border="0" />');
return str;
}
//连接关闭的回调方法
websocket.onclose = function(){
};
//获取时间
function getCurrentDate(format) {
var now = new Date();
var year = now.getFullYear(); //得到年份
var month = now.getMonth();//得到月份
var date = now.getDate();//得到日期
var day = now.getDay();//得到周几
var hour = now.getHours();//得到小时
var minu = now.getMinutes();//得到分钟
var sec = now.getSeconds();//得到秒
month = month + 1;
if (month < 10) month = "0" + month;
if (date < 10) date = "0" + date;
if (hour < 10) hour = "0" + hour;
if (minu < 10) minu = "0" + minu;
if (sec < 10) sec = "0" + sec;
var time = "";
//精确到天
if(format==1){
time = year + "-" + month + "-" + date;
}
//精确到分
else if(format==2){
time = year + "-" + month + "-" + date+ " " + hour + ":" + minu + ":" + sec;
}
//去掉年
else if(format==3){
time = month + "-" + date+ " " + hour + ":" + minu + ":" + sec;
}
//去掉年月日
else if(format==4){
time = hour + ":" + minu + ":" + sec;
}
return time;
}