納尼?昨天剛剛列印了個“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狀态。
看字比較麻煩,我們還是看圖吧!

既然有建立連接配接,那麼肯定也有斷開連接配接,但是沒想到斷開連接配接比建立連接配接還要麻煩,還有四次揮手協定,納尼?是的,斷開連接配接需要經過四次揮手協定,直接看圖吧:
- 可靠性
上面說的通訊機制之是以這麼麻煩,主要還是為了傳輸的可靠性,因為TCP是在整個網絡中去傳輸,隻要你有IP位址,并且在廣域網中通路,那我們就可以建立TCP連接配接,說會悄悄話~是以,兩者之間的可靠性就顯得很重要了,用戶端與服務端之間的連接配接可靠性靠很多政策去實作的比如心跳包機制,這裡就不再詳細叙述了,有點跑題了,這是ESP8266學習筆記~
- 資料包格式
TCP的資料包相對來說是比較麻煩的,确認資訊占據了整個資料包的很大一部分,設計的這麼複雜主要原因還是保證在整個網絡當中每包資料傳輸的正确性,這裡我們就看一下資料包到底有多麼複雜:
- ESP8266作為TCP Client跟Server(PC)通信
了解了一點點基礎知識後,我們還是要落實到實踐上,下面我們正式開始,首先我們需要先看一下官方SDK程式設計指導手冊,還沒有?不知道去哪裡下?好嘞,直接戳下面卡片下載下傳好了~
官方SDK程式設計指導手冊英文版戳這裡,如果你英語跟小編一樣好的話,建議看英文版,要不斷提升自己的英語~
官方SDK程式設計指導手冊英文版咳咳咳,那我們先打開這個英文文檔,找到97頁的TCP/UDP接口,我們先看一下接口分了幾類:
咳咳咳,不要懷疑,使用的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這個工程,然後先修改一點東西,這一步很重要,請不要忽略:
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位址修改成上面我們檢視的本機位址,看圖:
最後我們還需要設定一下開放端口,這裡步驟有點多,就不再細述了,大家可以直接看這個百度知道,寫的很詳細:
百度知道開放端口然後打開指令視窗,具體操作辦法,在此檔案下按住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去連接配接的端口号:
其中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的神奇物聯網之旅唯有愛與科技不可辜負。