Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: "Слоеный" протокол
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Misile_Inc
Здравствуйте! Есть протокол из трех уровней:

1. Физический (physical) - RS-232;
2. Канальный (data link);
7. Прикладной (application);

Уровни 2 и 7 имеют свои механизмы контроля ошибок (CRC, контрольная сумма), механизмы адресации и подтверждений.

Если с физическим уровнем все ясно, то остальные 2 уровня заставили меня задуматься.

Как вы считаете, как наиболее "Красиво" и "Правильно" организовать "Заворачивание" данных сначала в один протокол (Уровень 7), затем другой (Уровень 2) на языке C++ ?

Если не затруднит- с примерами / pattern.

Спасибо!
Ruslan1
Цитата(Misile_Inc @ Jul 24 2012, 17:25) *
Как вы считаете, как наиболее "Красиво" и "Правильно" организовать "Заворачивание" данных сначала в один протокол (Уровень 7), затем другой (Уровень 2) на языке C++ ?

Ну например, (сугубо мое мнение) критерий красивости-правильности:
1. оно работает. надежно и на всем множестве оговоренных сочетаний железо-операционка-программа
2. Программист "со стороны" может рассказать, что и как сделано (понял зависимость между модулями программы) и согласен внести изменения в код (для небольшие косметические изменения) после одного часа ознакомления с исходными текстами.

А будете Вы делать цикл через for(), do() или while() - это неважно.
Misile_Inc
Ruslan1, я старался поставить вопрос несколько глубже: С++ в отличие от С поддерживает объектно-ориентированный стиль программирования. Возможно, в рамках идеологии ООП будет правильнее определить объекты, с помощью которых (над которыми) выполняются все операции.
Например, объект самого пакета для каждого уровня программирования.
А for(), do() или while() - это из области процедурного программирования. По идее, такие детали должны скрываться от пользователя в методах класса.
kolobok0
Цитата(Misile_Inc @ Jul 24 2012, 19:48) *
...в рамках идеологии ООП будет правильнее определить объекты, с помощью которых (над которыми) выполняются все операции...


прелесть ООП так-же состоит в том, что сущности(а после них и объекты) определяются исходя из задачи. Если Вам требуется поднять TCP/IP протокол - это одно решение. Если MODBUS - это второе..если что нить аля своё - третье... всё зависит от задачи. конкретной. а решать сферического коня в вакууме можно конечно же. Но внося или сразу оговаривая грани, за которые ходить табу. тем самым вырисовывая чего хочется, или чего можется в зависимости от опыта, виденья, ресурсов и т.д...
Misile_Inc
Все же, мне кажется, коня не такого уж и сферического я нарисовал: задача достаточно типовая- сплошь и рядом имеем связи между устройствами через несколько протоколов из модели OSI. Канальный уровень обеспечивает доставку сообщений и контроль целостности, уровень прикладной занимается решением самой задачи взаимодействия.
Дело лишь в том, что раньше платформы, с которыми я имел дело, не имели сред разработки с поддержкой ООП.
AlexandrY
Цитата(kolobok0 @ Jul 24 2012, 18:56) *
прелесть ООП так-же состоит в том, что сущности(а после них и объекты) определяются исходя из задачи. Если Вам требуется поднять TCP/IP протокол - это одно решение. Если MODBUS - это второе..если что нить аля своё - третье... всё зависит от задачи. конкретной. а решать сферического коня в вакууме можно конечно же. Но внося или сразу оговаривая грани, за которые ходить табу. тем самым вырисовывая чего хочется, или чего можется в зависимости от опыта, виденья, ресурсов и т.д...


Делать из пакета объект довольно оригинально.
Но если подумать не очень умно.
Протоколам и так достаточно работы по формированию полей пакета, выделению памяти, контроля целостностии т.д.
А тут еще понадобится дополнительная работа по поддержанию актуальных переменных со свойствами пакета, членов класса.
Сразу встает проблема синхронизации,т.е. безконфликтные запись объектом уровня и чтение приложением при проходе пакетов через уровни OSI.
Если же у пакетов не будет свойств, то зачем из них делать объекты?

Думаю для RS232 достаточно сделать объект порта и объект подключения.
В объект подключения передавать объект фабрики пакетов.
XVR
В такой задаче нужно отталкиваться не от ООП принципов, а от самих протоколов и форматов. Если каждый уровень просто 'заворачивает' данные предыдущего уровня в дополнительную обертку, то можно воспользоваться парадигмой объекта - пакета, который может расширяться с 2х сторон обработчиками протокола следующего уровня (это делается просто заказом буфера достаточной длинны и заполнением его начиная с середины).
Каждый уровень протокола так же представлен объектом, из которых выстоен стек. В стеке каждый объект-протокол принимает экземпляр объекта-пакета, добавляет к нему свой пролог и эпилог и передает по стеку дальше

(Приблизительно так сделан стек TCP/IP в Linux'е, если я правильно помню)
AlexandrY
Цитата(XVR @ Jul 25 2012, 10:10) *
В стеке каждый объект-протокол принимает экземпляр объекта-пакета, добавляет к нему свой пролог и эпилог и передает по стеку дальше

(Приблизительно так сделан стек TCP/IP в Linux'е, если я правильно помню)


А разве стек TCP/IP линукса сделан на C++?
Да, и какие вы свойства и методы припишите объекту пакета?
Метод послать самого себя? Или свойство дошел или не дошел? И зачем это нужно будет уровням, которые за этим вообще не следят?

В стеке TCP нет понятия пакета внутри реализации. Там есть несколько видов очередей.
Элементами тех очередей в свою очередь являются списки. А вот те списки уже описывают элементы пакета.
Т.е. пакеты могут никогда не представляться одной непрерывной областью памяти.
На физическом уровне пакеты посылаются DMA по связным спискам.
Но хуже того, каждый уровень может оставить в очереди копию фрагмента пакета для последующего быстрого переповтора. Один фрагмент может быть потом приписан нескольким пакетам.
Т.е. если создавать объекты пакетов, то возникнут неявные связи между этими пакетами, что вообще все ООП коту под хвост пошлет.
Вообщем для стека вроде TCP объекты пакетов лишь запутают реализацию.

XVR
Цитата(AlexandrY @ Jul 25 2012, 11:57) *
А разве стек TCP/IP линукса сделан на C++?
Нет, но и на С можно писать в ООП стиле.
Цитата
Да, и какие вы свойства и методы припишите объекту пакета?
Получить доступ к содержимому. Добавить данные в начало. Добавить данные в конец.
Можно взять готовый контейнер - std::deque<char>. Он вполне соотвествует пакету (хотя и несколько избыточен из за своей универсальности)

Цитата
В стеке TCP нет понятия пакета внутри реализации.
Там есть нечто похожее - struct sk_buff с указателями
* @head: Head of buffer
* @data: Data head pointer
* @tail: Tail pointer
* @end: End pointer
data-tail - содержит собственно тело пакета
head-data - место для заголовков, которые могут добавить уровни протокола
и tail-end - место для добавления суфиксов опять же от обработчиков уровней протокола

Цитата
Там есть несколько видов очередей.
Есть, и немало rolleyes.gif
Цитата
Элементами тех очередей в свою очередь являются списки. А вот те списки уже описывают элементы пакета.
Т.е. пакеты могут никогда не представляться одной непрерывной областью памяти.
Пакеты - слишком общее слово. Они бывают самые разные. Те, что в sk_buff - то же пакеты, я имел в виду именно их

Цитата
Вообщем для стека вроде TCP объекты пакетов лишь запутают реализацию.
Я не утверждал, что весь стек TCP построен на таких пакетах, я просто упомянул где я их видел biggrin.gif

А вот подойдет ли этот метод ТС - это вопрос к нему. И он сильно зависит от собственно протоколов, которые ему надо реализовывать (это я тоже писал)
AlexandrY
Цитата(XVR @ Jul 25 2012, 11:45) *
Получить доступ к содержимому. Добавить данные в начало. Добавить данные в конец.
Можно взять готовый контейнер - std::deque<char>. Он вполне соотвествует пакету (хотя и несколько избыточен из за своей универсальности)


Данные не добавляются в начало/конец, вместо этого пересобираются списки описывающие пакет.
При этом элементы этих списков могут жить независимо от конечных собранных пакетов. Скажем, чтобы не пересчитывать CRC заголовков.
У пакетов может и середина на ходу подменяться, скажем при шифрации в тоннелях.
Ну и где, в каких объектах вы будете хранить только заголовки или исходные незашифрованные фрагменты еще не назначенные пакетам?

XVR
Цитата(AlexandrY @ Jul 25 2012, 13:10) *
Данные не добавляются в начало/конец, вместо этого пересобираются списки описывающие пакет.
В sk_buff именно добавляются. Сами экземпляры sk_buff действительно собираются в списки, но это уже следующий уровень иерархии.
Цитата
У пакетов может и середина на ходу подменяться, скажем при шифрации в тоннелях.
Может
Цитата
Ну и где, в каких объектах вы будете хранить только заголовки или исходные незашифрованные фрагменты еще не назначенные пакетам?
Так, еще раз:
Мое предложение касается ТОЛЬКО случаев, когда каждый уровень протокола только добавляет свои заголовки и хвосты к данным, полученным из предыдущего уровня. Это сильно ограничивает применимость такого подхода, но все же имеет право на жизнь (может у ТС как раз такой стек протоколов).

Пример - UDP через Ethernet (без поддержки фрагментирования).

Объект, описывающий буфер, содержит память на 1 максимальный Ethernet пакет (около 1.5К). Сначала в него пишут пользовательские данные. Потом уровень UDP пишет заголовок UDP и CRC (не помню, в начале оно или в конце). Потом IP добавляет свой заголовок, потом Ethernet добавляет Ethernet заголовок и Frame CRC. Все, что получилось, отправляется в контролер.

Класс буфера будет иметь такой интерфейс:
Код
class Buffer {
public:

void* get_image(size_t &size); // Return accumulated so far image
void push_front(const void* image, size_t size); // Prepend data in buffer with 'image'
void push_back(const void* image, size_t size); // Append 'image' to data in buffer
};


Описание стека будет выглядеть так:
Код
class StackItem {
StackItem* next;
public:
StackItem(StackItem* n=NULL) : next(n) {}

virtual void process(Buffer&) =0;
void call_next(Buffer& b) {next->process(b);}
};


class UDP : public StackItem {
public:
UDP(StackItem* n) : StackItem(n) {}

virtual void process(Buffer& b)
  {
   b.push_front( ... UDP header ...);
   b.push_back( ... CRC ... );
   call_next(b);
  }
};

class IP ...
class EthPkt ...

class EthernetMAC : public StackItem {
public:

virtual void process(Buffer& b)
  {
   EthernetSendBuffer(b.get_image()):
  }
};

void main()
{
  EthernetMAC e_mac;
  EthPkt e_pkt(&e_mac);
  IP ip(&e_pkt);
  UDP udp(&ip);

  Buffer b;
  b.push_back(...); // Data to send
  udp.process(b);
}
AlexandrY
Цитата(XVR @ Jul 26 2012, 10:12) *
Мое предложение касается ТОЛЬКО случаев, когда каждый уровень протокола только добавляет свои заголовки и хвосты к данным, полученным из предыдущего уровня. Это сильно ограничивает применимость такого подхода, но все же имеет право на жизнь (может у ТС как раз такой стек протоколов).

Пример - UDP через Ethernet (без поддержки фрагментирования).

Объект, описывающий буфер, содержит память на 1 максимальный Ethernet пакет (около 1.5К). Сначала в него пишут пользовательские данные. Потом уровень UDP пишет заголовок UDP и CRC (не помню, в начале оно или в конце). Потом IP добавляет свой заголовок, потом Ethernet добавляет Ethernet заголовок и Frame CRC. Все, что получилось, отправляется в контролер.


Что то вы запутались.
Говорите, что заголовки добавляет уровень протокола, а на самом деле метод реализуете в объекте пакета.
Получается объект пакета реализует весь протокол, тогда зачем уровни OSI?
Сразу пишите Packet.Send и придете к логическому концу. wink.gif

Здесь вообще проявляется ошибочность мнения, что на C можно писать в стиле ООП.
Если бы писали на С-и, то действительно мелочь, кто добавляет заголовки, объект пакета или объект уровня. Перенес процедуру из одного модуля в другой и забыл.

В ООП же если вы пригрузили объект пакета несвойственной функциональностью, то вам придется впоследствии очень много переписывать.
Т.е. в реальном ООП ошибка модели объектов дорого стоит.
А в C-и она не стоит ничего, ибо это все умозрительные построения. За что я и люблю С-и wink.gif
XVR
Цитата(AlexandrY @ Jul 26 2012, 12:27) *
Говорите, что заголовки добавляет уровень протокола,
Да
Цитата
а на самом деле метод реализуете в объекте пакета.
Нет. В объекте пакета реализованна функциональность по добавлению произвольных данных, а собственно данные поставляет объект из стека протоколов
Цитата
Получается объект пакета реализует весь протокол, тогда зачем уровни OSI?
Пакет ничего о протоколах не знает
Цитата
Сразу пишите Packet.Send и придете к логическому концу. wink.gif
И метода Send у пакета нет.
Цитата
Здесь вообще проявляется ошибочность мнения, что на C можно писать в стиле ООП.
Можно, и пишут
Цитата
В ООП же если вы пригрузили объект пакета несвойственной функциональностью, то вам придется впоследствии очень много переписывать.
Я же написал пример интерфейсов. Где там 'несвойственной функциональность' у объекта пакета?

Цитата
Т.е. в реальном ООП ошибка модели объектов дорого стоит.
Это да
Цитата
А в C-и она не стоит ничего, ибо это все умозрительные построения.

Угу, там где в С++ приходится переписывать реализации классов, в С приходится писать программу заново, т.к. после исправлений в стиле 'умозрительные построения' часто концов не может найти сам автор wacko.gif
sasamy
Цитата(AlexandrY @ Jul 26 2012, 12:27) *
Здесь вообще проявляется ошибочность мнения, что на C можно писать в стиле ООП.


а как же модели драйверов, VFS и пр, или например
http://en.wikipedia.org/wiki/GLib
AlexandrY
Цитата(XVR @ Jul 26 2012, 12:53) *
Нет. В объекте пакета реализованна функциональность по добавлению произвольных данных, а собственно данные поставляет объект из стека протоколов


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

Но изменение в пакетах некоторых атрибутов в теле тоже широко распространено. Например номер пакета при ретрансмитах или изменение приоритета для продвижения в очереди на передачу. Почему нет метода push_middle ?

Потом, понятие пакета появляется только на некотором низком уровне протоколов. А так вообще пользователь работает с потоком или, например, со списками пар [имя]=[значение]. Навязывание пакета как изначальной единицы обмена сильно ограничивает функциональные возможности протокола.
Пакетизация реализоваться должна по необходимости где-то на внутренних уровнях. А до того данные должны идти единой очередью.
Опять же имеет значение синхронный или асинхронный способ взаимодейстивя со стеком предусматривается.
kolobok0
Цитата(AlexandrY @ Jul 24 2012, 20:59) *
...Делать из пакета объект довольно оригинально. ...


это Ваше виденье задачи... наверное оно идеально.... для вас...

для тех кто идеально не читал - повторюсь...

исходя из задачи.
пока лично я НЕ вижу задачи самой. Вижу абстрактные рассуждения о правильности использование плазменной пушки при выборах в губернаторы. Если всё и без неё прокатит - то нафига её тщить? А если есть прямая выгода - то да надо юзать...

а теоретические изыски - это я пас... я больше практик...
XVR
Цитата(AlexandrY @ Jul 26 2012, 15:05) *
Ну да, за многоточиями в ваших аргументах можно спрятать любой смысл.
Но что-то много многоточий.
Заметьте, что все многоточия собранны в обработчиках уровней протокола, а не в самом объекте пакета (class Buffer)
Цитата
Это говорит от том, что объект-пакет изначально задумывался вами как протокольно зависимый, а... ?
Неа, в объекте-пакете (Buffer) не одной точки нету, не то что многоточий rolleyes.gif

Цитата
Но изменение в пакетах некоторых атрибутов в теле тоже широко распространено. Например номер пакета при ретрансмитах или изменение приоритета для продвижения в очереди на передачу. Почему нет метода push_middle ?
А я об этом сразу говорил, вот цитата -

Цитата
Если каждый уровень просто 'заворачивает' данные предыдущего уровня в дополнительную обертку, то можно воспользоваться парадигмой объекта - пакета, который может расширяться с 2х сторон обработчиками протокола следующего уровня (это делается просто заказом буфера достаточной длинны и заполнением его начиная с середины).

И еще одна
Цитата
Мое предложение касается ТОЛЬКО случаев, когда каждый уровень протокола только добавляет свои заголовки и хвосты к данным, полученным из предыдущего уровня. Это сильно ограничивает применимость такого подхода, но все же имеет право на жизнь (может у ТС как раз такой стек протоколов).


Цитата
Потом, понятие пакета появляется только на некотором низком уровне протоколов. А так вообще пользователь работает с потоком или, например, со списками пар [имя]=[значение].
Полностью согласен
Цитата
Навязывание пакета как изначальной единицы обмена сильно ограничивает функциональные возможности протокола.
Так я и не навязываю, я лишь предложил нечто, весьма ограниченное по функциональности, но простое. А уж ТС решать - хватит ему этой функциональности или нет biggrin.gif

Misile_Inc
Спасибо, господа sm.gif Вашу дискуссию чрезвычайно приятно было наблюдать.
Я сделал из нее некоторые выводы и буду на основе этих выводов решать задачу.
Уточню, что действительно "каждый уровень протокола только добавляет свои заголовки и хвосты к данным".
Еще раз спасибо!
TigerSHARC
Цитата(XVR @ Jul 25 2012, 12:45) *
Нет, но и на С можно писать в ООП стиле.


Можно подробнее про программирование на С в ООП стиле? желательно с примером
sasamy
Цитата(TigerSHARC @ Jul 29 2012, 19:31) *
Можно подробнее про программирование на С в ООП стиле? желательно с примером


Предыдущую страницу слобо посмотреть ? пример
http://prex.sourceforge.net/src/S/252.html#L48
TigerSHARC
Цитата(sasamy @ Jul 29 2012, 20:23) *
Предыдущую страницу слобо посмотреть ? пример
http://prex.sourceforge.net/src/S/252.html#L48

спасибо. Не могли бы вы немного объяснить в чём приемущества такого подхода(когда работаем с объектами на С)? пока не понятна сама суть. Просто все объекты кода описываются как структуры
AlexandrY
Цитата(TigerSHARC @ Jul 29 2012, 22:12) *
спасибо. Не могли бы вы немного объяснить в чём приемущества такого подхода(когда работаем с объектами на С)? пока не понятна сама суть. Просто все объекты кода описываются как структуры


Приведен избитый пример структуры базового драйвера. Написан он так не потому, что хотели изобразить в стиле ООП, а потому что иначе не написать драйвер который может динамически подключаться к прикладным уровням.
Например к файловой системе может быть подключен драйвер NAND Flash, а потом отключен и подключен драйвер SD карты.
Но это показуха, поскольку чуть меняется тип периферии скажем нужен драйвер мультиплексированного канала к внешнему вспомогательному контроллеру, как такая структура драйвера уже не подходит и нужно рядом сочинять другой.
Т.е. полное отсутствие полиморфизма.
Другая проблема в том, что самые интересные функции драйвера скрыты в функциях ioctl и devctl.
Там вообще творится беспредел. Что точно эти функции делают можно понять исключительно просканировав все их исходники сверху донизу.
Это противоположность принципу наследования.
В нормальном ООП подходе места таким функциям нет. Вместо этого должно было быть наследование абстрактных методов от базового драйвера и дополнение уникальными методами.
Вместо этого же имеем ioctl с десятками (если не сотнями) типов аргументов. Браузеры кода можно выкинуть, это самые скверные места в операционках.
И после этого любители линукса говорят, что что-то могут сделать за пять минут. Ну-ну!
sasamy
Цитата(AlexandrY @ Jul 30 2012, 00:15) *
В нормальном ООП подходе места таким функциям нет. Вместо этого должно было быть наследование абстрактных методов от базового драйвера и дополнение уникальными методами.


И на каждый такой уникальный метод создавать новый системный вызов ? ггг - типичный такой индусский подход sm.gif как все же прекрасно что не все болеют ООП головного мозга.
Misile_Inc
Может есть какая - нибудь книга, где бы автор рассматривал подходы к программированию протоколов?
А то каждый раз что-нибудь свое изобретаю.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.