Mina僞裝接收到心跳回復

在應用中,作爲客戶端,可能會遇到服務端不回覆心跳的情況,那麼這個時候就需要客戶端自發自收。

不必費力寫一個定時器向自身發送心跳回復,直接在發送時調用其內部API就可以:

public class KeepAliveGatewayZigbee implements KeepAliveMessageFactory {

	public static final String ROBOT_HEART = "robot_heart";
	public static final String GATEWAY_HEART = "gateway_heart";

	@Override
	public boolean isRequest(IoSession ioSession, Object o) {
		if (o instanceof String) {
            String msg = (String) o;
			if (ROBOT_HEART.equalsIgnoreCase(msg)) {
                L.v("發送心跳");
				//假裝收到心跳
                ioSession.getFilterChain().fireMessageReceived(GATEWAY_HEART);
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean isResponse(IoSession ioSession, Object o) {
		if (o instanceof String) {
            String msg = (String) o;
			if (GATEWAY_HEART.equalsIgnoreCase(msg)) {
                L.v("接收心跳");
				return true;
			}
		}
		return false;
	}

	@Override
	public Object getRequest(IoSession ioSession) {
		return ROBOT_HEART;
	}

	@Override
	public Object getResponse(IoSession ioSession, Object o) {
		return o;
	}
}

是的,只需要在判斷爲發送心跳的isRequest方法中,調用一次fireMessageReceived傳入應該回復的心跳即可。

究其原理,是因爲我們清楚整個消息傳遞是怎樣的。
這個可以從Mina源碼看心跳超時機制略知一二,如同心跳超時調用sessionIdle一樣,消息傳遞會回調一系列類似於sessionIdle的方法。

當我們實現心跳機制時,肯定都會使用類似

getFilterChain().addLast(HEARTBEAT, keepAliveFilter)

的代碼添加一個KeepAliveFilter,KeepAliveFilter繼承自IoFilterAdapter。
當這個Filter被添加到FilterChain中時,每次session的動作都會觸發其相應的方法。而KeepAliveFilter正是對這些方法進行了覆寫,實現了一些判斷的邏輯。

比如每次發送消息後與接收消息後:

//KeepAliveFilter.java
    @Override
    public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
        Object message = writeRequest.getMessage();
        
        if (!isKeepAliveMessage(session, message)) {
            nextFilter.messageSent(session, writeRequest);
        }
    }

    @Override
    public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
        try {
            if (messageFactory.isRequest(session, message)) {
                Object pongMessage = messageFactory.getResponse(session, message);

                if (pongMessage != null) {
                    nextFilter.filterWrite(session, new DefaultWriteRequest(pongMessage));
                }
            }

            if (messageFactory.isResponse(session, message)) {
                resetStatus(session);
            }
        } finally {
            if (!isKeepAliveMessage(session, message)) {
                nextFilter.messageReceived(session, message);
            }
        }
    }

每次發送消息後或接收消息後,KeepAliveFilter會使用isKeepAliveMessage方法判斷髮送的消息是否爲心跳消息。而這個方法是交給開發者實現的:

    private final KeepAliveMessageFactory messageFactory;

    private boolean isKeepAliveMessage(IoSession session, Object message) {
        return messageFactory.isRequest(session, message) || messageFactory.isResponse(session, message);
    }

因爲這個“||"邏輯或的原因,當isRequest爲真時,isResponse就不會調用了,而當isRequest爲假時,isResponse纔會調用。

所以會有這麼一種現象:
發送心跳消息時,因爲經過messageSent,所以會調用一次isRequest方法;
收到心跳回復時,因爲經過messageReceived,所以會將isRequest和isResponse先調用一次,然後再在isKeepAliveMessage中重複調用一次isRequest和isResponse方法。

在這麼一種邏輯下,當服務端沒有回覆心跳時,完全可以模擬接收到心跳的回覆,假裝自己已經收到心跳回復,避免因心跳超時問題而被服務端斷開或自行斷開。

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