mqtt長連線報錯32000
阿新 • • 發佈:2022-05-17
背景
專案需要使用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延遲