ESP8266 SDK開發: 網絡篇-8266開啟TCP服務器(LWIP,RAW模式,PCB控制塊)


 

 

 

 

前言

關於網絡通信:

每一台電腦都有自己的ip地址,每台電腦上的網絡應用程序都有自己的通信端口,

張三的電腦(ip:192.168.1.110)上有一個網絡應用程序A(通信端口5000),

李四的電腦(ip:192.168.1.220)上有一個網絡應用程序B(通信端口8000),

張三給李四發消息,首先你要知道李四的ip地址,向指定的ip(李四ip:192.168.1.220)發信息,

信息就發到了李四的電腦。

再指定一下發送的端口號(通信端口8000),信息就發到了李四電腦的網絡應用程序B上。

TCP--一種網絡通信方式而已。分為服務器(網絡應用程序)和客戶端(網絡應用程序).

 

說明

對於網絡模塊而言實現網絡通信最終還是使用的 LWIP

LWIP實際上是別人為了讓小型網絡設備實現網絡通信,

而開發的低內存易移植的網絡傳輸解析程序.

LWIP實現網絡通信可以使用操作系統,也可以裸機

咱這節就使用 PCB控制塊實現TCP服務器

 

1.包含以下頭文件

 

 

 

#include "lwip/api.h"
#include "lwip/err.h"
#include "lwip/ip_addr.h"
#include "lwip/dns.h"
#include "lwip/igmp.h"
#include "lwip/tcp.h"

 

2.new 一個TCP控制塊

 

 

 

err_t err = ERR_OK;//接收返回的錯誤信息
struct tcp_pcb *tcp_pcb1 = tcp_new();//建立一個TCP控制塊

 

3. 綁定IP地址和端口號,啟動監聽

 

 

 

    //控制塊綁定IP地址和端口號
    err = tcp_bind(tcp_pcb1, IP_ADDR_ANY, 8080);//IP_ADDR_ANY:綁定本模塊IP  8080:綁定8080端口
    if (err == ERR_OK) {//沒有錯誤
        struct tcp_pcb *pcb1 = tcp_listen(tcp_pcb1);//啟動監聽
    }

 

 

4. 設置客戶端連接回調函數

 

 

 

 

 

5. 下載測試(手機APP連接測試)

 

5.1手機APP連接模塊無線

 

 

 

5.2 使用手機APP調試助手測試

安裝調試助手

 

點擊左上角菜單

 

 

 

 

 

點擊網絡通信

 

 

 

選擇 TCP/UDP通信

 

 

 

選擇TCP客戶端,IP地址192.168.4.1(8266默認IP)

端口號:8080

點擊 連接

 

 

 

 

 

 

 

6. 下載測試(電腦上位機連接測試)

 

 

 

電腦連接8266無線

 

 

 

 

 

 

 

 

 

 

 

 

 

 

接收/發送數據

以后接收/發送數據都是通過獲取的tcp_pcb

 

 

 

 

 為了咱方便各個地方使用,咱定義一個公共的tcp_pcb

 

 

 

 

struct tcp_pcb *tcp_pcb_server;//定義一個TCP控制塊

 

 

注冊其它回調函數

 

 

 

 

 

 

 

 

 

 

 

/**
* @brief   TCP接收數據
* @param   arg:tcp_arg函數傳入的參數
* * @param   p:接收的數據緩存
* @param   err:錯誤信息
* @param   None
* @retval  None
* @warning None
* @example
**/
static err_t net_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
    tcp_pcb_server = tpcb;

    if (!p || err!=ERR_OK) {
        if(p){
            pbuf_free(p);
        }
        tcp_close(tcp_pcb_server);//關閉連接
        return ERR_CLSD;
    }

    //固定處理
    tcp_recved(tcp_pcb_server, p->tot_len);/*更新接收,告訴底層可以接着緩存數據了*/
    pbuf_free(p);//釋放鏈表
    return ERR_OK;
}


/**
* @brief   TCP鏈接錯誤
* @param   arg:tcp_arg函數傳入的參數
* @param   err:錯誤信息
* @param   None
* @param   None
* @retval  None
* @warning None
* @example
**/
static void net_err_cb(void *arg, err_t err) {
    tcp_pcb_server = (struct tcp_pcb*)arg; //tcp_arg傳遞了該參數
    tcp_close(tcp_pcb_server);//關閉連接
    tcp_pcb_server = NULL;//清空
}

/**
* @brief   客戶端連接回調
* @param   arg:tcp_arg函數傳入的參數
* @param   newpcb:鏈接的TCP控制塊
* @param   err:錯誤信息
* @param   None
* @retval  None
* @warning None
* @example
**/
static err_t net_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err) {
    tcp_pcb_server = newpcb;//賦值給定義的控制塊

    tcp_arg(newpcb, newpcb);//傳遞的arg參數為 tcp_pcb_server
    tcp_err(newpcb, net_err_cb);//錯誤回調
    tcp_recv(newpcb, net_tcp_recv_cb);//接收數據回調

    printf("客戶端連接 \n");
    return ERR_OK;
}

 

 

 

 

串口輸出TCP接收的數據

 

 

 

 

 

 

 

 

#define TcpServerBuffLen 1460
u8 TcpServerBuff[TcpServerBuffLen];//接收緩存

 

 

/**
* @brief   TCP接收數據
* @param   arg:tcp_arg函數傳入的參數
* * @param   p:接收的數據緩存
* @param   err:錯誤信息
* @param   None
* @retval  None
* @warning None
* @example
**/
static err_t net_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
    struct pbuf *q;
    u32 length = 0,i=0;

    tcp_pcb_server = tpcb;

    if (!p || err!=ERR_OK) {
        if(p){
            pbuf_free(p);
        }
        tcp_close(tcp_pcb_server);//關閉連接
        return ERR_CLSD;
    }

    //接收TCP數據(固定處理)
    for(q=p;q!=NULL;q=q->next)
    {
        if(q->len > (TcpServerBuffLen-length))//接收的數據個數大於了數組可以接收的數據個數
            memcpy(TcpServerBuff+length,q->payload,(TcpServerBuffLen-length));//只接收數組可以接收的數據個數
        else
            memcpy(TcpServerBuff+length,q->payload,q->len);//接收TCP所有數據
        length += q->len;
        if(length > TcpServerBuffLen) break;
    }


    /*串口輸出接收的數據*/
    for(i=0;i<length;i++){
        uart_tx_one_char(UART0,TcpServerBuff[i]);
    }

    //固定處理
    tcp_recved(tcp_pcb_server, p->tot_len);/*更新接收,告訴底層可以接着緩存數據了*/
    pbuf_free(p);//釋放鏈表

    return ERR_OK;
}

 

 

說明:

對於初學者而言,有些地方不懂為什么這樣做!

其實LWIP確實挺復雜的,咱們先學會用!

對於接收數據而言這樣接收完全沒有問題

大家可以直接先用即可,如果后期大家有時間可以慢慢的

了解LWIP

 

 

 

 

 

我只提示一下

LWIP存儲數據使用的鏈表形式

 

 

 

假設數據來了,因為數據的個數不一定,而每一個鏈表存數據的個數都是有限的,

所以呢就出現了上圖,把數據分割依次存入幾個鏈表中

 

測試

 

 

 

 

 

 

 

 

 

 

 

模塊串口接收的數據轉發給TCP客戶端

判斷串口接收到一條完整的數據以后,把數據發給客戶端

串口判斷接收到一條完整的數據參考串口章節:

https://www.cnblogs.com/yangfengwu/p/12375342.html

 

忘了一件事情,需要定義一個變量來判斷客戶端是不是連接了

 

 

 

接收回調里面

 

 

 

 

鏈接錯誤回調函數 和 客戶端鏈接回調函數里面

 

 

 

 

好接着寫咱的串口數據轉發給TCP客戶端程序

 

 

 

 

測試

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM