使用LwIP回調編程實現paho開源MQTT庫的移植

來源:互聯網
上載者:User

標籤:lwip   raw   tcp_connect   mqtt   paho   

文章內容為工程調試記錄,內容排版散亂,閱讀慎入!
LwIP Raw回調編程調試記錄
1. tcp_connect串連問題:
1.1 串連不上,沒有任何的回呼函數調用,延時可以偶爾的解決這個問題解決思路:在軟體定時器中定時檢測TCP_PCB塊的狀態
1.2 伺服器主動斷開的話,在tcp_recv_cb中會收到無資料響應此處判斷其狀態應該是已經斷開
1.3 tcpip_callback做何用,屏蔽之後帶來的影響該函數往tcpip線程中發送了一條郵箱,tcpip線程收到之後,調用傳入的回呼函數將進行中的申請的tpcb加入到tcp_tw_pcbs鏈表中,並在tcpip進程中註冊一個定時器事件作為當前pcb串連的核心定時器。通過後續的分析,發現當前tpcb建立串連後,串連成功回呼函數立馬將當前tpcb從tcp_tw_pcbs中移除。可見,其本意是通過tcpip回調的方式為當前tpcb開啟一個核心定時器。因為在普通進程或函數中調用的tcp_connect雖然也有TCP_REG的調用,但卻是把當前tpcb的軟體定時器事件掛在到當前線程的軟體定時器鏈表上,如果當前調用tcp_connect的進程不是通過sys_thread_new建立的,或者就是個普通函數,這時候連軟體定時器鏈表也找不到,自然也沒有定時事件的觸發,這也是困擾我多日為什麼tpcb的核心定時器沒有啟動的原因。
1.4 2個軟體定時器分別負責斷開檢測及重發工程內建有ttcp.c常式用於測試raw tcp,而自己也主要參考該常式編寫,此處困擾我多日的一點是:採用了軟體定時器來處理資料的續發。因為資料可能無法一次性傳完,ttcp.c中採用了軟體定時器來發送當前要發送資料的剩餘資料,在實際測試中大多數時候是沒問題的,但是參照該種方式,卻時不時的出現資料無法發送。調試發現是發送緩衝區滿,抓包發現是伺服器不停的回複一個ACK值,而lwip用戶端沒有重發,經過老衲五木的提醒,意識到lwip中tcp核心的資料處理是在一個進程中完成的,頻繁的觸發定時器來執行tpc_write操作,可能打亂tcp核心的執行順序,出現上述問題。改良後的版本使用tcp_sent來續發剩餘資料,只用一個軟體定時器來檢測tpcb的狀態。
1.5. 建立串連及未串連的狀態建立串連: Current PCB:2000b3f8, state = ESTABLISHED一直未串連成功: Current PCB:2000b3f8, state = SYN_SENT伺服器斷開:其它狀態串連不上,偶爾會回調tcp_err中的回呼函數,需要標識該狀態,等待定時器的處理
2. 接收到的資料異常
犯了個低級的錯誤,將之前的代碼    mqtt_comm_p->recvlen = p->tot_len;       for ( data_p = mqtt_comm_p->recvbuf, q = p;  q->next != NULL; q = q->next )    {      memcpy(data_p, q->payload, q->len);      data_p += q->len;    }改成如下代碼    mqtt_comm_p->recvlen = p->tot_len;        data_p = mqtt_comm_p->recvbuf;    q = p;       while ( q != NULL )      {      memcpy(data_p, q->payload, q->len);      data_p += q->len;      q = q->next;    }
3. 資料無法立即發送,接收資料後才會發送出去
在調用tcp_write之後,資料並不會立即發送出去,緊隨其後調用tcp_ouput會將掛在的資料發送出去。在當前寫的發送函數中,沒有選擇使用tcp_output,因為tpcb的核心定時器會定時調用它。

4. 資料發送的處理
最初的想法:使用隊列來完成資料的填充與發送。這將使得資料的發送效率降低,因為涉及到資料入隊列的複製,資料出隊列的複製,以及資料拷貝到發送隊列的複製。
新的改進:無隊列,無發送緩衝區,使用號誌及指標來處理髮送。這也是不違背為了節省記憶體的初衷。

5. 花費一天時間尋找兩個HardFault:
5.1 buf指向了常駐記憶體區,之後又指向了靜態記憶體地區    //vPortFree(RtCtrlPkgAck);    //vPortFree(PicInfoPkg);    //vPortFree(buf);釋放出錯,
5.2 訂閱主題初始化不完全  mqtt_comm_p->MQTTPkgSubs->subcount = 1;  //  只有一個訂閱主題
 // 之前初始化迴圈值為mqtt_comm_p->MQTTPkgSubs->subcount,也就是1而不是4,導致後面要用的主題名稱topicString內部變數未初始化,拷貝資料記憶體硬體錯誤 。                    for (i = 0; i < 4 ; i++)   {     mqtt_comm_p->MQTTPkgSubs->topicString[i].cstring = NULL;     mqtt_comm_p->MQTTPkgSubs->topicString[i].lenstring.data = NULL;     mqtt_comm_p->MQTTPkgSubs->topicString[i].lenstring.len = 0; // MQTTString_initializer     mqtt_comm_p->MQTTPkgSubs->QoSs[i] = 0; // 服務品質}
教訓:記憶體的分配和釋放處理,一定要小心小心再小心;要養成變數初始化的好習慣。
6. 軟體定時器中建立的tpcb控制塊,使用tcp_write無法發送資料
6.1 tpcb塊的tcp_connect操作中有TCP_REG調用,其本意是註冊當前tpcb到tcp active鏈表中,再在tcpip逾時鏈表中建立一個逾時定時器。但是tcp_connect並不在tcpip中調用,也就無法在tcpip逾時鏈表中建立定時器,tpcb核心定時器自然無法啟動,資料無法發送,這也是為什麼需要tcpip_callback(tcp_connect_timer_cb, mqtt_comm_p->tpcb); 的原因;改進:可以不在外部程式中調用tcpip_callback,而是直接tcp_connect中的TCP_REG(); 替換成tcpip_callback,不過這改動了核心的代碼,謹慎。tpcb斷開後,謹慎起見,也需要TCP_RMV刪除,防止下次無法建立新的TCP核心定時器sys_timeout。
6.2 不使用軟體定時器來觸發重發,以免打亂tcp核心的資料處理,改用tcp_sent完成剩餘資料發送。
6.3 以上2點原因得益於老衲五木的提點,在此非常感謝~。
7. LwIP raw tcp發送程式參考:
/ MQTT 資料發送函數static OSStatus tcp_mqtt_send(struct mqtt_comm_t *mqtt_comm_p, uint8_t data[], uint32_t len, int msBlock, uint8_t apiflag){    mqtt_comm_p->sendbuf = data;    mqtt_comm_p->sendlen = len;    mqtt_comm_p->apiflags = apiflag;         sys_log("tcp_mqtt_send,sendlen: %d.\r\n", mqtt_comm_p->sendlen);    tcp_send_data(mqtt_comm_p);        if( xSemaphoreTake(mqtt_comm_p->sendsema, msBlock) != pdTRUE )    {           sys_log("tcp send timeout.\r\n");        return TCP_MQTT_SEND_ERR;    }       if (mqtt_comm_p->sendlen != 0)        return TCP_MQTT_SEND_ERR;   else      return NoErr;}// TCP 資料發送函數void tcp_send_data(struct mqtt_comm_t *mqtt_comm_p){    err_t err;    uint32_t len;    len = mqtt_comm_p->sendlen;    len = (len > tcp_sndbuf(mqtt_comm_p->tpcb) ) ?  tcp_sndbuf(mqtt_comm_p->tpcb) : len;        // 串連斷開    if ( (mqtt_comm_p->tpcb == NULL ) || (mqtt_comm_p->tpcb->state != ESTABLISHED) )    {        xSemaphoreGive(mqtt_comm_p->sendsema);        sys_log("tcp_send_data: tpcb error.\r\n");        return;    }    // 發送緩衝區滿    if(tcp_sndbuf(mqtt_comm_p->tpcb) == 0)    {        vTaskDelay(1);        return;    }     do    {        err = tcp_write(mqtt_comm_p->tpcb, mqtt_comm_p->sendbuf, len, mqtt_comm_p->apiflags);        if (err == ERR_MEM)            len /= 2;    }    while (err == ERR_MEM && len > 1);    if (err == ERR_OK)    {        mqtt_comm_p->sendlen -= len;        mqtt_comm_p->sendbuf += len;    }    else        sys_log("[tcp_send_data]: tcp_write failed.\r\n");    }err_t tcp_send_cb(void *arg, struct tcp_pcb *tpcb, u16_t len){  struct mqtt_comm_t *mqtt_comm_p = arg;    if ( mqtt_comm_p->sendlen > 0 )  {    tcp_send_data(mqtt_comm_p);  }  else  {    xSemaphoreGive(mqtt_comm_p->sendsema);  }    return ERR_OK;}



MQTT移植記錄
未完待續

使用LwIP回調編程實現paho開源MQTT庫的移植

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.