目前,如果需要与服务器长时间的通信,有两种方法,
- 轮询,每隔一段时间就向服务器发出请求获取数据
- websocket连接
1 有哪些好处,工作量简单(代码简单)
1 有哪些坏处,对服务器压力过大,每隔一段时间就会访问,可能会产生许多的没用请求造成资源的浪费
2 有哪些好处,在说2有哪些好处之前,先来介绍以下websocket到底是什么
websocket的背景
Websocekt
websocket协议是html5提出的一个新的协议和http协议有交集但是没有直接关系,为了兼容现有的浏览器握手规范:通过一个握手的机制,客户端和服务端之间能够建立起一个类似于tcp的连接,从而方便c——s之间的通信,再websocket出现之间,web交互一般是基于http的短连接或者长连接。
websocket是为了解决客户端与服务端实时通信而产生的技术,websocket协议本质上是一个基于tcp的协议,是先通过http协议发起握手请求,客户端告诉服务端,这是一个websocket协议,于是http协议升级为websocket协议
客户端发送:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
这段类似HTTP协议的握手请求中,多了几个东西。
我会顺便讲解下作用。
Upgrade: websocket
Connection: Upgrade
告诉服务器这是websocket协议不是http协议
服务器返回:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议啦~
Upgrade: websocket
Connection: Upgrade
2 的优点:以前webserver实现推送技术或者实时通信用的都是轮询,再特定的时间间隔由浏览器发送请求,将服务器的消息拉回来,在这种情况下,我们需要不停的向服务器发送请求,然而http request的header是非常长的,里面包含的数据可能是一个非常小的值,这样会占用很多的资源
而比较新的技术使用的ajax技术取做轮询,这种技术虽然可能做到全双工通信,但是依然需要发出请求
websocket API的最伟大之处在于可以让客户端和服务端再给定的时间内的任意时刻互相推送消息,浏览器与服务器只需要一个握手的动作
在建立连接之后,服务器可以主动传送消息给客户端,客户端也可以随时向服务器发送数据,此外,服务器与客户端之间交换的标头信息很小。
因此从服务器角度来说, websocket有以下好处:
节省每次请求的header ,httpheader的大小一般有几十节
serverpush:服务器可以主动的推送数据给客户端
在我自己搭建websocket后端使用的是spring自带的websocket支持
首先是spring的配置文件
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
/**
* 该configureMessageBroker()方法将重写方法WebSocketMessageBrokerConfigurer来配置消息代理。
* 它首先调用enableSimpleBroker()一个简单的基于内存的消息代理,将问候消息带回以“/stock”为前缀的客户端。
* 它还指定了为“ @MessageMapping注解”方法绑定的消息的“/app”前缀。这个前缀将被用来定义所有的消息映射;
* 例如,“/app/hello”是该GreetingController.greeting()方法被映射为处理的端点。
*
* @param config
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/stock","/user");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user/");
}
/**
* 该registerStompEndpoints()方法注册“/test-info”端点,
* 启用SockJS后备选项,以便在WebSocket不可用时可以使用替代传输。
* SockJS客户端将尝试连接到“/test-sockJs”
* 并使用可用的最佳传输(websocket,xhr-streaming,xhr-polling等)。
*
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//开启一个sockJs的端点
registry.addEndpoint("/test-sockJs").setAllowedOrigins("*").withSockJS();
//开启一个原生websocket的端点
registry.addEndpoint("/test-webSocket").setAllowedOrigins("*");
}
}
这里是接收消息的类
@Controller
@EnableScheduling
public class GreetingController {
* 这个控制器简洁明了,让我们一步一步分解。
* <p>
* 该@MessageMapping注解确保如果一个消息被发送到目的地“/hello”,则该greeting()方法被调用。
* <p>
* 消息的有效参数绑定到HelloMessage传入的对象greeting()。
* <p>
* 在内部,该方法的实现通过使线程休眠1秒来模拟处理延迟。
* 这是为了演示在客户端发送消息后,服务器可以采取异步处理消息。
* 客户可以继续进行所需的任何工作,而无需等待响应。
* <p>
* 在1秒延迟之后,该greeting()方法创建一个Greeting对象并返回它。返回值被广播给所有订阅者,如@SendTo注释中所指定的“/ topic / greetings” 。
* 接收消息处理器
*/
@MessageMapping("/hello")
@SendTo("/stock/price")
public void greetingHello(Greeting message) {
//这个greeting是消息的封装
//会将消息发送给订阅了地址的人
}
// 如果WebsocketConfig中开了消息代理就走消息代理,不开就随意指定目的地,只要有人订阅就能收到消息
//接收控制消息
@MessageMapping("/hello1")
//@SendToUser(broadcast = false)
public String Hello(String name) {
return "控制";
}
//SubscribeMapping注解返回的消息直接发送到 client,不经过代理,而 @SendTo 注解的路径,就会经过代理,然后再发送到 目的地)
//如果开了消息代理就走消息代理
//由于后台开启了消息代理,前台订阅前缀一定要是 stock开头 sendto注解功能:重载目的地
@SubscribeMapping("/price")
public String greetingSubscribe() {
return "订阅成功";
}
由于是开了页面到服务端的代理与服务端到页面的代理。
页面到服务端的代理是 /app
服务端到页面的代理是 /stock
首先,websocket 有订阅模式,订阅主题,订阅主题就是,服务端会发出一些信息到一个类似于公众号的地方,前端页面要获取这些消息就要订阅这个公众号,那么订阅的地址是什么呢?可以到在后台我设置了服务端到前端页面的代理前缀为 “/stock”,所以 订阅的地址前缀加上 “/stock” 就可以了。这个 “公众号” 的地址就只需要前端订阅地址与后端发送的地址相同就可以了
如何在订阅时希望能告知一个消息返回呢? 在spring提供的websocket 支持中有一个注解,@SubscribeMapping 这个注解所在的方法在有页面订阅这个参数地址的时候会触发,方法的返回值就是会返回到页面的值。
注意:这里订阅时,后台开了页面到服务端的代理 ,由于这里不是真正的订阅,只是希望做这个操作时能有一个返回值,所以 订阅地址要加上 /app ,这里是发送给@Controller 请求处理的,并不是订阅,所以地址会不一样!
前端页面函数
/**
* 该connect()函数使用SockJS和stomp.js打开“/ gs-guide-websocket”的连接,
* 这是我们的SockJS服务器正在等待连接的地方。
* 连接成功后,客户端将订阅“/topic/greetings”目标,服务器将在其中发布问候消息。
* 当在该目的地收到问候语时,它会在DOM上添加一个段落元素来显示问候消息
*/
function connect() {
var url = 'http://localhost:8091/test-sockJs';
//这里是使用sockJs做连接
var socket = new SockJS(url);
//这里是使用websocket做连接
//两种方式初始化连接都不同,要看清楚,还要后台开启这两个连接的支持
//var url1 = 'ws://192.168.1.3:8090/test-webSocket';
//var socket = new WebSocket(url1);
stompClient = Stomp.over(socket);
//stompClient= Stomp.client(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log(frame);
//发送订阅消息
stompClient.subscribe('/app/bPage', function (greeting) {
});
stompClient.subscribe('/app/bServer', function (greeting) {
});
stompClient.subscribe('/user/stock/greetings1', function(data) {
$("#ret").text(data.body);
});
/**
*
订阅地址通过消息目的地到后台@subscribe注解中,注解地址/price ,隐去了app
stompClient.subscribe('/app/price', function (greeting) {
});
/stock/bServer 则是订阅主题,用来接收服务端发布到 "/stock/bServer" 目的地的数据
*/
stompClient.subscribe('/stock/bServer', function (greeting) {
});
stompClient.subscribe('/stock/bPage', function (greeting) {
});
});
}