1. 程式人生 > >jfinal整合websocket 伺服器向瀏覽器推送通知

jfinal整合websocket 伺服器向瀏覽器推送通知

最近,需要公司需要寫一個app下單之後,後臺要接收到提醒的功能,需要伺服器向瀏覽器推送下單提醒通知。查了好多資料,中間也是遇到了一些坑,所以在這裡記錄一下。第一次寫部落格,有不對的地方,希望大牛指正。

一、要做的準備工作:

1.將專案放在tomcat裡面

2.下載一個wepsockt-api的jar包

3.新建一個websocketController

4.新建一個indexConter和一個test頁面(這裡因為涉及到 公司的私密,所以暫時搞了個test頁面)

二、wepsockt-api.jar包下載

三.websocketContrller程式碼

package com.mult.work.websocket;

import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

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 com.jfinal.kit.Kv;
import com.jfinal.kit.LogKit;
import com.jfinal.kit.StrKit;


@ServerEndpoint("/websocket.ws/{ids}")
public class WebSocketController {

    public static final WebSocketController me = new WebSocketController();
  //用來存放每個客戶端對應的MyWebSocket物件。
    private static final Map<String, WebSocketController> WEB_SOCKET_MAP = new ConcurrentHashMap<String, WebSocketController>();
    private static final Map<String, Kv> WEB_KV = new ConcurrentHashMap<String, Kv>();

    //與某個客戶端的連線會話,需要通過它來給客戶端傳送資料
    private Session session;
    //UUID 生成的IDS
    private String ids;
    //name 暱稱
    private String name;
    
    /**
     * 連線建立成功呼叫的方法
     * @param session  可選的引數。session為與某個客戶端的連線會話,需要通過它來給客戶端傳送資料
     */
    @OnOpen
    public void onOpen(@PathParam("ids")String ids,Session session){
    	this.ids = ids;
        this.session = session;
        WEB_SOCKET_MAP.put(ids, this);
        this.name = getKvByIds(ids).getStr("name");
        
        System.out.println("session=" + session.getId());
        
    }

    /**
     * 連線關閉呼叫的方法
     */
    @OnClose
    public void onClose(){
    	WEB_SOCKET_MAP.remove(this.session);
    	System.out.println("有一連線關閉!當前線上人數為" + WEB_SOCKET_MAP.size());
    	System.out.println("session=" + session.getId());
        
        sendMessage(this.name);
    }

    /**
     * 收到客戶端訊息後呼叫的方法
     * @param message 客戶端傳送過來的訊息
     * @param session 可選的引數
     */
    @OnMessage
    public void onMessage(String message, Session session) {
    	System.out.println("來自客戶端的訊息:" + message);
    	System.out.println("session=" + session.getId());
        sendMessage(message);
    }

    /**
     * 發生錯誤時呼叫
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error){
        error.printStackTrace();
    }
    /**
     * 指定 傳送訊息
     */
    public void sendMessage(String ids, String message){
    	WebSocketController me = WEB_SOCKET_MAP.get(ids);
    	if (me != null) {
    		me.sendMessage(message);
		}
//    	else{
//			this.sendMessage("【系統訊息】" + getNameByIds(ids) + " 不線上");
//		}
    }
    
    /**
     * 向客戶端 傳送訊息
     */
    protected void sendMessage(String message){
        try {
			this.session.getBasicRemote().sendText(message);
		} catch (IOException e) {
			e.printStackTrace();
		}
        //this.session.getAsyncRemote().sendText(message);
    }
    
    public static Kv getKvByIds(String ids){
    	Kv kv = WEB_KV.get(ids);
    	if (kv == null) {
			WEB_KV.put(ids, new Kv());
			return getKvByIds(ids);
		}
		return kv;
    }
    
    public static String getNameByIds(String ids){
    	return getKvByIds(ids).getStr("name");
    }
    
    public static String setNameByIds(String ids, String name){
    	if (StrKit.isBlank(name)) {
    		name = "匿名使用者";
    	}
    	Kv kv = getKvByIds(ids);
    	if ( ! name.equals(kv.get("name"))) {
    		kv.set("name", name);
    		// 同步成員變數
    		WebSocketController me = WEB_SOCKET_MAP.get(ids);
    		if (me != null) {
				me.name = name;
			}
		}
    	return name;
    }
}

@ServerEndpoint("/websocket.ws/{ids}")因為這裡沒有在config裡面配置,所以要在路由這裡加上一個.ws的字尾,不然的話會報404的錯

 四、indexController檔案程式碼段

package com.mult.work.websocket;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import com.mult.common.annotation.Controller;
import com.mult.common.controller.WorkController;

/**
 * 總控制器
 * @author dufuzhong
 *
 */
@Controller(controllerKey = "/websocket/index")
public class IndexController extends WorkController {
	/***
	 * 設定暱稱
	 */
	public void getwebsocket(){
		String host = getPara("host");
		String name = getPara("name");
		String ids = getPara("ids");
		
		if (StrKit.isBlank(ids)) {
			ids = StrKit.getRandomUUID();
			setCookie("websocket_ids", ids, (3 * 365 * 24 * 60 * 60), true);
		}
		
		name = WebSocketController.setNameByIds(ids, name);
		setCookie("websocket_name", encode(name), (3 * 365 * 24 * 60 * 60), true);
		
		renderJson(Ret.ok("url", "ws://" + host + "/websocket.ws/" + ids));
	}

	private String encode(String name){
		if (StrKit.notBlank(name)) {
			try {
				return URLEncoder.encode(name, "utf-8");
			} catch (UnsupportedEncodingException e) {}
		}
		return null;
	}
	
	private String decode(String name){
		if (StrKit.notBlank(name)) {
			try {
				return URLDecoder.decode(name, "utf-8");
			} catch (UnsupportedEncodingException e) {}
		}
		return null;
	}
	
}

五、test頁面程式碼段

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- for HTML5 -->
<title>Java後端WebSocket的Tomcat實現</title>
</head>
<body>
	Welcome
	<br />
	<input id="text" type="text" />
	<button onclick="send()">傳送訊息</button>
	<hr />
	<button onclick="closeWebSocket()">關閉WebSocket連線</button>
	<hr />
	<div id="message"></div>
</body>
<script type="text/javascript">
	var websocket = null; //判斷當前瀏覽器是否支援WebSocket
	var name = $("#name").val();
	var host = window.location.host;//+window.location.pathname;
	$.getJSON("/websocket/index/getwebsocket", { "name": name, "host": host,"ids":88 }, function(ret){
        if(ret.state == "ok") {
       	    newWebSocket(ret.url);
        }else{
            alert('抱歉,失敗')
        }
    });
	 function newWebSocket(url){
		    if ('WebSocket' in window) {
		        websocket = new WebSocket(url);
		    } else {
		        alert('請使用火狐或谷歌瀏覽器')
		        return;
		    }
		    //連線發生錯誤的回撥方法
		    websocket.onerror = function () {
		        setMessageInnerHTML("離線");
		    }

		    //連線成功建立的回撥方法
		    websocket.onopen = function () {
		        setMessageInnerHTML("上線");
		    }

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

		    //連線關閉的回撥方法
		    websocket.onclose = function () {
		        setMessageInnerHTML("下線");
		    }

	    }

	//監聽視窗關閉事件,當視窗關閉時,主動去關閉websocket連線,防止連線還沒斷開就關閉視窗,server端會拋異常。
	window.onbeforeunload = function() {
		closeWebSocket();
	} 
	//將訊息顯示在網頁上
	function setMessageInnerHTML(innerHTML) {
		document.getElementById('message').innerHTML += innerHTML + '<br/>';
	} 
	//關閉WebSocket連線
	function closeWebSocket() {
		websocket.close();
	} 
	//傳送訊息
	function send() {
		var message = document.getElementById('text').value;
		websocket.send(message);
	}
</script>
</html>

六、總結

1.公司伺服器用的是tomcat和nginx反代理,所以在配置的時候要注意下配置nginx的配置。

2.websocket-api.jar包在伺服器環境中可以不要,但是在開發環境中需要,不然的話會報錯的。