ESP8266例程解析⑥TCP server and client
阿新 • • 發佈:2018-12-30
一、簡介
- 在網路傳輸應用層中,通常使用TCP和UDP這三種協議實現資料的傳輸。在傳輸過程中,需要雙向的通訊連線實現資料的互動。因此,在這雙向鏈路的一端稱之為socket,一個socket有一個IP地址和埠號。
- 目前流程的網路程式設計模型是客戶端/服務端(C/S)結構。
- TCP是Transmission Control Protocol的簡稱,是一個可靠的面向連結的協議,一旦成功建立連線,保證了資料的完整性傳到目的地!
- TCP好像打電話,雙方要通話,首先,要確定對方不是開機(網路可以到達),然後要確定是不是沒有訊號(),然後還需要對方接聽(通訊連結)。
- TCP要等待對面的確認,方可建立連線。也就是人們所說的“三次握手”,但是執行效率較慢,但是安全可靠,畢竟人家是保證了資料的完整性!
二、TCP server and client
TCP server(手機為TCP客戶端,8266為TCP服務端)
提示:
ESP8266充當服務端時候,要自身開啟WIFI熱點,等待裝置接入,好比一個閘道器。手機接入8266熱點WiFi時候,注意串列埠發出來的IP地址,此地址是手機要連線的服務端的地址。注意好串列埠號即可通訊,此通訊100%保證到達目的地。
TCP server流程分析 (即程式碼分析)
- 8266分割槽初始化
- 串列埠初始化
- WiFi設定模式(0X02 soft-AP 模式)
- esp8266 soft-AP 初始化並使能
- TCP準備(給本地埠8266,分配空間,設定協議,註冊TCP連線成功,並且回撥函式(接收 傳送 斷開) 註冊 TCP連線發生異常斷開時的回撥函式(可以在回撥函式中進行重連,並列印錯誤程式碼) 建立 TCP server,建立偵聽 設定超時斷開時間)
程式碼
#include "driver/uart.h" //串列埠0需要的標頭檔案 #include "osapi.h" //串列埠1需要的標頭檔案 #include "user_interface.h" //WIFI連線需要的標頭檔案 #include "espconn.h"//TCP連線需要的標頭檔案 #include "mem.h" //系統操作需要的標頭檔案 #include "gpio.h" struct espconn user_tcp_espconn; void ICACHE_FLASH_ATTR server_recv(void *arg, char *pdata, unsigned short len) { os_printf("收到PC發來的資料:%s", pdata); espconn_sent((struct espconn *) arg, "收到!", strlen("收到!")); } void ICACHE_FLASH_ATTR server_sent(void *arg) { os_printf("傳送成功!"); } void ICACHE_FLASH_ATTR server_discon(void *arg) { os_printf("連線已經斷開!"); } void ICACHE_FLASH_ATTR server_listen(void *arg) //註冊 TCP 連線成功建立後的回撥函式 { struct espconn *pespconn = arg; espconn_regist_recvcb(pespconn, server_recv); //接收 espconn_regist_sentcb(pespconn, server_sent); //傳送 espconn_regist_disconcb(pespconn, server_discon); //斷開 } void ICACHE_FLASH_ATTR server_recon(void *arg, sint8 err) //註冊 TCP 連線發生異常斷開時的回撥函式,可以在回撥函式中進行重連 { os_printf("連線錯誤,錯誤程式碼為:%d\r\n", err); //%d,用來輸出十進位制整數 } void Inter213_InitTCP(uint32_t Local_port) { user_tcp_espconn.proto.tcp = (esp_tcp *) os_zalloc(sizeof(esp_tcp)); //分配空間 user_tcp_espconn.type = ESPCONN_TCP; //設定型別為TCP協議 user_tcp_espconn.proto.tcp->local_port = Local_port; //本地埠 espconn_regist_connectcb(&user_tcp_espconn, server_listen); //註冊 TCP 連線成功建立後的回撥函式 espconn_regist_reconcb(&user_tcp_espconn, server_recon); //註冊 TCP 連線發生異常斷開時的回撥函式,可以在回撥函式中進行重連 espconn_accept(&user_tcp_espconn); //建立 TCP server,建立偵聽 espconn_regist_time(&user_tcp_espconn, 180, 0); //設定超時斷開時間 單位:秒,最大值:7200 秒 } void WIFI_Init() { struct softap_config apConfig; wifi_set_opmode(0x02); //設定為AP模式,並儲存到 flash apConfig.ssid_len = 8; //設定ssid長度 os_strcpy(apConfig.ssid, "xiaofang"); //設定ssid名字 os_strcpy(apConfig.password, "12345678"); //設定密碼 apConfig.authmode = 3; //設定加密模式 apConfig.beacon_interval = 100; //信標間隔時槽100 ~ 60000 ms apConfig.channel = 1; //通道號1 ~ 13 apConfig.max_connection = 4; //最大連線數 apConfig.ssid_hidden = 0; //隱藏SSID wifi_softap_set_config(&apConfig); //設定 WiFi soft-AP 介面配置,並儲存到 flash } void tcp_service_init() //初始化 { WIFI_Init(); Inter213_InitTCP(8266); //本地埠 }
TCP client(8266為TCP客戶端,手機為TCP服務端)
提示:
ESP8266充當TCP客戶端,必須要知道服務端的IP地址。所以我先在手機開啟TCP服務端的時候,拿到手機的IP地址,之後把IP地址拷貝到程式碼裡,寫成死的!這時候才通訊。
程式碼的實現過程,定時器開啟先連線至路由器,待成功連線路由器,則關閉定時器,開啟TCP客戶端連線。
TCP client流程分析 (即程式碼分析)
-
8266分割槽初始化
-
設定串列埠
-
WiFi設定模式(station模式)
-
esp8266 station初始化並使能
-
連線路由器
-
回撥定時器函式Check_WifiState
查詢esp8266 station介面連線AP的狀態
設定目標(手機)IP地址
查詢 WiFi模組的 IP 地址
連線到目標伺服器的6000埠並回調my_station_init函式(分配空間,設定協議,設定本地埠,目標埠等,初始化TCP(相當於註冊TCP連線) 成功就能(傳送 接收 斷開) 不成功就(列印錯誤程式碼 嘗試重連) 最後啟用連線)
程式碼
#include "driver/uart.h" //串列埠0需要的標頭檔案
#include "osapi.h" //串列埠1需要的標頭檔案
#include "user_interface.h" //WIFI連線需要的標頭檔案
#include "espconn.h"//TCP連線需要的標頭檔案
#include "mem.h" //系統操作需要的標頭檔案
#include "gpio.h"
os_timer_t checkTimer_wifistate;
struct espconn user_tcp_conn;
void ICACHE_FLASH_ATTR user_tcp_sent_cb(void *arg) //傳送
{
os_printf("傳送資料成功!");
}
void ICACHE_FLASH_ATTR user_tcp_discon_cb(void *arg) //斷開
{
os_printf("斷開連線成功!");
}
void ICACHE_FLASH_ATTR user_tcp_recv_cb(void *arg, //接收
char *pdata, unsigned short len) {
os_printf("收到資料:%s\r\n", pdata);
espconn_sent((struct espconn *) arg, "0", strlen("0"));
}
void ICACHE_FLASH_ATTR user_tcp_recon_cb(void *arg, sint8 err) //註冊 TCP 連線發生異常斷開時的回撥函式,可以在回撥函式中進行重連
{
os_printf("連線錯誤,錯誤程式碼為%d\r\n", err);
espconn_connect((struct espconn *) arg);
}
void ICACHE_FLASH_ATTR user_tcp_connect_cb(void *arg) //註冊 TCP 連線成功建立後的回撥函式
{
struct espconn *pespconn = arg;
espconn_regist_recvcb(pespconn, user_tcp_recv_cb); //接收
espconn_regist_sentcb(pespconn, user_tcp_sent_cb); //傳送
espconn_regist_disconcb(pespconn, user_tcp_discon_cb); //斷開
espconn_sent(pespconn, "8226", strlen("8226"));
}
void ICACHE_FLASH_ATTR my_station_init(struct ip_addr *remote_ip,
struct ip_addr *local_ip, int remote_port)
{
user_tcp_conn.proto.tcp = (esp_tcp *) os_zalloc(sizeof(esp_tcp)); //分配空間
user_tcp_conn.type = ESPCONN_TCP; //設定型別為TCP協議
os_memcpy(user_tcp_conn.proto.tcp->local_ip, local_ip, 4);
os_memcpy(user_tcp_conn.proto.tcp->remote_ip, remote_ip, 4);
user_tcp_conn.proto.tcp->local_port = espconn_port(); //本地埠
user_tcp_conn.proto.tcp->remote_port = remote_port; //目標埠
//註冊連線成功回撥函式和重新連接回調函式
espconn_regist_connectcb(&user_tcp_conn, user_tcp_connect_cb);//註冊 TCP 連線成功建立後的回撥函式
espconn_regist_reconcb(&user_tcp_conn, user_tcp_recon_cb);//註冊 TCP 連線發生異常斷開時的回撥函式,可以在回撥函式中進行重連
//啟用連線
espconn_connect(&user_tcp_conn);
}
void Check_WifiState(void)
{
uint8 getState;
getState = wifi_station_get_connect_status();
//查詢 ESP8266 WiFi station 介面連線 AP 的狀態
if (getState == STATION_GOT_IP)
{
os_printf("WIFI連線成功!\r\n");
os_timer_disarm(&checkTimer_wifistate);
struct ip_info info;
const char remote_ip[4] = { 192, 168, 43, 1 };//目標IP地址,必須要先從手機獲取,否則連線失敗.
wifi_get_ip_info(STATION_IF, &info); //查詢 WiFi模組的 IP 地址
my_station_init((struct ip_addr *) remote_ip, &info.ip, 6000);//連線到目標伺服器的6000埠
}
}
void tcp_client_init() //初始化
{
wifi_set_opmode(0x01); //設定為STATION模式
struct station_config stationConf;
os_strcpy(stationConf.ssid, "xiaofang"); //改成你自己的 路由器的使用者名稱
os_strcpy(stationConf.password, "12345678"); //改成你自己的 路由器的密碼
wifi_station_set_config(&stationConf); //設定WiFi station介面配置,並儲存到 flash
wifi_station_connect(); //連線路由器
os_timer_disarm(&checkTimer_wifistate); //取消定時器定時
os_timer_setfn(&checkTimer_wifistate, (os_timer_func_t *) Check_WifiState,
NULL); //設定定時器回撥函式
os_timer_arm(&checkTimer_wifistate, 500, 1); //啟動定時器,單位:毫秒
}