nginx服務器 部署websocket項目 連接中斷 報錯 :Error in connection establishment: net::ERR_NAME_NOT_RESOLVED

項目需要彈窗告警,原本採用comet4j的方式進行,但是後來發現不支持tomcat8.5,於是打算使用webSocket的方式實現,webSocket是瀏覽器客戶端和服務器後臺實現的一種全雙工通信方式,許多網頁聊天工具都是採用該方式進行。

本地開發的時候都可以正常使用,但是在部署到nginx代理服務器的時候發現報了錯誤,連不上

Error in connection establishment: net::ERR_NAME_NOT_RESOLVED

後來發現是nginx服務器默認是不打開webSocket的功能的,這需要我們在nginx服務器上配置:

 location /test/ {
                proxy_pass http://test.com;
                proxy_redirect default;
                proxy_set_header Upgrade $http_upgrade; # allow websockets
                proxy_set_header Connection "upgrade";
                proxy_http_version 1.1;
                }

另外nginx設置了連接超時時間或者讀取超時時間的時候,websoket會中斷,那麼需要我們維護socket連接,斷線自動重連,代碼如下,

websocket後臺:

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import net.sf.json.JSONObject;


@ServerEndpoint("/websocket/{sid}")
@Component
public class WebSocketServer {
	
	static Logger log=LoggerFactory.getLogger(WebSocketServer.class);
    //靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的。
    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) {
            	log.error(e.toString());
                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 void sendMessage(Map<String,Object> dataMap)throws IOException{
	   try{if(dataMap!=null){
			JSONObject jsonObject = JSONObject.fromObject(dataMap);
			StringBuilder builder = new StringBuilder(jsonObject.toString());		
			//發送告警到前臺
			this.session.getBasicRemote().sendText(builder.toString());
			log.info("發送成功");
			}	
	   }catch(Exception e) {
		   log.error(e.toString());
	   }
   }
    /**
     * 羣發自定義消息
     * */
    public static void sendInfo(String message, 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) {
            	log.error(e.toString());
                continue;
            }
        }
    }
    /**
     * 羣發自定義消息
     * */
    public static void sendInfoMap(Map<String,Object> dataMap, String sid) throws IOException {
    	log.info("推送消息到窗口"+sid+",推送內容:"+dataMap.toString());
        for (WebSocketServer item : webSocketSet) {
            try {
            	item.sendMessage(dataMap);
            	//這裏可以設定只推送給這個sid的,爲null則全部推送暫時全部放開
            	/*if(sid==null) {
            		item.sendMessage(dataMap);
            	}else if(item.sid.equals(sid)){
            		item.sendMessage(dataMap);
            	}*/
            } catch (IOException e) {
            	log.error(e.toString());
                continue;
            }
        }
    }
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}


前臺頁面以及js:

var websocket_connected_count = 0;
var onclose_connected_count = 0;
/**websocekt*/
function webSocketClient(){
	var socket;  
	if(typeof(WebSocket) == "undefined") {  
	    console.log("您的瀏覽器不支持WebSocket");  
	}else{  
	    console.log("您的瀏覽器支持WebSocket");  
	    	//httprequest請求id
	    	var sid = "<%=requestId%>";
	    	//實現化WebSocket對象,指定要連接的服務器地址與端口  建立連接  
	        socket =new WebSocket("ws://127.0.0.1:8080/butlerBf/websocket/"+sid);   
	        //打開事件  
	        socket.onopen = function() {  
	            console.log("Socket 已打開"); 
	            //socket.send("這是來自客戶端的消息" + location.href + new Date());  
	        };  
	        //獲得消息事件  
	        socket.onmessage = function(msg) {  
	            if(msg.data!="您的瀏覽器支持WebSocket"&&msg.data!="Socket 已打開"&&msg.data!="連接成功"&&msg.data!="ping"&&msg.data!=""){
	            	console.log(msg);
	            	checkAuthority(msg.data);
	            	heartCheck.reset().start();
	            }
	        };  
	        //關閉事件  
	        socket.onclose = function(e) {  
	            console.log("Socket已關閉"); 
	            console.log(e);
	        };
	        //發生了錯誤事件  
	        socket.onerror = function() {  
	        	websocket_connected_count++;
	            if(websocket_connected_count <= 5){
	            	webSocketClient()
	            }
	            console.log("Socket發生了錯誤");  
	            //此時可以嘗試刷新頁面
	        }  
	        //離開頁面時,關閉socket
	        //jquery1.8中已經被廢棄,3.0中已經移除
	         $(window).unload(function(){  
	             socket.close();  
	         });  
	}
	// 心跳檢測, 每隔一段時間檢測連接狀態,如果處於連接中,就向server端主動發送消息,來重置server端與客戶端的最大連接時間,如果已經斷開了,發起重連。
    var heartCheck = {
        timeout: 60000,        // 60s發一次心跳,比server端設置的連接時間稍微小一點,在接近斷開的情況下以通信的方式去重置連接時間。
        serverTimeoutObj: null,
        reset: function(){
            clearTimeout(this.timeoutObj);
            clearTimeout(this.serverTimeoutObj);
            return this;
        },
        start: function(){
            var self = this;
            this.serverTimeoutObj = setInterval(function(){
                if(socket.readyState == 1){
                    console.log("連接狀態,發送消息保持連接");
                    socket.send("ping");
                    heartCheck.reset().start();    // 如果獲取到消息,說明連接是正常的,重置心跳檢測
                }else{
                    console.log("斷開狀態,嘗試重連");
                    webSocketClient();
                }
            }, this.timeout)
        }
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章