STM Урок 134. LAN8742A. LWIP. SOCKET. TCP Client

 

 

 

Продолжаем работать с интерфейсом SOCKET с протоколом TCP и сегодня мы попытаемся создать клиент. Как мы убедились на примере клиента для интерфейса NETCONN, что с клиентом работать не только не легче, чем с сервером, но ещё и тяжелее.

В качестве сервера теперь у нас, наоборот, будет ПК.

Проект за основу мы возьмём с прошлого урока с именем LAN8742_TCP_SERVER_SOCKET и теперь у него, соответственно, будет имя LAN8742_TCP_CLIENT_SOCKET.

Откроем наш проект в Cube MX, сначала в настройки ETH и поменяем там MAC-адрес, так как, я думаю вы уже догадались, что мы будем наш клиент затем соединять с сервером на отладочной плате

 

 

В настройках LWIP изменим сетевой адрес

 

 

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

В файле main.c в функции main() исправим шапку

 

TFT_DisplayString(0, 10, (uint8_t *)"TCP Client", CENTER_MODE);

 

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

Поэтому удалим данные глобальные указатели

 

unsigned char *out_buffer;

char *str_usart;

char *str_out;

unsigned char *send_ws_buf;

char* str_buf1;

char* str_buf2;

char* str_buf3;

 

Вместо этого добавим глобальные массивы в обычной ОЗУ

 

 

Также в функции main() удалим инициализацию массивов в памяти SDRAM

 

//str_usart 512 bytes

str_usart = (char *) 0xC0400000;

//str_out 32 bytes

str_out = (char *) 0xC0400200;

//out_buffer 192 bytes

out_buffer = (uint8_t *) 0xC0400220;

memset(out_buffer,0,192);

//send_ws_buf 1312 bytes

send_ws_buf = (uint8_t *)0xC04002E0;

//str_buf1 256 bytes

str_buf1 = (char *) 0xC0400800;

//str_buf2 256 bytes

str_buf2 = (char *) 0xC0400900;

//str_buf2 1024 bytes

str_buf3 = (char *) 0xC0400A00;

 

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

 

 

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

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

Спасибо за наводку автора комментария в группе «В Контакте», который мне это подсказал, иначе я бы до сих пор думал по-прежнему!

Функцию client_socket_thread переименуем в recv_thread и удалим полностью код из её тела.

Аналогично поступим и с функцией tcp_thread, переименовав её в send_thread и также очистим её тело.

В функции задачи по умолчанию StartDefaultTask изменим номер порта нашего устройства-клиента

 

sock01.port = 1551;

 

А здесь изменим имя задачи

 

sys_thread_new("send_thread", send_thread, (void*)&sock01, DEFAULT_THREAD_STACKSIZE * 2, osPriorityNormal);

 

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

 

 

Создадим сокет для клиента, выделим память под адресную структуру и заполним её поля

 

 

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

 

 

Попытаемся связаться с сервером при помощи команда connect() и, если всё прошло нормально, то похвалимся об этом через шину USART в терминальной программе

 

 

Соединим плату с сетью, подключим её к ПК с помощью кабеля USB, соберём код, прошьём контроллер, запустим анализатор трафика WireShark, отфильтровавшись там по сетевому адресу нашего клиента, также запустим терминальную программу и соединимся с виртуальным портом платы и запустим ещё программу Netcat, только не с командной строки, а с помощью запуска исполняемого файла nc.exe или nc64.exe. Затем в появившейся командной строке введём команду для того, чтобы начать слушать порт сервера (5444)

 

Перезагрузим контроллер и в анализаторе трафика увидим, что клиент с сервером соединились

 

 

Также мы это видим и в терминальной программе

 

 

Чтобы разъединиться с клиентом, в программе netcat введём комбинацию клавиш «Ctrl + C«

 

 

Соединение разорвётся хоть и некорректно, но всё же порт освободится от прослушки

 

 

С корректностью разберёмся позже. Разрывать соединение будем по инициативе клиента, вот тогда всё и нормализуется.

Добавим глобальный идентификатор задачи приёма пактов

 

 

Удалим глобальную структуру struct_client_socket, а также переменную типа этой структуры, а вместо неё добавим другую структуру и переменную её типа

 

 

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

 

 

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

 

 

Добавим бесконечный цикл, в котором попытаемся принять пакет

 

 

Если это пакет с данными, то обнулим в буфере символ перевода строки и отобразим строку на дисплее

 

 

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

 

 

Если этих квантов больше 50000, то попытаемся закрыть наше соединение, чтобы проверить процесс разъединения с сервером

 

 

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

 

 

А если количество системных квантов не достигло ещё 50000, то мы, соответственно, в это тело условия не попадаем и продолжаем нашу программу, в которой отобразим количество системных квантов в терминальной программе, а также отправим их серверу. Затем применим задержку в 1 секунду до следующего цикла

 

 

Соберём код, прошьём контроллер, опять запустим netcat и начнём слушать порт.

Перезагрузим контроллер и увидим, что наши пакеты нормально приходят в netcat

 

 

Также пакеты наши видны и в терминальной программе

 

 

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

 

 

Также информацию о том, что соединение разорвано, мы видим на нашем дисплее

 

 

Попробуем во время передачи данных попытаемся что-то передать из программы netcat нашему клиенту

 

 

Клиент принял пакет и отобразил на дисплее результат в виде строки

 

Мы убедились в том, что наш клиент полностью работоспособен.

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

 

 

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

 

Исходный код

 

 

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

 

 

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

 

STM LAN8742A. LWIP. SOCKET. TCP Client

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

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

*