一、WebSocket概述

1、WebSocket简介

WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。

双全工通信:全双工通信又称为双向同时通信,即通信的双方可以同时发送和接收信息的信息交互方式。 半双工通信:数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输

1.1 轮询

ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。

所谓轮询(polling)其实是客户端按规定时间定时向服务端发送ajax请求,服务器接到请求后马上返回响应信息并关闭连接。

场景再现:

客户端:啦啦啦,有没有新信息(Request)

服务端:没有(Response)

客户端:啦啦啦,有没有新信息(Request)

服务端:没有。。(Response)

客户端:啦啦啦,有没有新信息(Request)

服务端:你好烦啊,没有啊。。(Response)

客户端:啦啦啦,有没有新消息(Request)

服务端:好啦好啦,有啦给你。(Response)

客户端:啦啦啦,有没有新消息(Request)

服务端:。。。。。没。。。。没。。。没有(Response) —- loop

1.2 长轮询

长轮询(Long Polling)是一种实现实时通信的技术,通常用于前端与后端服务器之间的交互。在传统的Ajax请求中,前端向后端发送请求并等待后端的响应,而长轮询则允许服务器在没有数据更新时不立即返回响应,而是一直保持连接,直到有新数据更新时再返回响应

场景再现:

客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)

服务端:额。。 等待到有消息的时候。。来 给你(Response)

客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop

二、为什么需要WebSocket

HTTP 是基于请求响应式的,即通信只能由客户端发起,服务端做出响应。

通过HTTP实现即时通讯,只能是页面轮询向服务器发出请求,服务器返回查询结果。轮询的效率低,非常浪费资源,因为必须不停连接,或者 HTTP 连接始终打开。

WebSocket的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。

2.1 WebSocket特点:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。 (2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。 (3)数据格式比较轻量,性能开销小,通信高效。 (4)可以发送文本,也可以发送二进制数据。 (5)没有同源限制,客户端可以与任意服务器通信。 (6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

三、SpringBoot整合netty-WebSocket

1.pom文件

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-test

test

com.alibaba

fastjson

1.2.62

org.projectlombok

lombok

//主要依赖

org.yeauty

netty-websocket-spring-boot-starter

0.7.3

2. yaml文件

websocket:

port: 2222

path: /api/websocket

3. 配置类

@Configuration

public class WebSocketConfig {

@Bean

public ServerEndpointExporter serverEndpointExporter() {

return new ServerEndpointExporter();

}

}

4.服务类

package com.my.websocket;

import com.alibaba.fastjson.JSONObject;

import io.netty.handler.codec.http.HttpHeaders;

import io.netty.handler.timeout.IdleStateEvent;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

import org.yeauty.annotation.*;

import org.yeauty.pojo.ParameterMap;

import org.yeauty.pojo.Session;

import java.io.IOException;

import java.util.HashMap;

import java.util.Map;

@Component

@ServerEndpoint(prefix = "websocket")//标注ws服务的路径

public class WebSocketServer {

private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);

public static final Map sessionMap = new HashMap<>();

@OnOpen

public void onOpen(Session session, HttpHeaders headers, ParameterMap parameterMap) throws IOException {

String userId = parameterMap.getParameter("userId");

sessionMap.put(userId,session);

System.out.println("新用户连接===================>" + userId);

for (Map.Entry entry : sessionMap.entrySet()) {

sendAllMessage(JSONObject.toJSONString(sessionMap.keySet()));

}

}

@OnClose

public void onClose(Session session) throws IOException {

System.out.println("one connection closed");

try{

for (Map.Entry entry : sessionMap.entrySet()) {

if(entry.getValue()==session){

sessionMap.remove(entry.getKey());

}

}

}catch (Exception e){

System.out.println(e);

}

}

@OnError

public void onError(Session session, Throwable throwable) {

throwable.printStackTrace();

}

@OnMessage

public void onMessage(Session session, String message) {

Mapmap = JSONObject.parseObject(message, Map.class);

if(map.get("invitee")!=null){

String invitee = map.get("invitee").toString();

for (Map.Entry entry : sessionMap.entrySet()) {

if(entry.getKey().equals(invitee)){

sendMessage(message,entry.getValue());

}

}

}else {

String to = map.get("to").toString();

for (Map.Entry entry : sessionMap.entrySet()) {

if(entry.getKey().equals(to)){

sendMessage(message,entry.getValue());

}

}

}

}

private void sendMessage(String message, Session toSession) {

try {

log.info("服务端给客户端[{}]发送消息{}", toSession.id(), message);

System.out.println(message);

toSession.sendText(message);

} catch (Exception e) {

log.error("服务端发送消息给客户端失败", e);

}

}

/**

* 服务端发送消息给所有客户端

*/

public static void sendAllMessage(String message) {

try {

for (Session session : sessionMap.values()) {

log.info("服务端给客户端[{}]发送消息{}", session.id(), message);

session.sendText(message);

}

} catch (Exception e) {

log.error("服务端发送消息给客户端失败", e);

}

}

}

四、前端代码

//创建链接

this.socket = new WebSocket(

"ws://qingshan.natapp1.cc/api/websocket?userId=" + this.user.id

);

//初始化方法

init() {

let _this = this;

//打开事件

this.socket.onopen = function () {

console.log("websocket已打开");

};

// 浏览器端收消息,获得从服务端发送过来的文本消息

this.socket.onmessage = function (msg) {

console.log("收到数据====" + msg.data);

if (!_this.chatUser) {

return;

}

let data = JSON.parse(msg.data);

if (data.promoter != null) {

_this.getFriendReq();

} else {

if (data.from == _this.chatUser.id && data.from) {

let message = {

senderId: data.from,

receiverId: _this.user.id,

time: data.time,

info: data.text,

};

_this.chatinfos.push(message);

} else {

_this.userRefList.forEach(function (f) {

if (f.user.id == data.from) {

f.info.info = data.text;

f.info.time = data.time;

f.unReadCount = f.unReadCount + 1;

}

});

}

}

};

//关闭事件

this.socket.onclose = function () {

console.log("websocket已关闭");

_this.socket = new WebSocket(

"ws://qingshan.natapp1.cc/api/websocket?userId=" + _this.user.id

);

_this.init();

};

//发生了错误事件

this.socket.onerror = function () {

console.log("websocket发生了错误,尝试重连");

};

},

//发送消息

send() {

let headers = {

token: this.token,

};

let _this = this;

if (!this.chatUser) {

this.$message({ type: "warning", message: "请选择聊天对象" });

return;

}

if (!this.text) {

this.$message({ type: "warning", message: "请输入内容" });

} else {

// 组装待发送的消息 json

// {"from": "zhang", "to": "admin", "text": "聊天文本"}

let chatInfo = {

from: this.user.id,

to: this.chatUser.id,

text: this.text,

};

this.socket.send(JSON.stringify(chatInfo)); // 将组装好的json发送给服务端,由服务端进行转发

let message = {

senderId: _this.user.id,

receiverId: _this.chatUser.id,

time: new Date(),

info: this.text,

isEnable: 1,

};

let url = "http://19174933852.gnway.cc:8000/api/message/sendInfo";

this.$axios

.post(url, message, { headers })

.then((res) => {

console.log(res.data);

})

.catch((err) => {

console.error(err);

});

_this.chatinfos.push(message);

document.getElementsByClassName("chatBox")[0].scrollTop =

document.getElementsByClassName("chatBox")[0].scrollHeight;

this.text = "";

}

},

相关文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: