初识标准H5的WebSocket

4 篇文章 0 订阅
3 篇文章 0 订阅

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

目前主流的浏览器都已经支持了该新协议,具体版本就需要自己去查。

在工作中需要做一个实时的监控告警系统,因为web前端需要实时拉取数据,我就选择了webscoket这项技术,用的也比较简单。由于在该项目中前端需要的数据量比较大,因此没有用webscoket来推送真实数据,而是当后端(Java)数据有更新时推送一条消息给前端,前端再进行拉取。废话不多说,直接上代码。

一、后端代码。后端用的SpringBoot框架,在pom.xml中引入了spring-boot-starter-websocket依赖

<dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-websocket</artifactId>  
    </dependency>  
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
	
	@Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.python.modules.synchronize;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import cn.xunlei.cbase.xlalarm.calculate.CalculateService;

@ServerEndpoint(value = "/pushMsg")
@Component
public class WebSocketPushMsg {
	
	private static Logger log = LoggerFactory.getLogger(CalculateService.class);

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketPushMsg> webSocketSet = new CopyOnWriteArraySet<WebSocketPushMsg>();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        log.info("有新连接加入!当前在线客户端数为:{}",getOnlineCount());
//        try {
//            sendMessage("欢迎!!!");
//        } catch (IOException e) {
//        	e.printStackTrace();
//        	log.error("WebSocket连接异常!");
//        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        log.info("有连接断开!当前在线客户端数为:{}",getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
//        System.out.println("来自客户端的消息:" + message);

        //群发消息
//        for (MyWebSocket 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("WebSocket发生错误!");
        error.printStackTrace();
    }


    public synchronized void sendMessageToOne(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }


    /**
     * 群发自定义消息
     * */
    public static void sendMessage(String message) {
        for (WebSocketPushMsg item : webSocketSet) {
            try {
                item.sendMessageToOne(message);
            } catch (IOException e) {
            	log.error("消息发送失败:{},{}",item.session.getId(),e.getMessage());
            	webSocketSet.remove(item);
            	try {
					item.session.close();
				} catch (IOException e1) {
					e1.printStackTrace();
	            	log.error("连接断开异常:{},{}",item.session.getId(),e.getMessage());
				}
            }
        }
    }

    public static int getOnlineCount() {
        return onlineCount.get();
    }

    private static void addOnlineCount() {
    	onlineCount.incrementAndGet();
    }

    private static void subOnlineCount() {
    	onlineCount.decrementAndGet();
    }
}

二、前端js代码。


<!DOCTYPE HTML>
<html>
<head>
    <title>My WebSocket</title>
</head>

<body>
Welcome<br/>
<input id="text" type="text" /><button οnclick="send()">Send</button>    <button οnclick="closeWebSocket()">Close</button>
<div id="message">
</div>
</body>

<script type="text/javascript">
    var websocket = null;

    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
    	//无nginx
      //websocket = new WebSocket("ws://localhost:8090/websocket");
        websocket = new WebSocket("ws://www.gudao.red/websocket");
    }
    else{
        alert('Not support websocket')
    }

    //连接发生错误的回调方法
    websocket.onerror = function(){
        setMessageInnerHTML("error");
    };

    //连接成功建立的回调方法
    websocket.onopen = function(event){
        setMessageInnerHTML("open");
    }

    //接收到消息的回调方法
    websocket.onmessage = function(event){
        setMessageInnerHTML(event.data);
    }

    //连接关闭的回调方法
    websocket.onclose = function(){
        setMessageInnerHTML("close");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){
        websocket.close();
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML){
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //关闭连接
    function closeWebSocket(){
        websocket.close();
    }

    //发送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>



三、nginx反向代理配置,这里要千万注意,如果使用nginx代理,那么websocket的连接默认超时时间是60s,因此如果60s不满足要求,则需要自行配置proxy_read_timeout超时时间


        location /websocket {  
		        proxy_pass http://127.0.0.1:8090;
		        proxy_redirect off;  
		        proxy_http_version 1.1;  
		        proxy_set_header Upgrade $http_upgrade;  
		        proxy_set_header Connection "upgrade";  
		        proxy_set_header Host $host:$server_port;  
		        proxy_set_header X-Real-IP $remote_addr;  
		        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
             proxy_read_timeout 600;
					}  


到此,整个过程就完成了。值得注意是在这个过程中我遇到一个很奇怪的问题,在我的项目中是这样一个流程,用户登录之后前端立马跟后端建立webscoket在连接,但在IE浏览器上却无法建立连接,也不报任务异常。反正就是连接建立之后马上就会断掉,在网上搜索无果之后想了一个折中的办法,登录成功之后不立马建立连接而是延迟10s之后再建立webscoket连接,这个办法成功解决了问题,但至今任然没找到这个问题的真正原因,如果那么大神有缘看到这个博客,知道该问题的解决办法还请赐教!



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值