2020年5月19日 星期二

六、3 存取點(AP)模式內建網路伺服器(WebServer)設計—基本概念篇



在網路通訊的世界裡,最基本就是兩個角色,一個是提出請求的客戶端(Client),另一邊則是回應需求的伺服器(Server)端,至於其他的網路設備,如路由器(Router)、集線器(Hub)、通訊閘道(Gateway)、中繼器(Repeater)...等,都只是為了要讓這兩種裝置能順利地連接在一起,達成網路通訊的目的而已。ESP8266這個晶片模組可分別執行客戶端(Client)與伺服器(Server)端這兩個腳色可以連上網際網路亦可自行組成一個區域性的無線WiFi網路如果要在這個無線WiFi區域網路擔任伺服器(Server)個腳色時就必須扮演無線WiFi網路中的存取點(AP:Access Point)功能了

所謂的網路伺服器(WebServer),是指一種連接在網路上的設備,這種設備主要用於儲存和提供數據/文件,而客戶端(Client)這種裝置可以請求這些文件或其他數據,網路伺服器將此請求(request)解譯之後,再將正確的數據/文件發送回客戶端一般來說這兩種設備之間使用所謂的HTTP通訊協定來進行需求與資料的雙向傳輸所謂的HTTP(Hypertext Transfer potocal)或超文本傳輸協定是用於與(Web)伺服器通信的基於文本的協定HTTP請求方法有多種,但由於ESP8266只是一顆內部資源有限的單晶片微電腦而已所以我們只會介紹兩種最常用的方法也就是:GET和POST。

HTTP通訊協定中GET這種請求用於從伺服器檢索數據/文件,例如瀏覽器連接網頁它只是單純從伺服器獲取數據/文件,不會要求或試著更改伺服器內部的資料,因此沒有其他的副作用。當我們使用瀏覽器中打開某一個網頁時,它將獲取URL並將其放入HTTP GET請求中這只是純文本(plain text)資料然後它將使用TCP通訊協定將請求發送到正確的伺服器伺服器讀取請求後,檢查URL,並將該URL的正確HTTP響應發送回瀏覽器。

六、3-1 GET請求的解析

在HTTP 的GET請求中最重要的部分是請求行(request Line)主機檔頭(host header)接下來我們以一個範例來說明:

請啟動你電腦的瀏覽器(在此使用的是Google Chrome其他的瀏覽器也可以當然你的電腦必須已經連上網際網路)接著在URL列輸入以下鏈接網址:

https//www.example.com


這時您的瀏覽器將發出以下HTTP請求:

圖六、4  瀏覽器連接http://www.example.com網站後的內容


在【圖六、4】中標記1是我們要前往網頁的URL網址,這是一個網路上有名的樣本網站,的內容就是左邊標記2簡單的文字說明;不過要看到右邊相關的資訊,請在連上這個網站之後按下電腦鍵盤的[F12]功能鍵,然後依序點擊的標記3重新載入按鈕再次的載入網頁,接著是標記4的[ Network ]標籤、標記5的網站名稱[www.example.com]、標記7的解析(parsed)選項後,便可以完整的看到標記6與8的內容了。

所謂的請求行主要包括兩個部份,分別是URI(Uniform Resource Identifier)也就是請求的內容,再來就是請求的方式「GET」;至於主機檔頭,則是要連接的主機伺服器(host server)的網域名稱。在標記6的區域中我們可看到前兩行的內容為:


Request URL:http://www.example.com/

Request Method:GET


第二行的內容就是請求的方式「GET」,而第一行的內容其實是由兩個部分所構成,分別是代表主機檔頭的主機伺服器(host server)網域名稱:[http://www.example.com],及請求的內容:「’/’」,這個內容也就是一般網站主網頁的意思。

接下來我們就以一個範例程式來看看,當我們用手機或平板電腦的瀏覽器去連接ESP8266的AP存取點伺服器網站時,會送出那些資訊出來:


範例程式功能與動作說明:

1、以ESP8266建立一無線WiFi存取點,SSID名稱為『ESP_softAP01』,不 使用密碼。

2、此AP存取點內建伺服器的IP位址為:[ 192.168.0.100 ]

3、當有客戶端裝置連線上ESP8266時,讀取客戶端(手機、平板或筆電上的瀏覽器)所傳送來的請求訊息,並顯示在電腦上的Arduino IDE序列監控視窗中。


程式說明與列表

程式的2~10行功能與前面範例相同,但是在第12、13行各宣告一個網路伺服器物件與使用的埠號,以及一個客戶端的物件變數:


WiFiServer  server(80); // 宣告網路伺服器物件與使用的埠號

WiFiClient  client; // 宣告一個客戶端的物件變數


然後在第28行程式初始化的部分(setup())啟動伺服器的功能:


server.begin(); // 啟動伺服器功能


在主迴圈(loop())開始的第35行程式呼叫「server.available()」這個方法函式去監測是否有客戶端裝置連線到本機的伺服器,若無則回頭繼續執行主迴圈:


  client=server.available(); // 測試是否有客戶端連接本機伺服器


如果有客戶端裝置連線到伺服器,則40~47行的while()迴圈會把客戶端裝置傳送來的訊息整合打包後,儲存在「requests」這個字串變數上;一般的瀏覽器在結束傳送時,會送出一個空白行,也就是一整行只有一個代表換行的字元”\n”,因此當ESP8266接收到這樣的字串時(45行),便會以”break”的整令結束while()迴圈;至於剩下的程式,會把完整的客戶端請求訊息顯示在電腦上的Arduino IDE序列監控視窗中。


以下是這個範例程式的全文:


 

//   soft AP 範例三: 分析及顯示瀏覽器傳送的資料內容

 

#include  <ESP8266WiFi.h> // 引入ESP8266專用的WiFi函式庫

 

String softSsid = "ESP_softAP01"; // 設定本機伺服器的SSID名稱

 

const char* softPassword = "12345678"; // 設定本機AP存取點的密碼為:”12345678”

IPAddress local_IP(192,168,0,100); // 設定新的伺服器IP位址

IPAddress gateway(192,168,0,1); // 設定新的伺服器閘道位址

IPAddress subnet(255,255,255,0); // 設定新的伺服器網路遮罩位址

 

WiFiServer  server(80); // 宣告網路伺服器物件與使用的埠號

WiFiClient  client; // 宣告一個客戶端的物件變數

 

void setup() {

   Serial.begin(115200); // 

  Serial.println();

  Serial.print("Setting soft-AP with configuratin... ");

  WiFi.softAPConfig(local_IP,gateway,subnet); // 設定新的本地IP位址

  boolean result=WiFi.softAP(softSsid);   // 以"ESP_softAP01"作為SSID的名稱

  if(result==true)

  {  // 初始化設定AP存取點成功

    Serial.println("soft-AP ready!");

    Serial.print("softAP = "); // 在Arduino IDE串列監控視窗中顯示目前

    Serial.println(softSsid);       // 顯示SSID名稱

    Serial.print("softIP = "); // 在Arduino IDE串列監控視窗中顯示目前使用的

    Serial.println(WiFi.softAPIP()); // 本地IP位址在此為[ 192.168.0.100 ]

server.begin(); // 啟動伺服器功能

  }  

  else

    Serial.println("soft-AP failed");

}

 

void loop() {

  client=server.available(); // 測試是否有客戶端連接本機伺服器

  if(!client) 

    return; // 若無則返回起點

// 測試到有客戶端裝置連線

  String request="",requests="";

  while(client.connected()) // 測試客戶端裝置連線是否還在連線狀態

  {

      request=client.readStringUntil('\r'); // 讀取客戶端送來的單行請求訊息

//      Serial.println(request);

      requests+=request; // 將讀取到的訊息合併

      if(request=="\n") // 測試是否接收到代表整個請求結束的換行字元

        break; // 如果是則結束while()迴圈

  }

  Serial.println("Request end!"); // 在電腦的串列監控視窗顯示客戶端請求結束訊息

  Serial.println("");

  Serial.println(requests);  // 在序列監控視窗顯示客戶端(client)所送來的指令碼資料

}



程式名稱softAP3_webServer1.ino


執行結果:

在程式執行之後,我們可在Arduino IDE串列監控視窗中看到【圖六、5】的內容,其中標記1的部分是在程式初始化時所顯示的資訊,包括目前設定的SSID名稱(在此為”ESP_softAP01”),和自行設定的本地IP位址(192.168.0.100),在本例中為了方便連線測試,所以沒有加上連線密碼。

圖六、5 程式啟動初始化後Arduino IDE串列監控視窗之內容


接著按照前面【圖六、2】的方式開啟手機/平板/筆電的WiFi功能,與”ESP_softAP01”的AP存取點連線後,再依【圖六、6】所示啟動瀏覽器,並在網址輸入欄中(標記1)輸入[ 192.168.0.100 ]這個IP位址並前往;由於這個範例程式只是單純的抓取瀏覽器傳送過來的訊息,然後顯示在Arduino IDE串列監控視窗內,並沒有回傳任何請求,所以會在手機瀏覽器的畫面中看到標記2的回應內容,也就是:「 192.168.0.100 未傳送任何資料」!


圖六、6 手機啟動與瀏覽器連接ESP8266的AP存取點伺服器


圖六、7 瀏覽器連線後傳送資料內容


可是當我們再回頭去看Arduino IDE串列監控視窗,會看到如【圖六、7】的新的內容,也就是多了標記2的部分,而這也就是由手機瀏覽器實際傳送過來的資訊,這當中包含了許多訊息,其中標記3的第一行:


GET  /  HTTP/1.1


就是前面提到的請求行(request Line),其中包括請求的方式「GET」,及請求的內容:「’/’」,而後面的「HTTP/1.1」則是代表目前所使用的HTTP通訊協定版本為1.1版。至於標記3的第二行:

Host:192.168.0.100


則是所謂的主機檔頭(host header),由於我們的程式沒有提供DNS的功能,所以直接以數字IP位址的方式表示目標伺服器的網域名稱

如果我們在手機瀏覽器的網址輸入欄輸入URL:「 192.168.0.100/test 」這樣的內容送出之後,可看到【圖六、8】的內容,其中紅色框線中的部分就是此次客戶端送來請求行(request Line),跟前面的差別就是請求的內容由「’/’」 變成「’/test’」,,也就是說只要在網域名稱或是網路IP後面加上一參數,便可將這些訊息當成請求的一部分傳送給目的伺服器,以作為一些延伸應用;我們在後面的範例中會利用這個特性讓使用者可以用手機跟ESP8266做一些互動,例如遙控一些輸出點,或是讀取一些測量資料等。

圖六、8 瀏覽器網址列輸入”192.168.0.100/test”後傳送資料內容


六、3-2 以文本(plain)的方式回應GET請求

前面的範例是一個已讀不回的伺服器程式這樣的伺服器應該沒有人會喜歡接下來我們就把它改成當手機瀏覽器連線到它時會回傳


"ESP8266 AP模式--網路伺服器1"


這樣一段訊息的程式好讓使用者知道瀏覽器已經連接上伺服器網頁了

範例程式功能與動作說明

1、以ESP8266建立一無線WiFi AP存取點,此AP存取點SSID名稱為『ESP_softAP01』,而且不使用密碼。

2、此AP存取點內建伺服器的IP位址為:[ 192.168.0.100 ]

3、當有客戶端裝置連線上ESP8266時,會以文本(plain)的格式回應『ESP8266 AP模式--網路伺服器1』這樣的訊息給客戶端(手機、平板或筆電上的瀏覽器),此時客戶端的瀏覽器便可看見這段文字。


程式說明與列表

這個範例程式和前面不同的地方首先是新增加了一個函式庫(第4行):


#include  <ESP8266WebServer.h>


這個函式庫是專門讓ESP8266在建構網頁伺服器之用,其中有不少很有用的函式,在後面會陸續地介紹大家。

此外在初始化設定(也就是setup())部分的第30行:


   server.on("/",handleRoot); // 定義處理客戶首頁連結請求的事件對應函式


在此我們用server.on()這個內建於” <ESP8266WebServer.h>”的函式,定義了當客戶端的瀏覽器如果連線並且要開啟伺服器的首頁(即請求的內容為「’/’」)時,對應的處理函式,也就是「handleRoot」這個自訂副程式,其實這個自訂副程式的實體內容(41~43行)很簡單,只有一行程式碼而已:


void handleRoot(){

  server.send(200,"text/plain","ESP8266 AP模式--網路伺服器1"); // 

}


其中使用了server.send()這個內建於” <ESP8266WebServer.h>”的函式,向發送請求的客戶端回應處理後的結果訊息;這些訊息包含了三個部分,第一個是HTTP的處理狀態代碼「200」,表示請求成功一切正常;再來是回傳的訊息內容格式「"text/plain"」,代表訊息內容是單純的文字,所以當客戶端的瀏覽器收到回應訊息後,會直接把它顯示出來,不做任何的解譯或處理;最後一個部分就是回應的訊息本身,在此就是「"ESP8266 AP模式--網路伺服器1"」這段文字。

在主體迴圈(loop())部分,我們將上一個範例程式抓取客戶端瀏覽器傳送訊息的部分拿掉,取而代之的是專用以監聽及處理客戶端連線請求的內建函式:


  server.handleClient(); // 呼叫客戶端連線處理函式


這個函式一樣也是內建在” <ESP8266WebServer.h>”這個函式庫中呼叫時不需要任何的引數客戶端有任何連線請求出現時會依前面setup()部分已呼叫過server.on()這個函式設定的請求內容及對應的自訂函式去回應

HTTP狀態代碼

伺服器會使用HTTP狀態代碼回答所有請求這個狀態代碼是一個3位數字,告訴客戶端請求是成功還是出了什麼問題。後面的表格包含一些最重要和最有用的狀態碼,以及它們所代表的意義。

 

狀態代碼

內容含義

200

好的:請求成功了

303

請參閱其他:例如,在POST請求之後,用於重定向到不同的URI

400

錯誤請求:服務器無法理解請求,因為語法不正確

401

未經授權:需要用戶身份驗證

403

禁止:服務器拒絕執行請求,授權也無濟於事

404

未找到:找不到請求的URI

500

內部服務器錯誤:服務器遇到意外情況,無法滿足請求


以下便是此範例程式的全文:

 

//   soft AP 範例四 : 以純文字(plain)的方式回應客戶端的首頁連線請求

 

#include  <ESP8266WiFi.h> // 引入ESP8266專用的WiFi函式庫

#include  <ESP8266WebServer.h> // 新增的ES8266網頁伺服器函式庫

 

String softSsid = "ESP_softAP01"; // 設定AP存取點的SSID名稱

const char* softPassword = "12345678"; // 設定AP存取點的密碼值

 

IPAddress local_IP(192,168,0,100); // 設定新的伺服器IP位址

IPAddress gateway(192,168,0,1); // 設定新的伺服器閘道位址

IPAddress subnet(255,255,255,0); // 設定新的伺服器網路遮罩位址

 

ESP8266WebServer  server(80); // 設定伺服器使用的通信埠號碼為網頁專用的80

 

WiFiClient  client;

 

void setup() {

  // 

  Serial.begin(115200); // 初始化串列通訊埠

  Serial.println();

  Serial.print("Setting soft-AP with configuratin... ");

  WiFi.softAPConfig(local_IP,gateway,subnet); // 設定新的本的IP位址

  boolean result=WiFi.softAP(softSsid);

  if(result==true)

  {  // 初始化設定AP存取點成功

    Serial.println("soft-AP ready!");

    Serial.print("softAP = "); // 在Arduino IDE串列監控視窗中顯示目前

    Serial.println(softSsid);       // 顯示SSID名稱

    Serial.print("softIP = "); // 在Arduino IDE串列監控視窗中顯示目前使用的

    Serial.println(WiFi.softAPIP()); // 本地IP位址在此為[ 192.168.0.100 ]

    server.on("/",handleRoot); // 定義處理客戶首頁連結請求的事件對應函式

    server.begin(); // 啟動伺服器功能

  }  

  else

    Serial.println("soft-AP failed");

}

 

void loop() {

  server.handleClient(); // 呼叫客戶端連線處理函式

}

// 處理首頁’/’請求的自訂函式

void handleRoot(){

  server.send(200,"text/plain","ESP8266 AP模式--網路伺服器1"); // 

}



程式名稱softAP3_webServer2.ino


◎ 執行結果:

圖六、9 手機啟動瀏覽器連接ESP8266伺服器成功畫面


在程式燒錄成功之後,當我們再次啟動手機瀏覽器連接上ESP8266的內建伺服器成功時,便可看到【圖六、9】的畫面,標記2的地方便是我要顯示的訊息;在ESP8266所構成的伺服器中,也可以處理中文的訊息,就跟一般的網頁一樣。不過一般的使用者在連上這個網頁時,可能看不到像【圖六、9】那麼好看的畫面,必須將手機的螢幕畫面拉大之後才能看到清楚的文字內容;這是因為我們的ESP8266網頁伺服器回傳的是最基本的文本(plain)格式的訊息,一般來說瀏覽器會以預設最小的字形來顯示,而且是放在螢幕的左上角,所以使用者必須費一番力氣才能看得到。

這種以基本的文本(plain)的格式回傳訊息的原因,大部分是因為使用者會以幕後處理的方式來解析與處理這些訊息,所以美不美觀並不是重點!如果想讓我們的網頁畫面好看一點,那就必須使用標準的網頁設計方式來設計我們的程式,也就使是用html語法,而這也就是下一章要介紹的內容。


沒有留言:

張貼留言