В предыдущей части нашего занятия мы научились закрывать WebSocket, а также отправили с клиента и начали обработку на сервере команды начала и окончания передачи данных.
Вспомним нашу табличку с описанием байтов протокола.
Если нам нужно передать данные до 125 байт, то младшие семь бит второго байта (а правильнее байта 1) будут содержать длину нашего фрейма, если нам надо передать фрейм длиной от 126 до 65535 байт, то тогда младшие семь бит байта 1 будут иметь значение 126. Тогда следующие два байта (2 и 3) будут содержать длину фрейма (причём старший бит будет бит 2, а бит 3 — это младший бит длины фрейма). Но, а если вдруг нам будет нужно передать фрейм длиной от 65536 до практически бесконечности (длина ограничивается размерностью 64-битного числа), то мы значение семи младших бит байта 1 объявляем 127, тогда следующие уже не 2, а восемь байт и будут содержать длину нашего фрейма. А дальше всё, как и было. После поля длины фрейма у нас пойдут наши данные, если, конечно, не объявлена маска. А если объявлена маска, то пойдёт маска 4 байта, а дальше уже пойдут байты, обработанные этой самой маской.
Вернёмся в наш бесконечный цикл и узнаем, а не превысила ли длина нашего будущего сообщения значение 125, и если она не превысила данное значение, то мы тогда в байт 1 заносим значение длины, не обращая внимание на бит маски, так как у нас её не будет, а затем уже, начиная с байта 2, положим в наш буфер список задач (уже по-настоящему). Потом, используя функцию сортировки, отсортируем наш массив с задачами и их свойствами и передадим весь наш фрейм клиенту. Мы здесь не проверяем тип фрейма, запрошенного клиентом, потому что если будет тип 2, то мы точно знаем, что он у нас больше 125 и в это тело условия он не попадёт
1 2 3 4 5 6 7 8 9 10 |
send_ws_buf[i * 2 + 5] = (uint8_t)(val>>8); } } if(len<126) { send_ws_buf[1] = len; osThreadList((uint8_t *)(send_ws_buf + 2)); StrSort((char *)(send_ws_buf + 2)); netconn_write(conn, (void *) send_ws_buf, len + 2, NETCONN_COPY); } |
Далее добавим условие, которое сработает, если длина наших данных будет от 125 до 65536. В таком случае мы заносим в байт 1 значение 126, укладываем длину фрейма в байты 2 и 3, если тип фрейма у нас 1 (текстовые данные), то уже начиная с байта 4 мы укладываем список задач, потом их сортируя, и отправляем наш фрейм клиенту. Мы помним, что если у нас фрейм бинарный, то наши байты там уже лежат
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
netconn_write(conn, (void *) send_ws_buf, len + 2, NETCONN_COPY); } else if((len<65536)) { send_ws_buf[1] = 126; send_ws_buf[2] = (uint8_t)(len>>8); send_ws_buf[3] = (uint8_t)len; if(type==1) { osThreadList((uint8_t *)(send_ws_buf + 4)); StrSort((char *)(send_ws_buf + 4)); } netconn_write(conn, (void *) send_ws_buf, len + 4, NETCONN_COPY); } |
Теперь мы отправили клиенту все данные.
Вроде бы здесь всё.
Теперь идём в index.html, где в функции обработки кнопки для старта текстовой информации startstring добавим ещё событие объекта, которое сработает в тот момент, когда придут данные
1 2 3 4 |
butchart_elem.innerHTML = ''; WebSocket_connection.onmessage = function (evt) { information_elem.innerHTML = evt.data; }; |
Попробуем пока, как будет работать передача списка задач (так как у нас ещё в index.html ничего ещё не сделано для кнопки под графиком).
Сохраним страницу, перегенерируем fsdata.c, обновим дерево проекта, пересоберём код и прошьём контроллер, после чего обновим страницу в браузере и снова нажмём на нашу верхнюю кнопку START
Мы видим, что у нас всё работает.
Посмотрим также процесс в анализаторе трафика
Мы видим, что пакеты приходят регулярно и клиент у нас их не запрашивает, он лишь подтверждает приём пакетов TCP.
Осталось нам только увидеть, как приходят данные бинарного типа и отобразить их в графике.
Остановим цикл с помощью кнопки STOP, закроем наше соединение с помощью кнопки Disconnect и идём опять в наш index.html работать уже с графиком. Сначала в функции-обработчике окончания загрузки документа инициализируем наш график аналогично тому, как мы делали в прошлом уроке
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
butconnect_elem = document.getElementById('butconnect'); //init chart var ctx = document.getElementById("myChart"); var data16Arr = new Uint16Array(512); myChart = new Chart(ctx, { type: 'line', data: { labels: ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""], datasets: [{ label: '# of Votes', data: data16Arr, backgroundColor: 'rgba(150, 150, 0, 0.0)', //полностью прозрачный фон borderColor: 'rgba(150, 150, 0,1)', borderWidth: 2, pointStyle: 'line', pointRadius: 0 }] }, options: { tooltips: { enabled: false }, animation: { duration: 0 }, responsive: true, scales: { xAxes: [{ ticks:{ min: 0, max: 512, stepSize : 64, }, stacked: true, gridLines: { lineWidth: 0, color: "rgba(255,255,255,0.0)" } }], yAxes: [{ stacked: true, ticks: { min: 0, max: 4096, stepSize: 256, } }] } } }); |
Добавим функцию-обработчик нажатия на вторую кнопку START
1 2 3 4 5 6 7 8 9 10 11 12 |
function startchart(){ WebSocket_connection.binaryType = 'arraybuffer'; butchart_elem.innerHTML = ' <input class = "butcolor" type="button" style="color: #000000; background-color: #666666;" onclick="stopchart()" value="STOP"/>'; butstring_elem.innerHTML = ''; WebSocket_connection.onmessage = function (evt) { var uint16Array = new Uint16Array(evt.data); myChart.data.datasets[0].data = uint16Array; myChart.update(); }; WebSocket_connection.send("2"); } </script> |
В данной функции мы опять назначаем тип бинарных данных в arraybuffer, превращаем кнопку START в STOP, кнопку START для текстового поля убираем, а также добавляем обработчик открытия сокета, в котором мы создаём объект типизированного 16-разрядного массива, где мы в качестве аргумента передаём пришедшие данные, а затем присваиваем наш массив полю данных графика. По окончанию не забываем обновить график. И также мы посылаем для запуска цикла серверу двоечку.
Также добавим функцию кнопки STOP для графика
1 2 3 4 5 6 |
function stopchart(){ WebSocket_connection.send("4"); butstring_elem.innerHTML = ' <input class = "butcolor" type="button" style="color: #000000; background-color: #bbbbbb;" onclick="startstring()" value="START"/>'; butchart_elem.innerHTML = ' <input class = "butcolor" type="button" style="color: #000000; background-color: #bbbbbb;" onclick="startchart()" value="START"/>'; } </script> |
Здесь мы уже передаём четвёрку и также возвращаем наши кнопки START обоим полям.
Проверим наш код в работе. Обновим fsdata, дерево проекта, соберём код, прошьём контроллер, перезагрузим страницу и понажимаем на наши кнопки, особенно нам интересен график (нажмите на картинку для увеличения изображения)
Мы видим, что всё у нас прекрасно работает.
Также посмотрим трафик в Wireshark
Здесь мы также видим, что пакеты идут в основном только от сервера. От клиента мы видим одни лишь подтверждения. Их бы тоже почти не было, если бы мы могли себе позволить буфер побольше.
Попробуем также убавить задержку в функции до 100 милисекунд. Мы видим, что в этом случае также всё успевает передаваться. Вы можете поэкспериментировать у себя с меньшими задержками. Напишите в комментариях, кому какую удалось установить задержку без потери работоспособности сокета.
Итак, сегодня мы научились очень многому.
Во-первых, мы освоили передачу данных с сервер в браузер клиента посредством технологии WebSocket, что позволило нам также обойтись без перезагрузки всей страницы, а также мы теперь можем передавать потоки данных без запроса клиента.
Во-вторых, мы научились использовать внешнюю память для хранения массивов, что позволило нам сэкономить внушительный объём памяти.
Всем спасибо за внимание!
Предыдущая часть Программирование МК STM32 Следующий урок
Отладочную плату можно приобрести здесь STM32F746G-DISCOVERY
Смотреть ВИДЕОУРОК (нажмите на картинку)
файл 'makefsdata.cmd' правильнее сейчас такой (из-за новых версий 'CubeMX'):
makefsdata.exe -11
ren fsdata.c fsdata_custom.c
«ren fsdata.c fsdata_custom.c» не заменяет существующий файл.
Нужно использовать «move /y fsdata.c fsdata_custom.c»
Hi,
I am trying this NETCONN_WEBSOCKET in NUCLEO-F746ZG. I am able to load the index page in static IP. but when i press connect button receive an message «соединение закрыто некорректно код: 1006»