連載
» 2012年02月09日 11時55分 公開

ソフトウェア完全自作のWebサーバを動かしてみようH8マイコンボードで動作する組み込みOSを自作してみよう!(7)(3/4 ページ)

[坂井弘亮,@IT MONOist]

6.HTTPサーバの実装

 次にhttpdタスクを見てみましょう。

 リスト10は、httpdタスク(httpd.c)の先頭部分にある文字列の定義です。見れば分かるように、送信するHTML文字列をそのまま文字列データ(つまり、charの配列)としてプログラム中に格納しています。

 KOZOSはファイルシステムを持たないため、HTMLデータを文字列の“ベタデータ”としてプログラム中に直接保持しているわけです。


static char header[] =
"HTTP/1.0 200 OK\r\n"
/*"Date: Sat, 23 Oct 2010 12:00:00 GMT\r\n"*/
"Server: KOZOS-httpd/1.0\r\n"
"Content-Type: text/html\r\n"
"Content-Length: #####\r\n"
"\r\n";
static char top_document[] =
"<html>\n"
"<head>\n"
"<title>This is KOZOS!</title>\n"
……(中略)……
"<h1>HTTP server on KOZOS (#####)</h1>\n"
"<p><a href=\"about.html\">KOZOSとは?</a>\n"
"<p><a href=\"kozos.html\">KOZOSの現状</a>\n"
……(中略)……
static char about_document[] =
"<html>\n"
"<head>\n"
"<title>KOZOSとは?</title>\n"
……(中略)……
"<center><h1>KOZOSとは?(#####)</h1></center>\n"
"<p>KOZOSは坂井弘亮が作成している、学習向けの組み込みOSです。主に秋月電子のH8/3069Fマイコンボードで動作し、組み込みOSの自作・動作などの学習ができます。\n"
"<p>以下の点で、個人での学習用に向いています。\n"
……(中略)……
static char kozos_document[] =
"<html>\n"
"<head>\n"
"<title>KOZOSの現状</title>\n"
……(中略)……
"<center><h1>KOZOSの現状(#####)</h1></center>\n"
"<p>KOZOSはKOZOSプロジェクトにより開発が進められています(まあ坂井1人でやっているのですが)。\n"
"<p>以下が既に実現されています。\n"
……(中略)……
static char makeos_document[] =
"<html>\n"
"<head>\n"
"<title>組み込みOSを作ってみませんか?</title>\n"
……(中略)……
"<center><h1>組み込みOSを作ってみませんか?(#####)</h1></center>\n"
……(中略)……
リスト10 HTML文字列をベタデータとして格納する(httpd.c)

 以下、リスト11の配列documents[]は、これらの文字列データへのポインタの配列と、それに対応するファイル名のテーブルです。HTTPのGETメソッドでファイル名を要求されたら、このテーブルと照らし合わせて、対応するHTMLデータを返してやればいいわけです。

static struct documents {
  char *counterp;
  char *filename;
  char *document;
} documents[] = {
  { NULL, "/index.html", top_document },
  { NULL, "/about.html", about_document },
  { NULL, "/kozos.html", kozos_document },
  { NULL, "/makeos.html", makeos_document },
  { NULL, NULL, unknown_document }
};
リスト11 ファイル名とHTMLデータの対応(httpd.c)

 リスト12は、httpdタスクの初期化処理とメインループです。

static void send_accept()
{
  ……(中略)……
  buf->cmd = TCP_CMD_ACCEPT;
  ……(中略)……
  buf->option.tcp.accept.port = 80;
  kz_send(MSGBOX_ID_TCPPROC, 0, (char *)buf);
}
 
int httpd_main(int argc, char *argv[])
{
  ……(中略)……
  send_accept();
  ……(中略)……
  while (1) {
    kz_recv(MSGBOX_ID_HTTPD, NULL, (char **)&buf);
 
    switch (buf->cmd) {
    ……(中略)……
    case TCP_CMD_RECV:
    ……(中略)……
    ret = parse(number, buffer);
    ……(中略)……
}
リスト12 httpdタスクのメインループ(httpd.c)

 httpd_main()の最初で、send_accept()を呼び出し、tcpタスクに対して接続待ち受けの依頼を行います。さらに、send_accept()では「MSGBOX_ID_TCPPROC」で待ち受けをします。これにより、実際にデータを受信した際、kz_recv()で受信データを受け取ることになります。受信したデータは、parse()によって解析を行います。

 リスト13が、parse()の実装となります。

static int parse(int number, char *str)
{
  ……(中略)……
 
  if (strncmp(str, "GET", 3))
    return 0;
 
  for (p = str + 3; *p == ' '; p++)
    ;
  filename = p;
  ……(中略)……
 
  for (docs = documents; docs->filename; docs++)
    if (!strcmp(filename, docs->filename))
      break;
  ……(中略)……
  send_string(number, header);
  send_string(number, docs->document);
  ……(中略)……
}
 
リスト13 受信データの解析処理(httpd.c)

 parse()では、GETメソッドの行からファイル名を抽出し、リスト11の配列documents[]からファイル名を検索して、対応するHTMLデータを取得します。さらに、send_string()により、HTTPヘッダとHTMLのメッセージを送信します。

 リスト14が、send_string()の実装です。

static void send_write(int number, int size, char *data)
{
  ……(中略)……
  buf->cmd = TCP_CMD_SEND;
  ……(中略)……
  kz_send(MSGBOX_ID_TCPPROC, 0, (char *)buf);
}
 
static void send_string(int number, char *str)
{
  ……(中略)……
    send_write(number, size, str);
    ……(中略)……
}
リスト14 HTTPの応答の送信(httpd.c)

 send_string()からはsend_write()を呼び出します。send_write()では、tcpタスクに対してデータをメッセージ送信することで送信依頼をします。後は、tcpタスクが送信処理を適切に行ってくれることになります。

Copyright © ITmedia, Inc. All Rights Reserved.