バックナンバーはこちら。
https://www.simulationroom999.com/blog/In-vehicle-network-backnumber/
はじめに
lwIPのHTTPサーバ関連の話。
HTTPサーバ機能の下にあるaltcp(application layered TCP connection API)の説明、
初期化コードを呼べばHTTPサーバとして動作するが、どこでlistenしているか。
などなど。
登場人物
博識フクロウのフクさん
イラストACにて公開の「kino_k」さんのイラストを使用しています。
https://www.ac-illust.com/main/profile.php?id=iKciwKA9&area=1
エンジニア歴8年の太郎くん
イラストACにて公開の「しのみ」さんのイラストを使用しています。
https://www.ac-illust.com/main/profile.php?id=uCKphAW2&area=1
lwIPのHTTPサーバ関連の話
そういえば、すっかり忘れていたのだけど、
目的の中にHTTPサーバがあるんだよねー。
これの下調べが全然んできてない・・・。
いままでも一切触れてないもんねー。
lwIPに最初からアドオンされてる機能って話は聞いていたけど
どうつかえばいいんだろう?
機能としては
\apps\http\httpd.c
で定義されてるものになる。
ソースコードとしては組み込み済みなんで、
あとは初期化コードを呼べば動作はするはずだよ。
lwIPのHTTPサーバの初期化コード
初期化コードを呼ぶだけで動作するの?
イマイチ、ピンとこないなぁ・・・。
初期化コード自体は
\apps\http\httpd.c
の中にある、
httpd_init
って関数なんだけど、これの中を追ってみよう。
/**
* @ingroup httpd
* Initialize the httpd: set up a listening PCB and bind it to the defined port
*/
void
httpd_init(void)
{
struct altcp_pcb *pcb;
#if HTTPD_USE_MEM_POOL
LWIP_MEMPOOL_INIT(HTTPD_STATE);
#if LWIP_HTTPD_SSI
LWIP_MEMPOOL_INIT(HTTPD_SSI_STATE);
#endif
#endif
LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n"));
/* LWIP_ASSERT_CORE_LOCKED(); is checked by tcp_new() */
pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL);
httpd_init_pcb(pcb, HTTPD_SERVER_PORT);
}
さらに、
altcp_tcp_new_ip_type
と
httpd_init_pcbって関数を読んでるみたいだね。
altcp_tcp_new_ip_typeの中身
さらにaltcp_tcp_new_ip_typeの中を見ると。
struct altcp_pcb *
altcp_tcp_new_ip_type(u8_t ip_type)
{
/* Allocate the tcp pcb first to invoke the priority handling code
if we're out of pcbs */
struct tcp_pcb *tpcb = tcp_new_ip_type(ip_type);
if (tpcb != NULL) {
struct altcp_pcb *ret = altcp_alloc();
if (ret != NULL) {
altcp_tcp_setup(ret, tpcb);
return ret;
} else {
/* altcp_pcb allocation failed -> free the tcp_pcb too */
tcp_close(tpcb);
}
}
return NULL;
}
tcp関連の初期化をしてるのかな?
ただ、altcpってどういうこと?
altcpはapplication layered TCP connection APIの略だね。
application layered TCP?
TCPを抽象化している層なんだけど、
例えばHTTPSって、HTTPをSSL/TLSで暗号化したものなんだけど、
HTTPサーバからすると暗号化しているかどうかは管理したくない。
よって、HTTPとTCPの間に抽象化層を挟む。
暗号化するかどうかは抽象化層で決めて
HTTPサーバとしては特に関知しないという仕組みだね。
んー、なんとなくわかる気がするが、
今回の場合は特にSSL/TLSは使わないから、
そんままTCPになるってことなのかな?
そうだね。
今回はTCP直結と全く一緒の動きになるよ。
httpd_init_pcbの中身
そして、httpd_init_pcbの中を見ると、
static void
httpd_init_pcb(struct altcp_pcb *pcb, u16_t port)
{
err_t err;
if (pcb) {
altcp_setprio(pcb, HTTPD_TCP_PRIO);
/* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
err = altcp_bind(pcb, IP_ANY_TYPE, port);
LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
pcb = altcp_listen(pcb);
LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
altcp_accept(pcb, http_accept);
}
}
こっちもaltcpだね。
TCPと一緒と考えると、
①bindして
②listenして
③accept時のコールバック関数を設定
って感じかな?
そうだね。
その認識でOKだよ。
http_acceptの中身
ここまで来たら、http_acceptの中も見てみよう。
http_accept(void *arg, struct altcp_pcb *pcb, err_t err)
{
struct http_state *hs;
LWIP_UNUSED_ARG(err);
LWIP_UNUSED_ARG(arg);
LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void *)pcb, arg));
if ((err != ERR_OK) || (pcb == NULL)) {
return ERR_VAL;
}
/* Set priority */
altcp_setprio(pcb, HTTPD_TCP_PRIO);
/* Allocate memory for the structure that holds the state of the
connection - initialized by that function. */
hs = http_state_alloc();
if (hs == NULL) {
LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n"));
return ERR_MEM;
}
hs->pcb = pcb;
/* Tell TCP that this is the structure we wish to be passed for our
callbacks. */
altcp_arg(pcb, hs);
/* Set up the various callback functions */
altcp_recv(pcb, http_recv);
altcp_err(pcb, http_err);
altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
altcp_sent(pcb, http_sent);
return ERR_OK;
}
あー!
ここで受信コールバック、エラーコールバック、ポーリングコールバック、送信コールバックを設定してるのか!!
そうそう。
流れてとしては
①初期化時にlisten状態にし、accept時のコールバック関数を設定
②accept時に受信、エラー、ポーリング、送信のコールバック関数を設定
結果的にhttp_init関数を読んでおけばHTTPサーバとしては勝手に動作するって寸法。
へー。
上手くできてるなー。
とりあえず、初期化時にhttp_initを呼ぶようにしておこう。
まとめ
まとめだよ。
- HTTPサーバ機能の下にaltcp(application layered TCP connection API)が存在。
- HTTPサーバの初期化コードを呼べばHTTPサーバとして動作する。
- httpd_initの中でlisten待ち。
- accept時に受信、エラー、ポーリング、送信のコールバック設定。
バックナンバーはこちら。
コメント