1. 程式人生 > 其它 >mqtt長連線報錯32000

mqtt長連線報錯32000

背景

專案需要使用mqtt協議建立長連線,我是客戶端,需要連上服務端同學的提供的地址;客戶端使用的是paho提供的客戶端sdk,如下:

<dependency>
    <groupId>org.eclipse.paho</groupId>
    <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
    <version>1.2.5</version>
</dependency>

建立連線程式碼也十分簡單,和github上demo差不多:

public void createMqttConnection(){
        try {
            String ts = System.currentTimeMillis() + "";
            String password = "password";
            String clientId = "server_" + InetAddress.getLocalHost().getHostAddress() + "_" + ts;
            String broker = mqttConfig.getMqttHost();

            MemoryPersistence persistence = new MemoryPersistence();
            mqttClient = new MqttClient(broker, clientId, persistence);
            mqttClient.setCallback(new MqttCallback() {
                @Override
                public void connectionLost(Throwable cause) {
                    System.out.println("連線斷開");
                    cause.printStackTrace();
                }

                @Override
                public void messageArrived(String topic, MqttMessage message) throws Exception {

                }

                @Override
                public void deliveryComplete(IMqttDeliveryToken token) {

                }
            });
            MqttConnectOptions connectOptions = new MqttConnectOptions();
            connectOptions.setUserName("server");
            connectOptions.setPassword(password.toCharArray());
            connectOptions.setAutomaticReconnect(true);
            connectOptions.setCleanSession(true);
            connectOptions.setKeepAliveInterval(20);

            log.info("Mqtt connection Initialization begin!");
            mqttClient.connect(connectOptions);
            log.info("Mqtt connection success!");
        } catch (Exception e) {
            log.error("Mqtt connection initialization error ", e);
        }
    }

問題

服務啟動,併成功連線上過後,總是報錯:等待來自伺服器的響應超時(32000),如下圖

排查

開啟wireshark抓包,結果如下,時間順序是從下到上,發現,TCP三次握手建立連線後(362,363,364),併成功驗證了username,password後(392,393,395,396),的確20s後(keepAliveInterval設定的20),客戶端向伺服器傳送了心跳包(3456【PSH, ACK】),這個傳送,是被【ACK】(3458)了,但是在接下來的20s內,伺服器都沒有返回心跳給客戶端,在客戶端看來,那麼伺服器就是掛了,所以斷開連線,產生了接下來的【FIN ACK】(6136),四次揮手(6137,6138)斷開了TCP連線(至於為什麼四次揮手只抓到3個包,後文有補充);由於設定了setAutomaticReconnect=true

,又開始三次握手建立連線了。

結論

伺服器沒有返回心跳給客戶端,客戶端以為伺服器掛了,就斷開連線報錯。需要讓伺服器端同學,修復一下心跳傳送相關程式碼。

為什麼四次揮手只有3個包
參考:Linux之TCP的實現,四次揮手
大概意思就是Linux中支援TCP的延遲ACK機制,傳送ack的條件不能滿足立即傳送ack的條件,導致ack的傳送被延時了,於是和下一個FIN合在一起發;包括本文中問題修復正常後,伺服器端(Linux)返回的心跳也有這個ACK延遲