Java 杂谈Java进阶之路

WebSocket-服务器到客户端的请求

2019-05-20  本文已影响0人  elijah777

WebSocket通信

引言:

WebSocket主要用过后端向前端访问,这样避免了一些项目上执行时间比较久的,不必前端一直请求数据,定时刷新什么的。

比如说,需要实现一个等待时间比较久的功能,提交某类单子,或者充值支付等,不方边使用异步操作,需要实时返回消息给用户,可以选用,也可以用百分比的形式返回后端执行的情况。

一、WebSocket 简介

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

二、WebSocket客户端

websocket允许通过JavaScript建立与远程服务器的连接,从而实现客户端与服务器间双向的通信。

在websocket中有两个方法: websocket允许通过JavaScript建立与远程服务器的连接,从而实现客户端与服务器间双向的通信。

在websocket中有两个方法:

  1. send() 向远程服务器发送数据
  2. close() 关闭该websocket链接

websocket同时还定义了几个监听函数

  1. onopen 当网络连接建立时触发该事件
  2. onerror 当网络发生错误时触发该事件
  3. onclose 当websocket被关闭时触发该事件
  4. onmessage 当websocket接收到服务器发来的消息的时触发的事件,也是通信中最重要的一个监听事件。

三、代码实战

请求与接收消息的页面

 var socket;
if(typeof(WebSocket) == "undefined") {
 console.log("您的浏览器不支持WebSocket");
}else{
​
 var url='ws://'+window.location.host+'/websocket/${cid}';
​
 debugger;
 console.log("您的浏览器支持WebSocket");
 //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
 //等同于socket = new WebSocket("ws://localhost:8083/checkcentersys/websocket/20");
 socket = new WebSocket(url);
 //打开事件
 socket.onopen = function() {
 console.log("Socket 已打开");
 //socket.send("这是来自客户端的消息" + location.href + new Date());
 };
 //获得消息事件
 socket.onmessage = function(msg) {
 console.log(msg.data);
 //发现消息进入    开始处理前端触发逻辑
 };
 //关闭事件
 socket.onclose = function() {
 console.log("Socket已关闭");
 };
 //发生了错误事件
 socket.onerror = function() {
 alert("Socket发生了错误");
 //此时可以尝试刷新页面
 }

}

请求页面与推送消息

[http://127.0.0.1:18080/checkcenter/socket/2 第一次请求

http://127.0.0.1:18080/checkcenter/socket/push/2?message=%E4%B8%80%E5%A4%A9](http://127.0.0.1:18080/checkcenter/socket/push/2?message=一天)

对2窗口发送消息

//页面请求
@GetMapping("/socket/{cid}")
public ModelAndView socket(@PathVariable String cid) {
 ModelAndView mav=new ModelAndView("/socket");
 mav.addObject("cid", cid);
 return mav;
}
​
/**
 * 发送消息 推送数据接口
 * @param cid
 * @param message
 * @return
 */
@ResponseBody
@RequestMapping("/socket/push/{cid}")
public String pushToWeb(@PathVariable String cid,String message) {
 try {
 WebSocketServer.sendInfo(message,cid);
 } catch (IOException e) {
 e.printStackTrace();
 return "error" + cid+"#"+e.getMessage();
 }
 return "success" + cid;
}

相关socket服务方法

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
​
/**
 * @description: WebSocketServer消息服务
 * @author: Shenshuaihu
 * @version: 1.0
 * @data: 2019-03-18 17:02
 */
​
@ServerEndpoint("/websocket/{sid}")
@Component
@Slf4j
public class WebSocketServer {
​
 //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
 private static int onlineCount = 0;
 //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
 private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
​
 //与某个客户端的连接会话,需要通过它来给客户端发送数据
 private Session session;
​
 //接收sid
 private String sid="";
 /**
 * 连接建立成功调用的方法*/
 @OnOpen
 public void onOpen(Session session,@PathParam("sid") String sid) {
 this.session = session;
 webSocketSet.add(this);     //加入set中
 addOnlineCount();           //在线数加1
 log.info("有新窗口开始监听:"+sid+",当前在线人数为" + getOnlineCount());
 this.sid=sid;
 try {
 sendMessage("连接成功");
 } catch (IOException e) {
 log.error("websocket IO异常");
 }
 }
​
 /**
 * 连接关闭调用的方法
 */
 @OnClose
 public void onClose() {
 webSocketSet.remove(this);  //从set中删除
 subOnlineCount();           //在线数减1
 log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
 }
​
 /**
 * 收到客户端消息后调用的方法
 * @param message 客户端发送过来的消息
 *
 **/
 @OnMessage
 public void onMessage(String message, Session session) {
 log.info("收到来自窗口"+sid+"的信息:"+message);
 //群发消息
 for (WebSocketServer item : webSocketSet) {
 try {
 item.sendMessage(message);
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 }
​
 /**
 *
 * @param session
 * @param error
 */
 @OnError
 public void onError(Session session, Throwable error) {
 log.error("发生错误");
 error.printStackTrace();
 }
 /**
 * 实现服务器主动推送
 */
 public void sendMessage(String message) throws IOException {
 this.session.getBasicRemote().sendText(message);
 }
​
​
 /**
 * 群发自定义消息
 * */
 public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {
 log.info("推送消息到窗口"+sid+",推送内容:"+message);
 for (WebSocketServer item : webSocketSet) {
 try {
 //这里可以设定只推送给这个sid的,为null则全部推送
 if(sid == null) {
 item.sendMessage(message);
 } else if(item.sid.equals(sid)){
 item.sendMessage(message);
 }
 } catch (IOException e) {
 continue;
 }
 }
 }
​
 public static synchronized int getOnlineCount() {
 return onlineCount;
 }
​
 public static synchronized void addOnlineCount() {
 WebSocketServer.onlineCount++;
 }
​
 public static synchronized void subOnlineCount() {
 WebSocketServer.onlineCount--;
 }
}

以下是我在一个项目上应用到的,是调用一个用时比较久的API,把实时消息返回给用户。

function toText(id) {
​
 $("#message").attr("disabled", true);
​
 $.ajax({
 // url : BaseUrl+'/to/text',
 url : '/to/text',
 type : "POST",
 data : {"id" : id ,"sid" : sid } ,
 success : function(data) {
 if(data.success) {
 layer.close(messageWin);
 $('#message').addClass("hide"); //取消继续隐藏
 // 刷新数据
 $(".layui-laypage-btn")[0].click();
​
 } else {
 layer.close(messageWin);
 //eg1
 layer.confirm(data.data, {icon: 3, title:'失败提示'}, function(index){
 layer.close(index);
 });
 $('#message').addClass("hide"); //取消继续隐藏
 }
 $("#message").attr("disabled", false);
​
 },
 error : function() {
​
 }
 });  // ajax结束
​
}
​
function f1() {
​
 $('#message').removeClass("hide");
​
 messageWin =  layer.open({
 type : 1,
 shade : 0.4,
 title : '正在执行',
 shadeClose: true, //点击遮罩关闭层
 area : [ '300px', '250px' ], //显示空间
 content : $('#message'), //捕获的元素
 cancel : function(index) {
 layer.close(messageWin);
 $('#message').addClass("hide"); //取消继续隐藏
 }
 });
​
 // 改变状态
​
}
​
​
​
var socket;
if(typeof(WebSocket) == "undefined") {
 console.log("您的浏览器不支持WebSocket");
}else{
 var url='ws://'+window.location.host+'/websocket/' + sid;
 console.log("您的浏览器支持WebSocket");
 socket = new WebSocket(url);
 //打开事件
 socket.onopen = function() {
 console.log("Socket 已打开");
 socket.send("这是来自客户端的消息" + location.href + new Date());
 };
 //获得消息事件
 socket.onmessage = function(msg) {
 console.log(msg.data);
 //发现消息进入    开始处理前端触发逻辑
 sendMessage(msg.data);
​
 };
 //关闭事件
 socket.onclose = function() {
 console.log("Socket已关闭");
 };
 //发生了错误事件
 socket.onerror = function() {
 alert("Socket发生了错误");
 }
​
}
​
function sendMessage(message) {
 $('#socket').html('转换中,请等待,请勿关闭窗口!!! ' + '\n' + message);
 if (message === "保存文件") {
 console.log("messageWin 已关闭");
 }
}

需要注意的是后端发送消息时要对于sid,免得推送错了。本章部分内容是整合过的文章,如有改善请告知。

上一篇下一篇

猜你喜欢

热点阅读