勉強したことの備忘録です
前回記事にてm5stackでhttpのサーバーサンプルプログラムを動かしました。
シンプルにm5stackをサーバーとしてhttp通信を使ってブラウザ上でボタンクリックの情報を受けとると言ったものです。
ですが、m5stack側からクライアントから送られてくるデータをテキスト形式で見ることができるので勉強がてら色々調べてみました。
※自分の思い込みで書いていますので間違っている情報もあると思います。間違っていたらコメントしていただけると助かります。
サーバープログラムを理解する
m5stackにて、SimpleWiFiServerというWifiライブラリ内にあるサンプルプログラムでhttpの動きを確認しました。
このサンプルプログラムでは、クライアント側の送信文字列はシリアルで出力され、サーバー側の応答はプログラムでprintln()で送信しているためどちらの動きもわかりやすくなっています。
確認した事柄は以下になります。
- クライアントからの処理
-
iphoneのsafariから接続した場合 -
windows 10でgoogle chromeから接続した場合
-
- サーバー側の応答
-
httpの送信文字列形式
-
http通信については、基本的に以下のものを参考に読み解いていきます。
クライアント側の送信
teratermで受信した文字列が見れるのでブラウザがこのサーバーに対してどういう要求を投げているのか確認することができます。
iphoneのsafariから接続した場合
iphoneのsafariから接続した場合は、以下のようなリクエストメッセージが来ているようです。(長いので折り畳んでいます。)
リクエストメッセージ(iphone)
GET / HTTP/1.1
Host: 192.168.10.254
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.4 Mobile/15E148 Safari/604.1
Accept-Language: ja-jp
Accept-Encoding: gzip, deflate
Connection: keep-alive
Client Disconnected.
New Client.
GET /apple-touch-icon-120x120-precomposed.png HTTP/1.1
Host: 192.168.10.254
Accept: /
Accept-Language: ja-jp
Connection: keep-alive
Accept-Encoding: gzip, deflate
User-Agent: MobileSafari/604.1 CFNetwork/1121.2.2 Darwin/19.2.0
Client Disconnected.
New Client.
GET /apple-touch-icon-120x120.png HTTP/1.1
Host: 192.168.10.254
Accept: /
Accept-Language: ja-jp
Connection: keep-alive
Accept-Encoding: gzip, deflate
User-Agent: MobileSafari/604.1 CFNetwork/1121.2.2 Darwin/19.2.0
Client Disconnected.
New Client.
GET /apple-touch-icon-precomposed.png HTTP/1.1
Host: 192.168.10.254
Accept: /
Accept-Language: ja-jp
Connection: keep-alive
Accept-Encoding: gzip, deflate
User-Agent: MobileSafari/604.1 CFNetwork/1121.2.2 Darwin/19.2.0
Client Disconnected.
New Client.
GET /apple-touch-icon.png HTTP/1.1
Host: 192.168.10.254
Accept: /
Accept-Language: ja-jp
Connection: keep-alive
Accept-Encoding: gzip, deflate
User-Agent: MobileSafari/604.1 CFNetwork/1121.2.2 Darwin/19.2.0
Client Disconnected.
なぜか一回読み込んだだけで4回も読み込みが発生しています。
なぜなのだろうと調べてみたら、以下の記事で説明がありました。
iphoneのブラウザのマーク?を取得するようです。しかし、4回も読み込んでるのにあまり遅さを感じないあたり通信速度や処理速度の速さを実感しますね。こういうことを試さないと本当にわからないです。
応答で来ているメッセージは以下の8(or 7)のコマンド?が来ています。
(今回デバッグで受け取っているリクエストメッセージはteratermの設定で\nと\rを共に改行として読み込んでいるため、2行の空きができています。)
-
GET- 1行目はコマンドと、
http通信のバージョンを送信しています。GETメソッドはクライアント側からサーバーに対して後ろの引数に対する応答を要求するコマンドです。
- 1行目はコマンドと、
-
Host- 接続先の名前です。今回はIPアドレスで接続しているので、IPアドレスが表示されています。
-
Upgrade-Insecure-Requests-
httpsで接続するためのヘッダーです。最近のブラウザがhttpsでの通信を基本としているため、httpでの通信する場合でもこれを使って通信するようです。
-
-
Accept- 受け取るデータ形式を表しています。今回の場合は,すべてのタイプを宣言していますが、
q:=で優先じゅにをつけています。優先順位はthml=xml=xhtml>そのほかのデータ形式です。
- 受け取るデータ形式を表しています。今回の場合は,すべてのタイプを宣言していますが、
-
User-Agent- サーバーへ送っている端末のブラウザ情報、OS情報などなどを表示している部分です。これで接続している端末の種類やアプリケーションを判別できるそうです。
-
Accept-Language- サーバーへ送っている端末の使用している言語情報を表す部分です。おそらくOSから引っ張ってきているのかも?ここは補足的な用途として使う様子です。
-
Accept-Encoding- 送信するときに使う圧縮アルゴリズムです。文字エンコーディングとは違い、どの方法で圧縮するかを選択する部分です。
-
Connection- サーバーへの接続を続けるかどうかの選択部分です。
http 1.1はデフォルトでkeep-aliveだそうです。
- サーバーへの接続を続けるかどうかの選択部分です。
windows 10でgoogle chromeから接続した場合
windows 10でgoogle chromeから接続した場合は、以下のリクエストメッセージを送っている様子です。(こちらも折りたたんでいます)
応答メッセージ(chrome)
New Client.
GET / HTTP/1.1
Host: 192.168.10.254
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: ja,en-US;q=0.9,en;q=0.8
Client Disconnected.
New Client.
GET /favicon.ico HTTP/1.1
Host: 192.168.10.254
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36
Accept: image/webp,image/apng,image/,/*;q=0.8
Referer: http://192.168.10.254/
Accept-Encoding: gzip, deflate
Accept-Language: ja,en-US;q=0.9,en;q=0.8
Client Disconnected.
こちらも2回読み込みが発生しています。.icoを要求しているということはこちらも同じくブラウザのマーク?を確認しているのだと思います。
応答メッセージについては、基本的にiphoneのsafariとほぼ同じですが、順番や要求データ形式の種類、言語情報など違っている部分もあり、これがOSやブラウザの違いなんだとわかります。
サーバー側の応答
http通信はクライアント->サーバー->クライアントと、サーバーがクライアント側のメッセージを受信してやり取りをするプロトコルです。
UARTやSSPIでいうと、マスターはクライアント、スレーブ(子機)側がサーバーといった印象でしょうか。
サンプルプログラムでは、サーバー側の応答について記載があります。
httpの送信文字列形式
サンプルプログラムでのレスポンスを行っている部分は以下になります。(折りたたんでいます)
レスポンスメッセージ(`m5stack`)
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
if (c == '\n') {
Serial.println(outpurcount++); // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
// the content of the HTTP response follows the header:
client.print("Click <a href=\"/H\">here</a> to turn ON the LED.<br>");
client.print("Click <a href=\"/L\">here</a> to turn OFF the LED.<br>");
// The HTTP response ends with another blank line:
client.println();
// break out of the while loop:
break;
} else { // if you got a newline, then clear currentLine:
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
// Check to see if the client request was "GET /H" or "GET /L":
if (currentLine.endsWith(Response_H)) {
//LED display "GET /H"
tft.setCursor(0, LCDWIDTH / 2);
tft.println(Response_H);
}
if (currentLine.endsWith(Response_L)) {
//LED display "GET /L"
tft.setCursor(0, LCDWIDTH / 2);
tft.println(Response_L);
}
}
}
改行を二回確認したら、以下の文字列を送信しています。
HTTP/1.1 200 OK
Content-type:text/html
Click <a href=\"/H\">here</a> to turn ON the LED.<br>"
Click <a href=\"/L\">here</a> to turn OFF the LED.<br>
1行目は、レスポンスのステータスを表す行です。接続プロトコル、ステータスコード、ステータスコード名を表しています。
2行目は、HTTPヘッダーがおくられています。今回の場合はコンテンツタイプがhtmlですよって応答のみになっています。
3行目は区切りの改行です。どうやらこの改行は必須なようです。
4/5行目は、htmlを送っています。実際のhtmlファイルだとタグがもっとたくさんついていると思ったのですが、このサンプルプログラムでは<body>部分のみを送っている様子です。これだけでもブラウザでは表示されるので情報としては十分なようです。
このサンプルプログラムは/Hか/Lをクリックした場合GETしてくる動きになるのですが、/Hと/L以外を指定してもレスポンスメッセージは変わらないのでIPアドレスさえ合っていれば、何をGETしても同じhtmlを返します。
この辺りは、実際にはGETで予想外の部分は来た場合はステータスでかの有名な404 Not Foundを返してあげるように作る必要がありそうです。
最後に
web系の基本のhttp通信について動きを見ながら勉強してみました。
学びなおす前は、サーバーは高機能でないと処理が間に合わないのでサーバーは高性能であるべき!という固定概念が自分の中にあったのでどうしてもサーバーとクライアントの処理順番が、サーバー->クライアントという流れじゃないのか?と長年悩み続けていました。
(制御関係で務めているのでどうしても性能がいいものが頭、マスターになるという考えがこびりついていました)
ですが、今回'm5stack'を使ってサーバーの動きを学びなおしたら頭の中がすっきりでき、かなりいい勉強になりました。
次回は、'm5stack'に対してhttp通信するプログラムを作って文字列送受信を行ってみたいと思います。