天天看點

[網絡篇]ESP8266-SDK教程(三)之TCP通信Server<->Client

納尼?昨天剛剛列印了個“Hello World!”,今天你就讓我學習TCP通信?有沒有搞錯~哈哈,相信很多讀者會很迷,其實學習這東西嘛,單單學一些比較簡單的,相信沒兩天就沒人看了,是以咱就在基礎篇和網絡篇穿插着去學習一下ESP8266,畢竟興趣才是最好的老師嘛!大家以後遇到問題了,來翻文章建議大家根據[XX篇]去快速定位該去哪一篇文章中去查找問題,當然具體會在哪一篇文章中有講,也不一定了,後面也會穿插着寫一點[項目篇][進階篇][閑扯篇],總的來說就是,本系列文章并沒有固定的路線,大家不如忍忍?當然這裡寫的文章也不一定是對的,大家在實際測試中如果遇到了問題,還請私信我,或者在Github上送出issue!

閑話少說,既然要學習TCP通信的知識,那麼我們得先要了解一下TCP到底是個什麼東西(玩意),正所謂孫子曾經曰過“知彼知己,百戰不殆”,那我們先補一些關于TCP的知識吧!

TCP基礎知識:

  • 是什麼?

TCP(Transmission Control Protocol

傳輸控制協定

)是一種面向連接配接的、可靠的、基于位元組流的

傳輸層

通信協定。(百度詞條搜到的~)

說白了就是一種傳輸協定,就像我們序列槽、232、485等一樣的通訊協定,隻不過TCP多用于網絡間的通訊,不過TCP是在IP層之上的,相信你肯定見過或者聽說過TCP/IP協定,沒錯,這哥倆是綁在一塊的,TCP是依賴IP協定的。

  • 通訊機制

TCP與我們常使用的序列槽等通訊協定有什麼不同呢?首先是兩台主機建立TCP連接配接機制是比較麻煩的,要經過三次握手協定,才能建立連接配接,過程用漢字描述如下:

    • 用戶端發送SYN(SEQ=x)封包給伺服器端,進入SYN_SEND狀态。
    • 伺服器端收到SYN封包,回應一個SYN (SEQ=y)ACK(ACK=x+1)封包,進入 SYN_RECV 狀态。
    • 用戶端收到伺服器端的SYN封包,回應一個ACK(ACK=y+1)封包,進入Established狀态。

看字比較麻煩,我們還是看圖吧!

[網絡篇]ESP8266-SDK教程(三)之TCP通信Server<->Client

既然有建立連接配接,那麼肯定也有斷開連接配接,但是沒想到斷開連接配接比建立連接配接還要麻煩,還有四次揮手協定,納尼?是的,斷開連接配接需要經過四次揮手協定,直接看圖吧:

[網絡篇]ESP8266-SDK教程(三)之TCP通信Server<->Client
  • 可靠性

上面說的通訊機制之是以這麼麻煩,主要還是為了傳輸的可靠性,因為TCP是在整個網絡中去傳輸,隻要你有IP位址,并且在廣域網中通路,那我們就可以建立TCP連接配接,說會悄悄話~是以,兩者之間的可靠性就顯得很重要了,用戶端與服務端之間的連接配接可靠性靠很多政策去實作的比如心跳包機制,這裡就不再詳細叙述了,有點跑題了,這是ESP8266學習筆記~

  • 資料包格式

TCP的資料包相對來說是比較麻煩的,确認資訊占據了整個資料包的很大一部分,設計的這麼複雜主要原因還是保證在整個網絡當中每包資料傳輸的正确性,這裡我們就看一下資料包到底有多麼複雜:

[網絡篇]ESP8266-SDK教程(三)之TCP通信Server<->Client
  • ESP8266作為TCP Client跟Server(PC)通信

了解了一點點基礎知識後,我們還是要落實到實踐上,下面我們正式開始,首先我們需要先看一下官方SDK程式設計指導手冊,還沒有?不知道去哪裡下?好嘞,直接戳下面卡片下載下傳好了~

官方SDK程式設計指導手冊

英文版戳這裡,如果你英語跟小編一樣好的話,建議看英文版,要不斷提升自己的英語~

官方SDK程式設計指導手冊英文版

咳咳咳,那我們先打開這個英文文檔,找到97頁的TCP/UDP接口,我們先看一下接口分了幾類:

[網絡篇]ESP8266-SDK教程(三)之TCP通信Server<->Client

 咳咳咳,不要懷疑,使用的PDF檢視軟體有自動翻譯動能,手動滑稽.jpg,可以看到一共分了4類,本篇文章我們先研究下通用 API和TCP API,關于UDP和mDNS的我們再後面的文章中再繼續學習,你可以先大體看一下,每個API的注釋都講的很明白,我們不妨直接copy點代碼直接跑一下?

 1 git clone [email protected]:imliubo/makingfunxyz-esp8266.git 2 3 #如果之前有clong過,可以直接 git pull,不會用?那肯定是沒好好看廖雪峰的git教程 

下載下傳完成後,打開ESP_IDE導入在makingfunxyz-esp8266-NONOS檔案中的6.TCP_UDP_Server_Client這個工程,然後先修改一點東西,這一步很重要,請不要忽略:

[網絡篇]ESP8266-SDK教程(三)之TCP通信Server<->Client

Wi-Fi名稱跟密碼相信你肯定知道,TCP_SERVER_IP應該要修改成什麼呢?就是你電腦的IP位址,這裡每個人的都不一樣,建議你先去檢視一下,方法看視訊~

 視訊還在找托管方~

可以看到我這裡是192.168.0.109,那我就将它修改成192.168.0.109,這三個地方都修改好了後,我們直接先編譯代碼,先跑一下看看!編譯好代碼後,下載下傳到ESP8266,這裡跟上篇文章中是一樣的,就不再詳述,下載下傳位址參考:

  • eagle.flash.bin-------->0x00000
  • eagle.irom0text.bin---->0x10000
  • esp_init_data_default_v08.bin --> 0x3FC000
  • blank.bin --> 0x3FE000

下載下傳完成後,我們先不要急着去看結果,因為我們還沒有開啟TCP Server,那我們該怎麼開啟TCP Server呢?我這裡用Python寫了一個很簡單的Server端程式,大家可以使用我這個,要是使用别的TCP調試助手,注意TCP_SERVER_PORT也要修改一下,運作我們的Server很簡單,但是你需要安裝一下Python,我這裡沒有打包成可執行檔案,先去安裝一下Python吧,我用的是Python3,建議大家也安裝Python3,因為Python2跟Python3有些文法不一樣,戳卡片下載下傳:

Python下載下傳安裝

下載下傳安裝完成後,開始搞起來,我們需要在6.TCP_UDP_Server_Client這個檔案夾下找到TCP_Server.py這個檔案夾,然後打開修改一下IP位址,将IP位址修改成上面我們檢視的本機位址,看圖:

[網絡篇]ESP8266-SDK教程(三)之TCP通信Server<->Client

最後我們還需要設定一下開放端口,這裡步驟有點多,就不再細述了,大家可以直接看這個百度知道,寫的很詳細:

百度知道開放端口

然後打開指令視窗,具體操作辦法,在此檔案下按住shift鍵,然後右鍵選擇打開powershell視窗(WIN7 應該是cmd視窗),然後:

 1 start cmd 2 #可以直接在powershell裡面執行,但是我還是比較喜歡cmd視窗,是以就~win7不用,因為打開的就是cmd視窗 3 #cmd視窗打開後,輸入以下指令 4 python TCP_Server.py 

視訊還在找托管方~

  • ESP8266作為TCP Server跟Client(PC)通信

 上面是ESP8266作為Client去跟Server通信,但是ESP8266不僅可以作為Client還可以作為Server等待Client去建立連接配接去通信,這裡我們修改幾個地方,就可以将ESP8266作為Server去跟Client通信了,我在源碼中已經都寫好了,這裡我們将同樣将ESP8266的6666作為PC去連接配接的端口号:

[網絡篇]ESP8266-SDK教程(三)之TCP通信Server<->Client

其中TCP_Client.py檔案中的IP位址需要在ESP8266上電列印後修改一下,我們将上面小節中的tcp_client_init()注釋掉,tcp_server_init()取消注釋,然後重新編譯代碼下載下傳就好了,PC上的Client程式跟Server程式運作一樣,這裡我們直接看一下視訊吧!

這裡代碼就不再解釋了,我寫的注釋還算全,大家一看就懂,主要代碼:

1 /****************************
  2  *   TCP CLIENT FUNCTIONS   *
  3  ****************************/
  4 
  5 /**********************************
  6  *  TCP CLIENT STATIC PROTOTYPES  *
  7  **********************************/
  8 static void tcp_client_sent_cb(void *arg);
  9 static void tcp_client_recv_cb(void *arg,char *pdata,unsigned short length);
 10 static void tcp_client_recon_cb(void *arg,sint8 error);
 11 static void tcp_client_discon_cb(void *arg);
 12 static void tcp_client_connect_cb(void *arg);
 13 
 14 /**********************************
 15  *   TCP CLIENT STATIC VARIABLES  *
 16  **********************************/
 17 os_timer_t tcp_client_send_data_timer;
 18 struct espconn tcp_client;
 19 uint8 i;
 20 
 21 /**********************************
 22  *   TCP CLIENT STATIC FUNCTIONS  *
 23  **********************************/
 24 
 25 /**
 26  * TCP Client資料發送回調函數
 27  */
 28 static void ICACHE_FLASH_ATTR
 29 tcp_client_sent_cb(void *arg){
 30     os_printf("tcp client send data successful\r\n");
 31 }
 32 
 33 /**
 34  * TCP Client資料接收回調函數,可以在這處理收到Server發來的資料
 35  */
 36 static void ICACHE_FLASH_ATTR
 37 tcp_client_recv_cb(void *arg,char *pdata,unsigned short len){
 38     os_printf("tcp client receive tcp server data\r\n");
 39     os_printf("length: %d \r\ndata: %s\r\n",len,pdata);
 40 
 41     //TO DO
 42 
 43     /**
 44      *process the receive data
 45      */
 46 }
 47 
 48 /**
 49  * TCP Client重連回調函數,可以在此函數裡做重連接配接處理
 50  */
 51 static void ICACHE_FLASH_ATTR
 52 tcp_client_recon_cb(void *arg,sint8 error){
 53     os_printf("tcp client connect tcp server error %d\r\n",error);
 54     os_timer_disarm(&tcp_client_send_data_timer);//取消定時發送資料定時器
 55 }
 56 
 57 /**
 58  * TCP Client斷開連接配接回調函數
 59  */
 60 static void ICACHE_FLASH_ATTR
 61 tcp_client_discon_cb(void *arg){
 62     os_printf("tcp client disconnect tcp server successful\r\n");
 63     os_timer_disarm(&tcp_client_send_data_timer);
 64 }
 65 
 66 /**
 67  * TCP Client連接配接成功回調函數
 68  */
 69 static void ICACHE_FLASH_ATTR
 70 tcp_client_connect_cb(void *arg){
 71     struct espconn *pespconn = arg;
 72 
 73     os_printf("tcp client connect tcp server successful\r\n");
 74     espconn_regist_recvcb(pespconn,tcp_client_recv_cb);//注冊接收資料回調函數
 75     espconn_regist_sentcb(pespconn,tcp_client_sent_cb);//注冊資料發送完成回調函數
 76     espconn_regist_disconcb(pespconn,tcp_client_discon_cb);//注冊斷開連接配接回調函數
 77 
 78     os_timer_disarm(&tcp_client_send_data_timer);
 79     os_timer_setfn(&tcp_client_send_data_timer, (os_timer_func_t *) tcp_client_send_data,NULL);//注冊Client定時發送資料回調函數
 80     os_timer_arm(&tcp_client_send_data_timer, 1000, true);//時間設定為1s
 81 }
 82 
 83 /**********************************
 84  *   TCP CLIENT GLOBAL FUNCTIONS  *
 85  **********************************/
 86 /**
 87  * TCP Client定時發送資料回調函數
 88  */
 89 void ICACHE_FLASH_ATTR
 90 tcp_client_send_data(void){
 91     char buf[256],length;
 92     os_printf("tcp client send data tcp server\r\n");
 93     length = os_sprintf(buf,(char *)"Hi this is ESP8266 client! message num %d",i);
 94     i++;
 95     espconn_sent(&tcp_client,buf,length);
 96 }
 97 
 98 /**
 99  * TCP Client初始化函數
100  * @remote_ip    要連接配接的TCP Server IP位址
101  * @local_ip     ESP8266 IP位址
102  * @remote_port  要連接配接的TCP Server端口号
103  */
104 void ICACHE_FLASH_ATTR
105 tcp_client_init(uint8 *remote_ip,struct ip_addr *local_ip, int remote_port){
106 
107     uint32 server_ip = ipaddr_addr(remote_ip);
108 
109     os_printf("tcp client connect to tcp server\r\n");
110     tcp_client.proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
111     tcp_client.type = ESPCONN_TCP;
112 
113     os_memcpy(tcp_client.proto.tcp->remote_ip,&server_ip,4);//設定要連接配接的Server IP位址
114     tcp_client.proto.tcp->remote_port = remote_port;//設定要連接配接的Server 端口号
115     os_memcpy(tcp_client.proto.tcp->local_ip,local_ip,4);//設定本地IP位址
116     tcp_client.proto.tcp->local_port = espconn_port();//設定本地端口号
117 
118     espconn_regist_connectcb(&tcp_client,tcp_client_connect_cb);//注冊連接配接成功回調函數
119     espconn_regist_reconcb(&tcp_client,tcp_client_recon_cb);//注冊斷連重新連接配接回調函數
120 
121     espconn_connect(&tcp_client);//Client連接配接Server
122 }
123 
124 
125 
126 /****************************
127  *   TCP SERVER FUNCTIONS   *
128  ****************************/
129 /**********************************
130  *  TCP SERVER STATIC PROTOTYPES  *
131  **********************************/
132 static void tcp_server_sent_cb(void *arg);
133 static void tcp_server_recv_cb(void *arg,char *pdata,unsigned short length);
134 static void tcp_server_recon_cb(void *arg,sint8 error);
135 static void tcp_server_discon_cb(void *arg);
136 static void tcp_server_listen_cb(void *arg);
137 
138 /**********************************
139  *   TCP SERVER STATIC VARIABLES  *
140  **********************************/
141 os_timer_t tcp_server_send_data_timer;
142 struct espconn tcp_server;
143 uint8 z;
144 
145 /**********************************
146  *   TCP server STATIC FUNCTIONS  *
147  **********************************/
148 
149 /**
150  * TCP Server資料發送回調函數
151  */
152 static void ICACHE_FLASH_ATTR
153 tcp_server_sent_cb(void *arg){
154     os_printf("tcp server send data successful\r\n");
155 
156 }
157 
158 /**
159  * TCP Server資料接收回調函數,可以在這處理收到Client發來的資料
160  */
161 static void ICACHE_FLASH_ATTR
162 tcp_server_recv_cb(void *arg,char *pdata,unsigned short len){
163     os_printf("tcp server receive tcp client data\r\n");
164     os_printf("length: %d \r\ndata: %s\r\n",len,pdata);
165 
166     //TO DO
167 
168     /**
169      *process the receive data
170      */
171 }
172 
173 /**
174  * TCP Server重連回調函數,可以在此函數裡做重連接配接處理
175  */
176 static void ICACHE_FLASH_ATTR
177 tcp_server_recon_cb(void *arg,sint8 error){
178     os_printf("tcp server connect tcp client error %d\r\n",error);
179     os_timer_disarm(&tcp_server_send_data_timer);
180 }
181 
182 /**
183  * TCP Server斷開連接配接回調函數
184  */
185 static void ICACHE_FLASH_ATTR
186 tcp_server_discon_cb(void *arg){
187     os_printf("tcp server disconnect tcp client successful\r\n");
188     os_timer_disarm(&tcp_server_send_data_timer);
189 }
190 
191 /**
192  * TCP Server監聽Client連接配接回調函數
193  */
194 static void ICACHE_FLASH_ATTR
195 tcp_server_listen_cb(void *arg){
196     struct espconn *pespconn = arg;
197 
198     os_printf("tcp server have tcp client connect\r\n");
199     espconn_regist_recvcb(pespconn,tcp_server_recv_cb);//注冊收到資料回調函數
200     espconn_regist_sentcb(pespconn,tcp_server_sent_cb);//注冊發送完資料回調函數
201     espconn_regist_disconcb(pespconn,tcp_server_discon_cb);//注冊斷開連接配接回調函數
202 
203     os_timer_disarm(&tcp_server_send_data_timer);
204     os_timer_setfn(&tcp_server_send_data_timer, (os_timer_func_t *) tcp_server_send_data,NULL);//注冊Server定時發送資料回調函數
205     os_timer_arm(&tcp_server_send_data_timer, 1000, true);//設定時間為1s
206 }
207 
208 /**********************************
209  *   TCP CLIENT GLOBAL FUNCTIONS  *
210  **********************************/
211 
212 /**
213  * TCP Server定時發送資料回調函數
214  */
215 void ICACHE_FLASH_ATTR
216 tcp_server_send_data(void){
217     char buf[256],length;
218     os_printf("tcp server send data tcp client\r\n");
219     length = os_sprintf(buf,(char *)"Hi this is ESP8266 server! message num %d",z);
220     z++;
221     espconn_send(&tcp_server,buf,length);
222 }
223 
224 /**
225  * TCP Server初始化函數
226  * @local_port 本地監聽端口号,與Client連接配接的端口号一緻
227  */
228 void ICACHE_FLASH_ATTR
229 tcp_server_init(uint16 local_port){
230 
231     os_printf("tcp server waiting tcp client connect!\r\n");
232     tcp_server.proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
233     tcp_server.type = ESPCONN_TCP;
234 
235     tcp_server.proto.tcp->local_port = local_port;//設定本地監聽的端口号,等待Client連接配接
236 
237     espconn_regist_connectcb(&tcp_server,tcp_server_listen_cb);//注冊Server監聽回調函數
238     espconn_regist_reconcb(&tcp_server,tcp_server_recon_cb);//注冊斷連重新連接配接回調函數
239 
240     espconn_accept(&tcp_server);//建立Server,開始監聽
241     espconn_regist_time(&tcp_server,360,0);//逾時斷開連接配接時間
242 }      

user_main.c中的主要代碼:

1 os_timer_t wifistate_checktimer;
 2 void ICACHE_FLASH_ATTR
 3 WifiStatus_Check(void){
 4     uint8 wifiStatus;
 5     wifiStatus = wifi_station_get_connect_status();
 6     if (wifiStatus == STATION_GOT_IP) {
 7         os_printf("WiFi connection is successful!\r\n");
 8         os_timer_disarm(&wifistate_checktimer);
 9         struct ip_info local_ip;
10         wifi_get_ip_info(STATION_IF,&local_ip);
11         tcp_client_init(TCP_SERVER_IP,&local_ip.ip,TCP_SERVER_PORT);//TCP Client初始化,Client與Server隻能二選一
12 //        tcp_server_init(TCP_LOCAL_PORT);//TCP Server初始化,Client與Server隻能二選一
13     }else{
14         os_printf("WiFi connection failed!\r\n");
15     }
16 }
17 
18 /**
19  * Wi-Fi連接配接回調函數
20  */
21 void ICACHE_FLASH_ATTR
22 wifiConnectCb(uint8_t status){
23 
24     os_timer_disarm(&wifistate_checktimer);
25     os_timer_setfn(&wifistate_checktimer, (os_timer_func_t *) WifiStatus_Check,NULL);
26     os_timer_arm(&wifistate_checktimer, 2000, true);
27 }
28 
29 void ICACHE_FLASH_ATTR
30 user_init(void)
31 {
32     os_printf("\nHello World! ZHIHU IAMLIUBO\n\n");
33 
34     wifi_set_opmode(0x01);
35 
36     WIFI_Connect(STA_SSID, STA_PASS, wifiConnectCb);//
37 }      

最後附上我的ESP8266倉庫,後面代碼會全部在此倉庫更新,歡迎小夥伴們Star~

makingfunxyz-esp8266

本系列文章在知乎同步更新,知乎搜尋專欄:

IAMLIUBO的神奇物聯網之旅

唯有愛與科技不可辜負。