Сторінки

пʼятниця, 25 березня 2016 р.

Передача данных типа double между приложениями С++ и Matlab через TCP/IP


В один прекрасный момент возникла необходимость передавать массив данных типа double по сети из приложения, написанного на языке Matlab, в С++ приложение. Кое-что получилось, поэтому хочу поделиться примерами в этой статье. Надеюсь, кому-то пригодится.

Расписывать о базовых аспектах сетевого программирования нет необходимости, так как в интернете много литературы. При разработке сетевых приложений нужно выбрать какой тип сокетов использовать: TCP или UDP. Выбор пал на TCP, поскольку это протокол, который использует принцип надежного соединения (вся пересылаемая информация гарантированно доходит до получателя в том же порядке, в каком была отправлена) и прост в использовании. Хотя в дальнейшем нужно будет разобраться с UDP.

Для передачи информации нужны клиент и сервер. Серверу укажем порт 7654. Клиенту надо указать IP-адрес сервера и порт (также 7654), по которому будет происходить соединение. Когда пройдет инициализация сокета сервера, он будет ожидать подключения от клиента. При соединении клиента с сервером дальше осуществляется процесс передачи информации. Серверное приложение реализовано на С++ в среде VS2015. Клиентское приложение написано в среде Matlab.

Задача состоит в следующем. В Матлабе создается массив данных типа double, которые нужно передать серверу, написанному на С++. При этом необходимо сначала передать серверу сколько данных (в байтах) будем отправлять. Для упрощения задачи, обе программы находятся на одном компьютере.

Рассмотрим клиента. Пример передачи данных между двумя приложениями Матлаба можно просмотреть здесь. В основном оттуда и код клиента.

Клиент подключается к серверу 

После того, как клиентом осуществлено соединение, пересылаем данные с помощью функции fwrite(<идентификатор файла>, <переменная>, <тип данных>).
Например, имеем данные типа double

Сперва передадим размер данных, которые собираемся передать. Передавать будем 10 значений типа double, поетому передадим 80 байт.
В дальнейшем на стороне сервера получим размер, и выделим динамично память под необходимое количество данных.

Далее передаем массив данных, закрываем, и очищаем объект
Как видно, у Матлаба все довольно просто :) .

Сторона сервера.
За основу сервера взяты исходники программы на сайте gametutorials. Описание как создавать сокет в режиме прослушивания есть там же. Остановимся лишь на приеме данных от клиента.
После принятия подключения с помощью функции accept, которая возвращает сокет клиента, можем принимать данные от клиента.
Так как мы не знаем сколько точно данных пошлет нам клиент, то мы сначала принимаем размер ожидаемых данных с помощью функции recv(), которая в качестве третьего аргумента принимает количество байт для чтения.

Далее идет важный момент. Порядок байт в числах, представление которых занимает более одного байта, может быть для разных компьютеров различное. Есть вычислительные системы, в которых старший байт числа имеет меньший адрес, чем младший байт (big-endian byte order), а есть вычислительные системы, в которых старший байт числа имеет больший адрес, чем младший байт (little-endian byte order). При передаче числовой информации от машины, имеющей один порядок байт, к машине с другим порядком байт можно неправильно истолковать принятую информацию. Для того чтобы этого не произошло, было введено понятие сетевого порядка байт (network byte order), т.е. порядка байт, в котором должна представляться числовая информация в процессе передачи ее по сети (на самом деле – это big-endian byte order). Числовые данные из представления, принятого у отправителя (у MATLAB по умолчанию порядок байтов big-endian byte order), переводятся в сетевой порядок байт. Числа в таком виде путешествуют по сети и переводятся в нужный порядок байт на машине-получателе (взято здесь). Для перевода чисел из машинного представления в сетевое и обратно в С/С++ используются функции: htons(), htonl(), htond(), ntohs(), ntohl(), ntohd(). Если потребуется настроить порядок байтов объекта TCP/IP в MATLAB, то необходимо поменять его свойство ByteOrder (t.ByteOrder = littleEndian).

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

messageSize = ntohl(messageSize);

Теперь выделяем память под буфер, принимаем данные, настраиваем порядок байт, и показываем данные на консоль.
Вот и все! Мы приняли данные переменной длины типа double.

Исходники:
Сервер (С++)      - скачать.
Клиент (Matlab) - скачать.


Представление двоичного числа с помощью Matlab и библиотеки <bitset> в С++

Пока разбирался как реализовать эту задачу, увидел некоторую особенность. Последовательность байт числа double на клиенте не совпадает с последовательностью байт на сервере. 

В среде MatLab было создано число типа double, и показано на консоль его битовое представление.


Само число в Workspace Матлаба имеет вид:  0.025130095443337.
Теперь передаю его на сервер, написанный на С++, c использованием протокола tcp/ip функцией fwrite(t, data, 'double'). Принимаю, и с помощью библиотеки <bitset> вывожу на консоль битовое представление этих данных. На консоле следующее

Как видно из полученных результатов отличаются последние 12 бит. 
Используя преобразование из двоичной системы в double на сайте binaryconvert.com, было получено два соответствующих этим битовым представлениям числа
2.51300954433375522967253345996E-2 
и
2.51300954433374794383393435737E-2 
Тринадцать знаков после точки совпадают. Если учесть степень 10-2, то совпадают все 15 знаков. Тоесть данные передаются корректно. Но почему передаются и принимаются разные битовые последовательности???

Немає коментарів:

Дописати коментар