http协议在通讯过程当中存在一个巨大的缺陷,通讯只能由客户端发起,服务器只能根据响应返回响应的结果。也就说,服务器端没法主动给客户端发送消息。html
对于服务器端连续的状态变化,http协议就显得有些力不从心了,固然也能够经过其余的方式实现。好比:java
虽然这样也能够实现咱们的要求,可是资源就在轮询的过程当中被大量浪费。web
WebSocket协议,2008年诞生,2011年称为国际标准,其最大的特色就是服务器端能够主动向客户端发送消息,实现真正的双向平等对话。主要特色:redis
理论没看懂的能够戳这 故事描述型spring
建立WebSocketapi
var Socket = new WebSocket(url, [protocol] );//协议能够为空
属性缓存
Socket.readyState//链接状态 Socket.bufferedAmount //队列中等待传输,可是尚未发出的 UTF-8 文本字节数。
事件,编写的时候要加上on 好比onOpen安全
open //链接创建时触发 message //客户端接收服务端数据时触发 error //通讯发生错误时触发 close //链接关闭时触发
方法服务器
Socket.send() //使用链接发送数据 Socket.close() //关闭链接
实例:websocket
// 初始化一个 WebSocket 对象 var ws = new WebSocket("ws://localhost:9998/echo"); // 创建 web socket 链接成功触发事件 ws.onopen = function () { // 使用 send() 方法发送数据 ws.send("发送数据"); alert("数据发送中..."); }; // 接收服务端数据时触发事件 ws.onmessage = function (evt) { var received_msg = evt.data; alert("数据已接收..."); }; // 断开 web socket 链接成功触发事件 ws.onclose = function () { alert("链接已关闭..."); };
服务器端的就主要用代码来实现吧
源码地址 密码:f28e
服务端获取消息很简单,主要是向服务器端发送消息。须要向客户端发送消息,那么咱们须要知道客户端的某个惟一标识,那么这个标识用什么来表示呢,那就是session。
直接贴码,里面注释很清晰 须要的依赖
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency>
java源码
package me.gacl.websocket; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; /** * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端, * 注解的值将被用于监听用户链接的终端访问URL地址,客户端能够经过这个URL来链接到WebSocket服务器端 */ @ServerEndpoint("/websocket") public class WebSocketTest { //静态变量,用来记录当前在线链接数。应该把它设计成线程安全的。 private static int onlineCount = 0; //concurrent包的线程安全Set,用来存放每一个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通讯的话,可使用Map来存放,其中Key能够为用户标识 private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>(); //与某个客户端的链接会话,须要经过它来给客户端发送数据 private Session session; /** * 链接创建成功调用的方法 * @param session 可选的参数。session为与某个客户端的链接会话,须要经过它来给客户端发送数据 */ @OnOpen public void onOpen(Session session){ this.session = session; webSocketSet.add(this); //加入set中 addOnlineCount(); //在线数加1 System.out.println("有新链接加入!当前在线人数为" + getOnlineCount()); } /** * 链接关闭调用的方法 */ @OnClose public void onClose(){ webSocketSet.remove(this); //从set中删除 subOnlineCount(); //在线数减1 System.out.println("有一链接关闭!当前在线人数为" + getOnlineCount()); } /** * 收到客户端消息后调用的方法 * @param message 客户端发送过来的消息 * @param session 可选的参数 */ @OnMessage public void onMessage(String message, Session session) { System.out.println("来自客户端的消息:" + message); //群发消息 for(WebSocketTest item: webSocketSet){ try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); continue; } } } /** * 发生错误时调用 * @param session * @param error */ @OnError public void onError(Session session, Throwable error){ System.out.println("发生错误"); error.printStackTrace(); } /** * 这个方法与上面几个方法不同。没有用注解,是根据本身须要添加的方法。 * @param message * @throws IOException */ public void sendMessage(String message) throws IOException{ this.session.getBasicRemote().sendText(message); //this.session.getAsyncRemote().sendText(message); } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketTest.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketTest.onlineCount--; } }
这里经过从新写一个controller,直接给客户端发送消息,先贴这里的代码,应该大部分人都是想实现这个功能。这里的目的是,在处理一个其余请求以后,须要给原来的客户端发送消息,告诉它我已经处理完了,收到消息以后再处理后续的逻辑(扫码场景比较广泛)。
@GetMapping("/") public WebsocketResponse sendSuccess(){ MyHandler send = new MyHandler(); TextMessage msg = new TextMessage("发给客户端"); send.sendMessageToUser("888",msg); return new WebsocketResponse(1); }
有须要的直到源码中拉取代码吧,服务器端的原理都相似