STM Урок 127. LAN8742A. LWIP. NETCONN. HTTP Server



Продолжаем работу со стеком протоколов LWIP, а также с его интерфейсом NETCONN.

И сегодня мы начнём работать с протоколом уже прикладного уровня — HTTP. Данный протокол находится выше уровнем, чем протокол TCP, а протокол TCP является для протокола HTTP протоколом транспортного уровня. Со всем этим мы уже знакомы, так как с протоколом HTTP мы работаем давно и нет смысла углубляться в его особенности, а также в то, насколько в нынешний век интернета он востребован.

Поэтому переходим сразу к делу.

По изучению использования протокола HTTP в плане работы с интерфейсом NETCONN мы пойдём также последовательным путём. Сначала мы поработаем просто со страницами HTML, не используя никаких дополнительных технологий, а в дальнейших уроках мы уже попробуем усовершенствовать наш проект и попытаемся передать данные браузеру с нашего сервера без перезагрузки всей страницы. Какие мы будем для этого использовать технологии — мы узнаем в данных уроках.

А сейчас создадим проект из проекта урока 124 с именем LAN8742_TCP_SERVER_NETCONN и присвоим ему имя LAN8742_HTTP_SERVER_NETCONN.

Откроем проект в Cube MX, откроем настройки LWIP, перейдём там в раздел General Settings и немного добавим количество одновременно открытых соединений TCP

 

 

 

Проследите за тем, чтобы галочка в поле Show Advanced Parameters была установлена.

Ничего в данном разделе не трогаем, оставим пока всё по умолчанию.

Далее идём в раздел Key Options и кое-что поправим здесь

 

 

 

 

Перейдём в настройки FREERTOS в раздел Tasks and Queues и удалим оттуда вторую задачу, все равно мы ей не пользуемся. Останется только одна задача по умолчанию

 

 

Сгенерируем проект для System Workbench и откроем его там. Установим уровень оптимизации в 1, уберём при наличии отладочные настройки и закомментируем неизвестные компилятору строки в файле main.c.

Исправим шапку в main()

 

 

 

 

Попробуем собрать проект, только он у нас не соберётся и мы получим ошибку на отсутствие файла fsdata.c. Из урока 102 мы помним, как с этим бороться, поэтому с помощью утилиты makefsdata сгенерируем данный файл. Но, прежде чем мы его сгенерируем, мы должны будем в папку fs что-то положить, то есть подготовить файл странички.

Страничку вы можете положить туда любую, только желательно, чтобы имя её файла было index.html. Я создал страничку сам, как обычно. Взял техническую документацию нашей отладочной платы и скопировал из неё часть текста в редакторе CMS WordPress на виртуальном сайте. Размер получился около 15 килобайт, то что надо. Файл не слишком большой и не маленький, потребуется не один пакет TCP для его передачи, тем самым мы проверим, как наш сервер фрагментирует пакеты.

Ещё создадим файл, который мы будем отдавать клиенту, если не найдём запрошенный документ. Назовём его 404.html. Данный файл будет иметь следующее содержимое

 

 

 

По умолчанию утилита makefsdata генерирует заголовок в формате HTTP 1.0. Чтобы нам получить HTTP 1.1, мы должны воспользоваться специальным параметром командной строки утилиты.

А для того, чтобы нам всё время не вводить имя файла и параметры, создадим командный файл в папке с утилитой с именем makefsdata.cmd следующего содержания

 

makefsdata.exe -11

 

Сохраним и запустим файл makefsdata.cmd и получим файл fsdata.c.

Теперь при изменении содержимого папки  мы будем запускать файл makefsdata.cmd.

Если мы посмотрим содержимое файла fsdata.c, то мы увидим, что в заголовке у нас именно протокол HTTP 1.1

 

 

Скопируем файл fsdata.c, а также утилиту вместе с командным файлом и папку fs со всем содержимым и, если потребуется, файл msvcr100d.dll в папку проекта по пути «Папка с проектом//Middlewares/Third_Party/LwIP/src/apps/httpd/». Теперь мы собирать файлы будем там, чтобы каждый раз не копировать файл.

Вернёмся в проект, сделаем Refresh.

 

 

Затем найдём в дереве проекта файл fsdata.c, вызовем на нём контекстное меню, кликнув по нем правой кнопкой мыши и зайдём в его свойства. Выберем пункт C/C++ Build и установим галочку напротив надписи «Exclude resource from build»

 

 

Вот теперь проект у нас нормально соберётся.

Подключим в файле main.c библиотеку для работы с виртуальной файловой системой

 

 

 

В функции задачи по умолчанию StartDefaultTask мы можем закомментировать создание ещё одной задачи. Нам хватит и одной

 

 

 

Удалим в данной функции также и инициализацию параметра высоты, а в другом немного вертикальную координату поднимем

 

sock01.y_pos = 45;

sock02.y_pos = 180;

 

Инициализацию второго параметра также удалим

 

sock02.conn = conn;

 

Перейдём в задачу TCP-соединения tcp_thread и объявим там переменную вот такого типа для файловой структуры

 

char* buf;

struct fs_file file;

 

После создания соединения с клиентом в условии удалим всё тело. После этого в бесконечном цикле задачи у нас останется только вот такой вот код

 

for(;;)

{

  err = netconn_accept(conn, &newconn);

  if (err == ERR_OK)

  {

  }

  else

  {

    osDelay(1);

  }

}

 

Если соединение с клиентом прошло нормально, то попытаемся принять пакет

 

err = netconn_accept(conn, &newconn);

if (err == ERR_OK)

{

  recv_err = netconn_recv(newconn, &inbuf);

 

 

Если пакет нормально принят, то запишем его содержимое в буфер, после чего освободим память структуры буфера, также структуры соединения и закроем соединение с клиентом

 

 

 

Теперь зайдём в тело условия и поработаем там с принятой строкой.

Если строка является GET-запросом протокола HTTP, то очистим позицию, расположенную на 40 пикселей ниже нашей строки. Зачем она нужна, узнаем позже. И также напишем «Connect» в самом низу дисплея

 

netbuf_data(inbuf, (void**)&buf, &buflen);

if ((buflen >=5) && (strncmp(buf, "GET /", 5) == 0))

{

  qstruct = osMailAlloc(strout_Queue, osWaitForever);

  qstruct->y_pos = 250;

  sprintf(qstruct->str,"%-20s", "Connect");

  osMailPut(strout_Queue, qstruct);

  osDelay(1);

  qstruct->y_pos = arg_sock->y_pos + 40;

  sprintf(qstruct->str,"%-20s", " ");

  osMailPut(strout_Queue, qstruct);

  osDelay(1);

}

 

Если клиент обратился только по адресу к серверу, либо он явно указал файл главной страницы, то передадим ему её

 

osDelay(1);

if ((strncmp((char const *)buf,"GET / ",6)==0)||(strncmp((char const *)buf,"GET /index.html",15)==0))

{

  fs_open(&file, "/index.html");

  netconn_write(newconn, (const unsigned char*)(file.data), (size_t)file.len, NETCONN_NOCOPY);

  fs_close(&file);

}

 

А если запросил что-то другое, чего у нас на сервере нет, то передадим ему страницу 404.html с ошибкой, а также напишем на экране дисплея соответствующее сообщение на 40 пикселей ниже строки запроса

 

  fs_close(&file);

}

else

{

  /* Load Error page */

  fs_open(&file, "/404.html");

  netconn_write(newconn, (const unsigned char*)(file.data), (size_t)file.len, NETCONN_NOCOPY);

  fs_close(&file);

  qstruct->y_pos = arg_sock->y_pos + 40;

  sprintf(qstruct->str,"%-20s", "file not found");

  osMailPut(strout_Queue, qstruct);

  osDelay(1);

}

 

Отобразим строку запроса HTTP на дисплее, на всякий случай немного обрезав, если она больше 20 символов, и также освободим память структуры очереди

 

  osDelay(1);

}

if ((buflen >=20)) buf[20] = 0;

else buf[buflen] = 0;

qstruct->y_pos = arg_sock->y_pos;

sprintf(qstruct->str,"%-20s", buf);

osMailPut(strout_Queue, qstruct);

osMailFree(strout_Queue, qstruct);

osDelay(2);

 

Соберём проект, прошьём контроллер и попробуем ввести в браузере IP-адрес сервера

 

 

Аналогичную картину мы получим если введём в адресной строке браузера 192.168.1.191/index.html.

Теперь попробуем ввести имя несуществующего документа

 

 

В этом случае мы получим уведомление об ошибки с помощью страницы 404.html.

А на экране дисплея будет вот что

 

 

Отлично! Сервер работает. Теперь проверим, как будет отображаться страничка с картинками. То есть это тот случай, когда из страницы будут поступать на сервер дополнительные GET-запросы.

Для этого мы немного проиллюстрируем нашу страничку. Добавив в неё картинки. Для этого мы в папке fs создадим дополнительную папку IMG для удобства и скопируем туда несколько картинок.

Файл index.html продублируем с другим именем, например index1.html, чтобы не портить оригинальный без картинок.

Вставим в файл картинки приблизительно таким вот методом

 

<p style="text-align: center;">

  <img src="IMG/img01.jpg">

</p>

 

Пересоберём файл fsdata.c, обновим дерево проекта, также добавим в нашей задаче реакцию на запрос файла index1.html. Про обработку запросов на картинки также не забываем

 

if ((strncmp((char const *)buf,"GET / ",6)==0)||(strncmp((char const *)buf,"GET /index.html",15)==0))

{

 

  ...

}

else if (strncmp((char const *)buf,"GET /index1.html",16)==0)

{

  fs_open(&file, "/index1.html");

  netconn_write(newconn, (const unsigned char*)(file.data), (size_t)file.len, NETCONN_NOCOPY);

  fs_close(&file);

}

else if (strncmp((char const *)buf,"GET /IMG/img01.jpg",18)==0)

{

  fs_open(&file, "/IMG/img01.jpg");

  netconn_write(newconn, (const unsigned char*)(file.data), (size_t)file.len, NETCONN_NOCOPY);

  fs_close(&file);

}

else if (strncmp((char const *)buf,"GET /IMG/img02.jpg",18)==0)

{

  fs_open(&file, "/IMG/img02.jpg");

  netconn_write(newconn, (const unsigned char*)(file.data), (size_t)file.len, NETCONN_NOCOPY);

  fs_close(&file);

}

else if (strncmp((char const *)buf,"GET /IMG/img03.jpg",18)==0)

{

  fs_open(&file, "/IMG/img03.jpg");

  netconn_write(newconn, (const unsigned char*)(file.data), (size_t)file.len, NETCONN_NOCOPY);

  fs_close(&file);

}

else if (strncmp((char const *)buf,"GET /IMG/img04.jpg",18)==0)

{

  fs_open(&file, "/IMG/img04.jpg");

  netconn_write(newconn, (const unsigned char*)(file.data), (size_t)file.len, NETCONN_NOCOPY);

  fs_close(&file);

}

 

Вот теперь соберём код, прошьём контроллер и попробуем в браузере запросить файл index1.html

 

 

Все картинки на странице отображены.

Значит всё прекрасно работает. Думаю, что нет необходимости анализировать сетевой трафик в программе-анализаторе. Если у вас возникнет такое желание, то вы это сделаете самостоятельно, так как программой данной мы постоянно пользуемся и учить нам этому не нужно.

Итак, в данном уроке мы создали простой, но полностью функционирующий HTTP-сервер, используя интерфейс NETCONN стека протоколов LWIP.

Всем спасибо за внимание!

 

 

Предыдущий урок Программирование МК STM32 Следующий урок

 

Исходный код

 

 

Отладочную плату можно приобрести здесь STM32F746G-DISCOVERY

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

STM LAN8742A. LWIP. NETCONN. HTTP Server

3 комментария на “STM Урок 127. LAN8742A. LWIP. NETCONN. HTTP Server
  1. Valentin:

    Здравствуйте. Можно ли на этой библиотеке сделать мультисервер? В интернете ищу не могу найти. Во время подключение одновременно 4-х клиентов данная библиотека перестает работать вообще т.е. не реагирует ни никакие подключение. Код использовал из вашего урока. В чем может быть проблема? Прошу ответить на оба вопроса.

  2. Айнур:

    Здравствуйте, у меня возникла некоторая проблемка. При переходе по адресу платы открывается сайт, но без интерфейса, выводит просто html код. Если же открыть html документ с пк, то все будет в порядке.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*