STM Урок 129. LAN8742A. LWIP. NETCONN. HTTP. WebSocket. Часть 2



В предыдущей части урока мы познакомились с технологией WebSocket, настроили проект и подготовили страницу для клиента.

Теперь нам надо добавить функционал в автоматически созданную задачу для вывода данных на дисплей TaskStringOut.

Вот это, соответственно, мы вставим до бесконечного цикла

 

А вот это в бесконечный цикл

 

Также мы с вами видим, что у нас исчезла инициализация стека протоколов, вместо неё теперь здесь вот это

MX_MBEDTLS_Init();

если мы зайдём вовнутрь данной функции, то заметим, что она практически пустая, там есть пользовательские заготовки, то есть можно инициализировать LWIP и в ней, но мы это сделаем в тексте задачи по умолчанию StartDefaultTask в пользовательской части

 

Надеюсь, все мы понимаем, что без этого наша плата не будет осуществлять обмен данными по сети.

Функции DynWebPageStr и DynWebPage удалим вместе с телами, теперь мы данные передавать будем без таких огромных заголовков.

В функции сортировки StrSort изменим имя массива, применяя здесь уже массив в памяти SDRAM

strarray_ptr *strarray1 = (void*) str_buf3;

Функцию tcp_thread мы переименуем в http_thread, так как здесь у нас в основном происходит обмен по такому протоколу.

Причём тело из данной функции мы также полностью удалим, здесь будет сильная реструктуризация кода.

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

Добавим сюда только строку создания задачи по обработке пакетов HTTP

 

Начнём писать код функции созданной задачи http_thread.

Сначала добавим все переменные, массивы и структуры, которые нам потребуются в данной функции

 

Создание соединения мы сейчас будем полностью осуществлять в данной задаче, не разделяя по двум задачам. Раньше мы основную структуру инициализировали в задаче по умолчанию.

Далее применим другой цвет вывода информации на дисплей и создадим основную структуру соединения

 

Если со структурой всё нормально, то свяжем её с локальным портом

 

А теперь выше функции задачи http_thread добавим функцию с предыдущим именем — это у нас будет функция задачи по обработке соединения для WebSocket. Сразу добавим в данную функцию все локальные переменные, структуры и всякие указатели, которые нам будут нужны в последствии и также аналогичным образом создадим соединение и свяжем её с другим локальным портом, который у нас и будет служить для обмена по соединению WebSocket

 

 

Вернёмся в функцию http_thread, где при успешной связи соединения с портом создадим задачу для WebSocket и начнём слушать наш порт

 

 

В противном случае мы удалим структуру соединения

 

 

Вернёмся в тело положительного исхода ситуации и добавим туда бесконечный цикл, в котором свяжем основную структуру со структурой соединения с конкретным клиентом

 

 

 

Если всё нормально, то начинаем ждать от клиента пакет TCP

 

 

А если что-то не так, то пока уйдём в бесконечный цикл и там уже будем разбираться в ситуации, если что

 

 

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

 

 

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

 

 

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

 

 

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

Теперь начнём потихоньку уже заниматься вебсокетом.

В задаче tcp_thread мы также начнём слушать наш порт

 

 

Затем также добавим бесконечный цикл, в котором свяжем структуры

 

 

Добавим глобальную структуру соединения и создадим переменную типа данной структуры

 

 

Эта структура нам понадобится для параметра, который мы будем передавать ещё одной задаче.

Добавим задачу в которой, мы будем передавать пакеты клиенту.

Сначала создадим глобальный идентификатор данной задачи

 

 

Теперь добавим функцию для этой задачи выше функции tcp_thread

 

 

 

Вернёмся в функцию tcp_thread, где при удачной связи соединений, мы присвоим полю нашей структуры параметра указатель на структуру соединения, а также создадим нашу новую задачу, уходя после её создания в бесконечный цикл. После тела условия мы удалим структуру соединения и удалим задачу. В данное места мы попадаем, если вдруг мы выйдем из бесконечного цикла, а такой случай у нас предвидится

 

 

В бесконечном цикле попытаемся принять пакет от клиента, затем добавим условие успешного приёма, и, пока не добавляя код в тело успешного варианта, добавим код в тело противного случая, где очистим буфер, разъединимся с клиентом, а также выйдем из бесконечного цикла. Также после обоих тел условия мы также очистим структуру буфера

 

 

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

 

 

Проверим пакет, пришедший от клиента, на наличие HTTP-запроса типа GET, так как сначала такой и должен прийти

 

 

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

Пока, конечно, никакой запрос нам никто не пошлёт, для этого нужна функция в документе index.html.

Давайте займёмся этим.

Для начала мы добавим ещё одну кнопку для соединения с портом для WebSocket нашего сервера, которая будет потом превращаться в кнопку для разъединения

 

 

Создадим глобальные переменные для соединения WebSocket, для графика, а также для обращения к полям по идентификаторам, чтобы несколько раз такие длинные строки не повторять в скрипте. Инициализацию данных переменных мы проводим уже в функции, которая вызовется, когда до конца прогрузится страница

 

 

Вы, наверно, обратили внимание, что данную функцию мы применяем несколько по-другому. Этот способ удобен, если нам не хочется писать наименование функции в тегах начала содержимого страницы.

Добавим обработчик кнопки Connect, в котором попытаемся соединиться с сокетом

 

 

Здесь мы применили функцию для создания соединения типа WebSocket. Мы создали объект типа WebSocket, который в качестве параметра берёт строку, в которой сначала идёт аббревиатура протокола ws, затем двоеточие и два слэша, как и в случае с http, а затем уже IP-адрес или имя сервера, а также порт, который мы назначили соединению на сервере.

У данного класса (конечно, в JS не всегда это называется классом, хотя поддержка уже включена), объект которого мы создали, существует несколько видов функций обратного вызова, которыми мы будем пользоваться несколько позже.

Пока проверим, то. что клиент наш будет запрашивать соединение у сервера по кнопке. Поэтому соберём сначала, как всегда, файл fsdata.c, обновим дерево проекта, соберём код, прошьём его в контроллер, запустим WireShark, отфильтровав там весь наш поток по IP нашего сервера (кто конечно его уже не запустил заранее, а я думаю, что уже давно его все запустили), обновим страницу в браузере и нажмём на верхнюю кнопку. У нас вроде бы ничего не происходит в браузере, а на самом деле творится уже вот что

 

 

Браузер незаментненько отправил уже некий запрос серверу, причём сервер его подтвердил, то есть он его принял и вполне уже готов дать ответ, но за это мы скоро уже тоже возьмёмся.

Посмотрим теперь состав запроса нашего клиента

 

 

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

 

Sec-WebSocket-Key: ZbAt28e5ASwHIgSM/IGtZA==

 

Это и есть ключ, который даёт нам на обработку клиент.

Поэтому вернёмся в наш проект и попробуем с ним поработать.

В функции tcp_thread мы сначала запросим память под очередь для отправки данных в функцию вывода на дисплей, перешлём в структуру вертикальную координату и попытаемся найти то место в памяти скопированной из буфера строке, в которой присутствует вхождение строки, по которой мы и найдём наш заветный ключ

 

 

Если к длине ключа и прибавить к ней данную строку, то мы получим число 43. Вот по такому адресу мы и запишем окончание строки, так как мы уверены, что строку данную наша функция strstr уже нашла и указатель istr уже туда поставила

 

 

Затем мы наш ключ отобразим на дисплее, не забывая после этого очистить память очереди

 

 

Теперь начнём готовить клиенту ответ. Подготовим начало запроса, состоящего из стандартных строк

 

 

Теперь добавим глобальный строковый массив в виде константы для GUID, а также строковый массив для HASH

 

 

Вернёмся в нашу функцию и в отдельном буфере соединим ключ, который пришёл нам от клиента с уникальным идентификатором

 

 

Затем сформируем из этого всего хэш по алгоритму SHA1 с помощью специальной функции библиотеки mbedtls

 

Закодируем наш хэш по алгоритму Base64

 

Завершим нулём нашу выходную строку нулём в нужном месте используя для этого длину строки, которую возвратила функция

 

Присоединим наш закодированный хэш, который и будет ответом на ключ, к нашей частично подготовленной строке ответа клиенту и завершим данный ответ дважды возвратом каретки и переводом строки

 

Отправим строку с ответом клиенту

 

Соберём код, прошьём контроллер, обновим страницу в браузере и ещё раз попробуем нажать на кнопку.

Теперь мы уже видим наш ответ.

Значит соединение у нас успешно создано.

В следующей части нашего занятия мы научимся закрывать WebSocket, а также отправим с клиента и начнём обработку на сервере команды начала и окончания передачи данных.

 

 

Предыдущая часть Программирование МК STM32 Следующая часть

 

 

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

 

 

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

 

STM LAN8742A. LWIP. NETCONN. HTTP. WebSocket

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

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

*