ESP8266學(xué)習(xí)筆記(5)——TCP/UDP接口使用

一、TCP與UDP優(yōu)缺點(diǎn)

1、TCP面向連接(如打電話要先撥號(hào)建立連接);UDP是無(wú)連接的,即發(fā)送數(shù)據(jù)之前不需要建立連接。

2、TCP提供可靠的服務(wù)。也就是說(shuō),通過(guò)TCP連接傳送的數(shù)據(jù),無(wú)差錯(cuò),不丟失,不重復(fù),且按序到達(dá);UDP盡最大努力交付,即不保證可靠交付。
TCP通過(guò)校驗(yàn)和,重傳控制,序號(hào)標(biāo)識(shí),滑動(dòng)窗口、確認(rèn)應(yīng)答實(shí)現(xiàn)可靠傳輸。如丟包時(shí)的重發(fā)控制,還可以對(duì)次序亂掉的分包進(jìn)行順序控制。

3、UDP具有較好的實(shí)時(shí)性,工作效率比TCP高,適用于對(duì)高速傳輸和實(shí)時(shí)性有較高的通信或廣播通信。

4、每一條TCP連接只能是點(diǎn)到點(diǎn)的;UDP支持一對(duì)一,一對(duì)多,多對(duì)一和多對(duì)多的交互通信。

5、TCP對(duì)系統(tǒng)資源要求較多,UDP對(duì)系統(tǒng)資源要求較少。

二、TCP客戶端

TCP&UDP 接口位于 ESP8266_NONOS_SDK/include/espconn.h

注意:充當(dāng)TCP客戶端,必須要知道服務(wù)端的IP地址。

Step 1) 包含頭文件

#include "osapi.h"
#include "user_interface.h"
#include "espconn.h"
#include "mem.h"

Step 2) 定義一個(gè)TCP連接結(jié)構(gòu)體

struct espconn tcp_client_conn;

Step 3) 定義TCP客戶端初始化函數(shù)

/**
 @brief 初始化TCP客戶端
 @param remote_port 遠(yuǎn)程端口
 @return 無(wú)
*/
void ICACHE_FLASH_ATTR
user_tcpclient_init(uint32 remote_port)
{
    // 1. 定義相關(guān)結(jié)構(gòu)體
    struct ip_info local_info;                                  //  定義一個(gè)用于存放本地IP信息的結(jié)構(gòu)體
    struct ip_addr remote_ip;                                   //  定義一個(gè)用于存放遠(yuǎn)程IP地址的結(jié)構(gòu)體

    // 2.配置TCP客戶端相關(guān)參數(shù)
    char remote_ipbuffer[32] = "192.168.100.1";
    remote_ip.addr = ipaddr_addr(remote_ipbuffer);              // 點(diǎn)分十進(jìn)制寫(xiě)入IP結(jié)構(gòu)體

    tcp_client_conn.type = ESPCONN_TCP;                        // 設(shè)置類型為T(mén)CP協(xié)議
    tcp_client_conn.state = ESPCONN_NONE;                      // 設(shè)置為無(wú)連接狀態(tài)
    tcp_client_conn.proto.tcp = (esp_tcp *) os_zalloc(sizeof(esp_tcp));  

    wifi_get_ip_info(STATION_IF, &info);                       // 查詢連接路由器后分配的IP地址
    os_memcpy(tcp_client_conn.proto.tcp->local_ip, &local_info.ip, 4);  // 設(shè)置本地IP地址
    os_memcpy(tcp_client_conn.proto.tcp->remote_ip, &remote_ip, 4);     // 設(shè)置遠(yuǎn)程IP地址

    tcp_client_conn.proto.tcp->local_port = espconn_port(); // 獲取可用端口作為本地端口
    tcp_client_conn.proto.tcp->remote_port = remote_port;      // 設(shè)置遠(yuǎn)程端口

    // 3.注冊(cè)連接成功和重連的回調(diào)函數(shù)
    espconn_regist_connectcb(&tcp_client_conn, tcp_client_connect_cb);
    espconn_regist_reconcb(&tcp_client_conn, tcp_client_recon_cb);

    // 4.連接服務(wù)器
    espconn_connect(&tcp_client_conn);
}

Step 4) 定義相關(guān)回調(diào)函數(shù)

/**
 @brief 成功連接到服務(wù)器的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
tcp_client_connect_cb(void *arg)
{
    struct espconn *pespconn = arg;
    espconn_regist_recvcb(pespconn, tcp_client_recv_cb);        // 注冊(cè)接收回調(diào)函數(shù)
    espconn_regist_sentcb(pespconn, tcp_client_send_cb);        // 注冊(cè)發(fā)送回調(diào)函數(shù)
    espconn_regist_disconcb(pespconn, tcp_client_discon_cb);    // 注冊(cè)斷連回調(diào)函數(shù)
    espconn_sent(pespconn, "8226", strlen("8226"));             // 發(fā)送消息  
}

/**
 @brief 連接失敗的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @param err 為錯(cuò)誤代碼
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
tcp_client_recon_cb(void *arg, sint8 err)
{
    os_printf("tcpclient connect error, error number:%d\r\n", err);
    espconn_connect((struct espconn *) arg);                   // 連接服務(wù)器
}

/**
 @brief 成功接收到服務(wù)器返回?cái)?shù)據(jù)的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @param pdata 字符串?dāng)?shù)組
 @param len 字符串?dāng)?shù)組長(zhǎng)度
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
tcp_client_recv_cb(void *arg, char *pdata, unsigned short len)
{
    os_printf("tcpclient_recvdata:\t%s\n", pdata);
}

/**
 @brief 發(fā)送數(shù)據(jù)到服務(wù)器成功的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
tcp_client_send_cb(void *arg)
{
    os_printf("tcpclient_send succeed!\n");
}

/**
 @brief 斷開(kāi)服務(wù)器成功的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
tcp_client_discon_cb(void *arg)
{
    os_printf("tcpclient_disconnect!\n");
}

三、TCP服務(wù)端

注意:充當(dāng)服務(wù)端時(shí)候,要自身開(kāi)啟WIFI熱點(diǎn),等待設(shè)備接入,好比一個(gè)網(wǎng)關(guān)。

Step 1) 包含頭文件

#include "osapi.h"
#include "user_interface.h"
#include "espconn.h"
#include "mem.h"

Step 2) 定義一個(gè)TCP連接結(jié)構(gòu)體

struct espconn tcp_server_conn;

Step 3) 定義TCP服務(wù)端初始化函數(shù)

/**
 @brief 初始化TCP服務(wù)端
 @param local_port 本地端口
 @return 無(wú)
*/
void ICACHE_FLASH_ATTR
user_tcpserver_init(uint32 local_port)
{
    // 1.配置TCP服務(wù)端相關(guān)參數(shù)
    tcp_server_conn.type = ESPCONN_TCP;                        // 設(shè)置類型為T(mén)CP協(xié)議
    tcp_server_conn.state = ESPCONN_NONE;                      // 設(shè)置為無(wú)連接狀態(tài)
    tcp_server_conn.proto.tcp = (esp_tcp *) os_zalloc(sizeof(esp_tcp));  

    tcp_server_conn.proto.tcp->local_port = local_port;        // 設(shè)置本地端口

    // 2.注冊(cè)監(jiān)聽(tīng)成功和重連的回調(diào)函數(shù)
    espconn_regist_connectcb(&tcp_server_conn, tcp_server_listen_cb);
    espconn_regist_reconcb(&tcp_server_conn, tcp_server_recon_cb);

    // 3.開(kāi)始監(jiān)聽(tīng)
    espconn_accept(&tcp_server_conn);                          // 創(chuàng)建 TCP server,建立監(jiān)聽(tīng)
    espconn_regist_time(&tcp_server_conn, 180, 0);             // 設(shè)置超時(shí)斷開(kāi)時(shí)間 單位:秒,最大值:7200 秒
}

Step 4) 定義相關(guān)回調(diào)函數(shù)

/**
 @brief 成功連接到客戶端的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
tcp_server_listen_cb(void *arg)
{
    struct espconn *pespconn = arg;
    espconn_regist_recvcb(pespconn, tcp_server_recv_cb);        // 注冊(cè)接收回調(diào)函數(shù)
    espconn_regist_sentcb(pespconn, tcp_server_send_cb);        // 注冊(cè)發(fā)送回調(diào)函數(shù)
    espconn_regist_disconcb(pespconn, tcp_server_discon_cb);    // 注冊(cè)斷連回調(diào)函數(shù)
}

/**
 @brief 連接失敗的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @param err 為錯(cuò)誤代碼
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
tcp_server_recon_cb(void *arg, sint8 err)
{
    os_printf("tcpserver connect error, error number:%d\r\n", err);
}

/**
 @brief 成功接收到客戶端發(fā)來(lái)數(shù)據(jù)的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @param pdata 字符串?dāng)?shù)組
 @param len 字符串?dāng)?shù)組長(zhǎng)度
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
tcp_server_recv_cb(void *arg, char *pdata, unsigned short len)
{
    os_printf("tcpserver_recvdata:\t%s\n", pdata);
    espconn_sent((struct espconn *) arg, "this is 8266", strlen("this is 8266"));  // 發(fā)送消息
}

/**
 @brief 發(fā)送數(shù)據(jù)成功的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
tcp_server_send_cb(void *arg)
{
    os_printf("tcpserver_send succeed!\n");
}

/**
 @brief 斷開(kāi)連接成功的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
tcp_server_discon_cb(void *arg)
{
    os_printf("tcpserver_disconnect!\n");
}

四、UDP客戶端

注意:充當(dāng)客戶端時(shí)候,可用于廣播消息。

Step 1) 包含頭文件

#include "osapi.h"
#include "user_interface.h"
#include "espconn.h"
#include "mem.h"

Step 2) 定義一個(gè)UDP連接結(jié)構(gòu)體

struct espconn udp_client_conn;

Step 3) 定義UDP客戶端初始化函數(shù)

/**
 @brief 初始化UDP客戶端
 @param remote_port 遠(yuǎn)程端口
 @return 無(wú)
*/
void ICACHE_FLASH_ATTR
user_udpclient_init(uint32 remote_port)
{
    // 1. 定義相關(guān)結(jié)構(gòu)體和變量
    struct ip_info ipconfig;                                   //  定義一個(gè)用于存放IP信息的結(jié)構(gòu)體
    const char udp_remote_ip[4] = {0};                         //  定義一個(gè)用于存放遠(yuǎn)端IP地址的字符串?dāng)?shù)組

    // 2.設(shè)置廣播模式
    wifi_set_broadcast_if(STATION_MODE);                       // 僅在sta模式udp廣播

    // 3.配置UDP客戶端相關(guān)參數(shù)
    udp_client_conn.type = ESPCONN_UDP;                        // 設(shè)置類型為UDP協(xié)議
    udp_client_conn.proto.udp = (esp_udp *) os_zalloc(sizeof(esp_udp));  

    udp_client_conn.proto.udp->local_port = espconn_port();    // 獲取可用端口作為本地端口
    udp_client_conn.proto.udp->remote_port = remote_port;      // 設(shè)置遠(yuǎn)程端口

    wifi_get_ip_info(STATION_IF, &ipconfig);                   // 查詢連接路由器后分配的IP地址
    udp_remote_ip[4] = { 192, 168, ip4_addr3(&ipconfig.ip), 255 };  // 目標(biāo)IP地址(廣播)
    os_memcpy(udp_client_conn.proto.udp->remote_ip, udp_remote_ip, 4);

    // 4.注冊(cè)發(fā)送和接收的回調(diào)函數(shù)
    espconn_regist_sentcb(&udp_client_conn, udp_client_send_cb);
    espconn_regist_recvcb(&udp_client_conn, udp_client_recv_cb);

    // 5.建立UDP傳輸    
    espconn_connect(&udp_client_conn);

    // 6.發(fā)送消息   
    espconn_sent(&udp_client_conn, "8266", strlen("8266"));
}

Step 4) 定義相關(guān)回調(diào)函數(shù)

/**
 @brief 成功接收到服務(wù)端返回?cái)?shù)據(jù)的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @param pdata 字符串?dāng)?shù)組
 @param len 字符串?dāng)?shù)組長(zhǎng)度
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
udp_client_recv_cb(void *arg, char *pdata, unsigned short len)
{
    os_printf("udpclient_recvdata:\t%s\n", pdata);
}

/**
 @brief 發(fā)送數(shù)據(jù)到服務(wù)端成功的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
udp_client_send_cb(void *arg)
{
    os_printf("udpclient_send succeed!\n");
}

五、UDP服務(wù)端

注意:充當(dāng)UDP服務(wù)端時(shí)候,要自身開(kāi)啟WIFI熱點(diǎn),等待設(shè)備接入,好比一個(gè)網(wǎng)關(guān)。

Step 1) 包含頭文件

#include "osapi.h"
#include "user_interface.h"
#include "espconn.h"
#include "mem.h"

Step 2) 定義一個(gè)UDP連接結(jié)構(gòu)體

struct espconn udp_server_conn;

Step 3) 定義UDP服務(wù)端初始化函數(shù)

/**
 @brief 初始化UDP服務(wù)端
 @param local_port 本地端口
 @param remote_port 遠(yuǎn)程端口
 @return 無(wú)
*/
void ICACHE_FLASH_ATTR
user_udpserver_init(uint32 local_port, uint32 remote_port)
{
    // 1.配置UDP服務(wù)端相關(guān)參數(shù)
    udp_server_conn.type = ESPCONN_UDP;                        // 設(shè)置類型為UDP協(xié)議
    udp_server_conn.proto.udp = (esp_udp *) os_zalloc(sizeof(esp_udp));  

    udp_server_conn.proto.udp->local_port = local_port;        // 設(shè)置本地端口
    udp_server_conn.proto.udp->remote_port = remote_port;      // 設(shè)置遠(yuǎn)程端口

    // 2.注冊(cè)發(fā)送和接收的回調(diào)函數(shù)
    espconn_regist_sentcb(&udp_server_conn, udp_server_send_cb);
    espconn_regist_recvcb(&udp_server_conn, udp_server_recv_cb);

    // 3.建立UDP傳輸    
    espconn_connect(&udp_server_conn);
}

Step 4) 定義相關(guān)回調(diào)函數(shù)

/**
 @brief 成功接收到客戶端返回?cái)?shù)據(jù)的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @param pdata 字符串?dāng)?shù)組
 @param len 字符串?dāng)?shù)組長(zhǎng)度
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
udp_server_recv_cb(void *arg, char *pdata, unsigned short len)
{
    os_printf("udpserver_recvdata:\t%s\n", pdata);
    espconn_sent((struct espconn *) arg, "this is 8266", strlen("this is 8266"));  // 發(fā)送消息
}

/**
 @brief 發(fā)送數(shù)據(jù)到客戶端成功的回調(diào)函數(shù)
 @param arg 指向傳入?yún)?shù)的指針
 @return 無(wú)
*/
LOCAL void ICACHE_FLASH_ATTR
udp_server_send_cb(void *arg)
{
    os_printf("udpserver_send succeed!\n");
}

? 由 Leung 寫(xiě)于 2018 年 11 月 20 日

? 參考:ESP8266 Non-OS SDK API參考[7qq6]
    Esp8266學(xué)習(xí)之旅⑧ 你要找的8266作為UDP、TCP客戶端或服務(wù)端的角色通訊,都在這了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容