1. 程式人生 > >ESP8266例程解析⑥TCP server and client

ESP8266例程解析⑥TCP server and client

一、簡介

  1. 在網路傳輸應用層中,通常使用TCP和UDP這三種協議實現資料的傳輸。在傳輸過程中,需要雙向的通訊連線實現資料的互動。因此,在這雙向鏈路的一端稱之為socket,一個socket有一個IP地址和埠號。
  2. 目前流程的網路程式設計模型是客戶端/服務端(C/S)結構。
  3. TCP是Transmission Control Protocol的簡稱,是一個可靠的面向連結的協議,一旦成功建立連線,保證了資料的完整性傳到目的地!
  4. TCP好像打電話,雙方要通話,首先,要確定對方不是開機(網路可以到達),然後要確定是不是沒有訊號(),然後還需要對方接聽(通訊連結)。
  5. TCP要等待對面的確認,方可建立連線。也就是人們所說的“三次握手”,但是執行效率較慢,但是安全可靠,畢竟人家是保證了資料的完整性!

二、TCP server and client

TCP server(手機為TCP客戶端,8266為TCP服務端)

提示:
ESP8266充當服務端時候,要自身開啟WIFI熱點,等待裝置接入,好比一個閘道器。手機接入8266熱點WiFi時候,注意串列埠發出來的IP地址,此地址是手機要連線的服務端的地址。注意好串列埠號即可通訊,此通訊100%保證到達目的地。

TCP server流程分析 (即程式碼分析)

  1. 8266分割槽初始化
  2. 串列埠初始化
  3. WiFi設定模式(0X02 soft-AP 模式)
  4. esp8266 soft-AP 初始化並使能
  5. 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流程分析 (即程式碼分析)

  1. 8266分割槽初始化

  2. 設定串列埠

  3. WiFi設定模式(station模式)

  4. esp8266 station初始化並使能

  5. 連線路由器

  6. 回撥定時器函式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);	//啟動定時器,單位:毫秒
}