【Ethernet】車載ネットワーク その66【lwIP+npcap⑫】

【Ethernet】車載ネットワーク その66【lwIP+npcap⑫】 車載ネットワーク

バックナンバーはこちら。
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時に受信、エラー、ポーリング、送信のコールバック設定。

バックナンバーはこちら。

コメント

タイトルとURLをコピーしました