Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Работа с сетью в Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 03.01.2015, 18:38   #1
Antony41
Пользователь
 
Аватар для Antony41
 
Регистрация: 20.03.2009
Сообщений: 99
По умолчанию работа с чужим протоколом передачи данных

Помогите пожалуста господа. Голову ломаю уже 3 день.
Есть чужой протокол передачи данных. Необходимо написать сервер который принимает эти данные.

Вот небольшое описание протокола
Первые 4 байта кол-во передаваемых пакетов - Integer,
Затем 1 байт длина строки устройства - Byte
Далее N(длина строки устройства) байт - id устройства - строка (может быть разной длины)
Далее тип оборудования 1 байт - Byte
Короче и тд.

Примечание – при передаче нулевого количества пакетов пункт 2 пропускается, принимающая сторона сразу отвечает 0х55. (тоесть я должен отправить подтверждение)

Таймауты
Принимающая сторона должна ответить передающей стороне подтверждением 0х55 не позднее 5 секунд, после приема пакетов. Если принимающая сторона не успевает отправить подтверждение, то передающая сторона разорвет соединение и пошлет пакет заново.

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

Пример передачи
Передающая сторона: 00 00 00 00 //число пакетов – 0
Принимающая сторона: 55 // подтверждение приема, пропуск пункта 2

Передающая сторона: 01 00 00 00 //число пакетов – 1
Передающая сторона: 05 33 30 31 31 32 db 00 00 00 00 4b 71 00 e7 bc cd 08 2b 98 0a 42 89 06 66 42 33 33 97 42 41 01 b2 d0 ac 00 12 00 05 00 ca 00 4a 32 34 30 31 30 34 30 30 30 30 30 32 32 33 30 31 30 34 30 30 30 30 30 30 36 37 30 31 30 34 30 30 30 30 30 33 30 30 30 31 30 34 30 30 30 30 30 32 30 30 30 31 30 34 30 30 30 30 30 30 30 30 30 31 30 34 30 30 30 30 30 30 38 42

Принимающая сторона: 55 // подтверждение приема

Вот в чем вопрос
1. Сервер передачи может отправить сразу несколько пакетов например
F4 01 00 00 .............................. Что равно 500 пакетам за один раз передачи нам.

2.Между пакетами нет разделителей

3.При установлении соединения передающая сторона не отключается и при подтверждении шлет следующую кучу пакетов. Когда пакеты кончаются шлет просто 00 00 00 00 = 0 пакетов, а мы подтверждаем тем самым содинение установлено даже когда "не чего принимать"

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

Я пологаю нужно сделать так: принимаем порцию данных пишем в буфер
отправляем буфер в функцию обработки пакета, функция обработки видит что в буфере должен быть 1 пакет данных, далее обрабатывает буфер данных до позиции в которой этот пакет заканчивается (это можно определить если читать байты по порядку). или возвращает результат о том что данных не достаточно и следует буфер дописать следующую порцию данных. Далее буфер снова попадает в функцию обработки и обрабатывается весь пакет, и функция возвращает результат о том что данные в буфере нужно сместить, или удалить те данные которые обработаны, а если есть новые сместить их в начало.(хотя я считаю что новых данных быть не должно пока я не отправлю подтверждение 0x55) Жесть...

Что посоветуете я вобще правильно мыслю?
Antony41 вне форума Ответить с цитированием
Старый 03.01.2015, 19:09   #2
Streletz
Старожил
 
Регистрация: 03.01.2014
Сообщений: 2,870
По умолчанию

Цитата:
Сообщение от Antony41 Посмотреть сообщение
Между пакетами нет разделителей
А, размер пакета в протоколе указан? Может быть для указания размера предусмотрен особый фрагмент в "заголовке"?
Streletz вне форума Ответить с цитированием
Старый 03.01.2015, 19:17   #3
LISTAT
Пользователь
 
Регистрация: 27.10.2011
Сообщений: 50
По умолчанию

В моем понимании в любом случае должен быть указан размер принимаемого пакета. Иначе неразбериха в момент приема будет.
LISTAT вне форума Ответить с цитированием
Старый 03.01.2015, 19:32   #4
Antony41
Пользователь
 
Аватар для Antony41
 
Регистрация: 20.03.2009
Сообщений: 99
По умолчанию

Цитата:
Сообщение от Streletz Посмотреть сообщение
А, размер пакета в протоколе указан? Может быть для указания размера предусмотрен особый фрагмент в "заголовке"?
нет в заголовке только количество пакетов, более того в конце пакета есть данные которых может быть N количество

Например:
4a – поле data длиной 74 байта (может быть и не 74)

24 010400000223 010400000067 010400000300 010400000200 010400000000 01040000008B

Расшифровываем
24 - это длина строки = 36 байт, 01 - это тип датчика, 04 длина строки

Длина строки 36 байт, 6 датчиков, все типа 1, длиной 4 байта.

Показания датчика 0 – 547
Показания датчика 1 – 103
Показания датчика 2 – 768
Показания датчика 3 – 512
Показания датчика 4 – 0
Показания датчика 5 – 139

Получается что пакеты могут быть разной длины
Antony41 вне форума Ответить с цитированием
Старый 03.01.2015, 19:37   #5
Antony41
Пользователь
 
Аватар для Antony41
 
Регистрация: 20.03.2009
Сообщений: 99
По умолчанию

Цитата:
Сообщение от LISTAT Посмотреть сообщение
В моем понимании в любом случае должен быть указан размер принимаемого пакета. Иначе неразбериха в момент приема будет.
Я тоже так считаю, но следуя данным мануала, получается что их ни где не указывают, и пакеты могут быть разной длины. Указывают лишь кол-во пакетов которые отправлены передающей стороной за один раз.

Вот полное описание протокола.

ну что есть какие идеи?

вот так реализовал прием пакета
Код:
  // Анализируем, какое событие произошло
  case Msg.SockEvent of
    FD_READ: //Если к нам ломятся данные
      begin
        Logging('FD_READ - установка длины буфера');
        if Connection.BytesLeft = 0 then //Если до этого все данные были приняты
          begin
            //ProcessingData(Buf);
            //Получаем длину новой порции данных в BytesLeft
            ioctlsocket(Connection.ClientSocket, FIONREAD, Longint(Connection.BytesLeft));
            Connection.Offset := 0; //Говорим что сейчас прочитано 0 байт
            //Далее необходимо выделить буфер для приема данных, но так как функция обработки пакета нам может вернуть
            //не полный пакет, то его необходимо будет дописать новой порцией данных и снова отправить фуекции
            //Так что тут мы смотрим что нам вернула функция обработки пакета
            //Если функция отработала данные, то принимаем новые данные в начало буфера
            if ResultProcessingData = rpDataRecvDone then
              begin
                Logging('Функция отработала пакет, установка новой длины буфера '+IntToStr(Connection.BytesLeft));
                SetLength(Connection.rBuff, Connection.BytesLeft);
              end
            else
            if ResultProcessingData = rpGetMoreData then //Если функция требует еще данных то дописываем
            begin
              Logging('Функция требует еще данных, размер буфера будет увеличен на '+IntToStr(Connection.BytesLeft));
              SetLength(Connection.rBuff, Length(Connection.rBuff)+Connection.BytesLeft);
              Logging('Буфер увеличен и сейчас его размер '+IntToStr(Length(Connection.rBuff)));
            end;
          end;

          begin
            Logging('FD_READ - Прием данных в буфер...');
            //После того как размер в ByteLeft установлен и буфер расширен или обнулен, мы попадаем сюда и будем
            //попадать снова и снова пока очередная порция данных не будет принята полностью хотя будет принята она за 1 раз
            //На этом этапе мы должны принять данные в буфер, опять же в начало или дописать его.
            RecvLen := recv(Connection.ClientSocket, Connection.rBuff[Connection.Offset], Connection.BytesLeft, 0);
            if RecvLen > 0 then
              begin
                // Если RecvLen > 0, это означает, что получено Res байт.
                // Соответственно, увеличиваем на RecvLen количество прочитанных
                // на данном этапе байт и на такую же величину уменьшаем
                // количество оставшихся.
                Inc(Connection.Offset, RecvLen);
                Dec(Connection.BytesLeft, RecvLen);
                if Connection.BytesLeft = 0 then
                //На этом этапе очередная порция данных принята, и пакет можно отправить на обработку
                  begin
                    ProcessingData(Connection.rBuff);
                    //Connection.Offset количество прочитанных байт тут можно не сбрасывать так как
                    //в начале оно всё равно будет обнулено
                  end;
              end
            else
              if RecvLen = 0 then
                begin
                  logging('Клиент ' + Connection.ClientAddr + ' закрыл соединение');
                  RemoveConnection;
                  Exit;
                end
              else
                if RecvLen = INVALID_SOCKET then
                  begin
                    logging('Ошибка при получении данных от клиента ' + Connection.ClientAddr);
                    RemoveConnection;
                    Exit;
                  end
                else
                // Ошибку WSAEWOULDBLOCK игнорируем, т.к. она говорит
                // только о том, что входной буфер сокета пуст, но в целом
                // всё в порядке - такое вполне возможно при ложных
                // срабатываниях сообщения
                if WSAGetLastError <> WSAEWOULDBLOCK then
                begin
                  logging('Ошибка при получении данных от клиента ' +
                    Connection.ClientAddr + ': ' + GetErrorstring);
                  RemoveConnection;
                  Exit;
                end;
          end;
      end;
Вложения
Тип файла: zip Протокол ScoutOpen.zip (25.3 Кб, 22 просмотров)

Последний раз редактировалось Stilet; 08.01.2015 в 07:41.
Antony41 вне форума Ответить с цитированием
Старый 03.01.2015, 22:54   #6
Streletz
Старожил
 
Регистрация: 03.01.2014
Сообщений: 2,870
По умолчанию

Из этого примера
Цитата:
Сообщение от Antony41 Посмотреть сообщение
010400000223
следует, что длина сроки это и есть размер пакета, а дина строки, указанная в самом начале
Цитата:
Сообщение от Antony41 Посмотреть сообщение
24
это суммарный размер всего сообщения.
Полагаю, на это уже можно опереться.
Streletz вне форума Ответить с цитированием
Старый 03.01.2015, 23:06   #7
Antony41
Пользователь
 
Аватар для Antony41
 
Регистрация: 20.03.2009
Сообщений: 99
По умолчанию

Цитата:
Сообщение от Streletz Посмотреть сообщение
Полагаю, на это уже можно опереться.
Это для поля Data вот с ним всё правильно организовано но
Data – поле дополнительных данных. Создается при необходимости передать значение больше 2 аналоговых входов. В случае отсутствия в поле Datalen передается 0.
передаваемый пакет состоит так
кол-во пакетов|длина следующей строки ID|ID строка|тип оборудования|дата и время|........|Длина поля Data|Data Поле

Вот результат выполнения функции тут 3 пакета разом ппц

FD_READ - установка длины буфера
Функция требует еще данных, размер буфера будет увеличен на 348
Буфер увеличен и сейчас его размер 348
FD_READ - Прием данных в буфер...
03 00 00 00 06 32 32 35 30 30 33 0E 00 00 00 80 7B C9 26 A8 F5 D1 08 62 D6 82 42 BD 78 78 42 00 00 00 00 3D 00 00 00 00 00 00 00 07 00 C8 00 47 32 10 D0 83 25 00 C2 98 00 00 00 70 D0 82 15 00 00 00 00 00 D0 A0 D0 82 0B 00 00 00 00 00 D0 B0 D0 82 03 00 D0 91 6A 00 00 D1 80 D0 82 03 00 C2 BB 10 00 00 D1 80 D0 8F 21 00 00 00 00 00 00 D0 83 13 00 00 00 00 00 06 32 32 35 30 30 33 0E 00 00 00 80 1E AB 38 A8 F5 D1 08 62 D6 82 42 BD 78 78 42 00 00 00 00 3D 00 00 00 00 00 00 00 07 00 C8 00 47 32 10 D0 83 25 00 C2 98 00 00 00 70 D0 82 15 00 00 00 00 00 D0 A0 D0 82 0B 00 00 00 00 00 D0 B0 D0 82 03 00 D0 91 6A 00 00 D1 80 D0 82 03 00 C2 BB 10 00 00 D1 80 D0 8F 21 00 00 00 00 00 00 D0 83 13 00 00 00 00 00 06 32 34 39 34 34 33 0E 00 00 00 80 2F 8E 2B A8 F5 D1 08 E6 25 7F 42 F3 94 7B 42 00 00 00 00 00 00 00 00 00 00 00 00 07 00 D0 00 46 32 00 D0 83 13 00 00 00 00 00 D0 A0 D0 82 0B 00 00 00 00 00 10 D0 83 25 00 3E 00 00 00 D0 B0 D0 82 03 00 D1 80 5F 00 00 D1 80 D0 82 03 00 D0 98 10 00 00 D1 80 D0 8F 21 00 00 00 00 00 40 D0 82 06 00 46 05 00 00
Сервер остановлен

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

Слушайте ребят помогите умоляю! уже запарился ей богу.
Привет! Попробовал реализовать передачу данных по протоколу ScoutOpen всё впринципе хорошо, но непонятно следующее:
Из приведенного вами примера описания протокола в базе знаний приводится пример передачи данных вот эта строка:
Цитата:
05 33 30 31 31 32 db 00 00 00 00 4b 71 00 e7 bc cd 08 2b 98 0a 42 89 06 66 42 33 33 97 42 41 01 b2 d0 ac 00 12 00 05 00 ca 00 4a 32 34 30 31 30 34 30 30 30 30 30 32 32 33 30 31 30 34 30 30 30 30 30 30 36 37 30 31 30 34 30 30 30 30 30 33 30 30 30 31 30 34 30 30 30 30 30 32 30 30 30 31 30 34 30 30 30 30 30 30 30 30 30 31 30 34 30 30 30 30 30 30 38 42
Из данного набора байт интересует только передача данных Data, тоесть вот этот кусок
Цитата:
4a 32 34 30 31 30 34 30 30 30 30 30 32 32 33 30 31 30 34 30 30 30 30 30 30 36 37 30 31 30 34 30 30 30 30 30 33 30 30 30 31 30 34 30 30 30 30 30 32 30 30 30 31 30 34 30 30 30 30 30 30 30 30 30 31 30 34 30 30 30 30 30 30 38 42
В описании так же говорится что первый байт длина поля Data тоесть получается 4a(hex)-->74(dec),
далее в описании говорится что остальное это hex-строка тоесть представляем байты в строковом виде это получается так
Цитата:
24 01 04 00000223 01 04 00000067 01 04 00000300 01 04 00000200 01 04 00000000 01 04 0000008B
Далее по описанию вопросов не возникло, но когда я пытаюсь принять данные самостоятельно получается что сервер мне передает слебующий hex набор данных (показано только поле Data):
Цитата:
46 - длина = 70 (всё ок)
32 10 D0 83 25 00 68 00 00 00
40 D0 82 06 00 4F 03 00 00 D0
A0 D0 82 0B 00 04 00 00 00 D1
80 D0 8F 21 00 00 00 00 00 D0
B0 D0 82 03 00 E2 84 A2 6E 00
00 D1 80 D0 82 03 00 06 11 00
00 00 D0 83 13 00 00 00 00 00
С длиной данных всё хорошо байты соответствуют указанной длине, но когда я начинаю представлять HEX в виде строки (как сказано в описании), то получается какая то хрень
Цитата:
2Ѓ%h@ЂOРЂрЏ!аЂ™nрЂЃ
что нужно сделать чтоб данные представились в нормальном виде.
ЗЫ
документация по работе с протоколом прикреплена в сообщении на 1 страницы данной темы.

Последний раз редактировалось Stilet; 08.01.2015 в 07:41.
Antony41 вне форума Ответить с цитированием
Старый 07.01.2015, 23:03   #8
Streletz
Старожил
 
Регистрация: 03.01.2014
Сообщений: 2,870
По умолчанию

Цитата:
Сообщение от Antony41 Посмотреть сообщение
когда я начинаю представлять HEX в виде строки (как сказано в описании), то получается какая то хрень
Цитата:
Цитата:
2Ѓ%h@ЂOРЂрЏ!аЂ™nрЂЃ
что нужно сделать чтоб данные представились в нормальном виде.
А, в какой кодировке передаётся строка? В какой версии Delphi Вы работаете (начиная с 2009 формат string в Юникоде)?
Streletz вне форума Ответить с цитированием
Старый 07.01.2015, 23:48   #9
Antony41
Пользователь
 
Аватар для Antony41
 
Регистрация: 20.03.2009
Сообщений: 99
По умолчанию

Цитата:
Сообщение от Streletz Посмотреть сообщение
А, в какой кодировке передаётся строка? В какой версии Delphi Вы работаете (начиная с 2009 формат string в Юникоде)?
Этого не сказано в мануале указано только что это тип string
и еще есть пример декодирования на C#

Код:
public List<ExtraDataItem> Parse(string hexString) 
{ 
    var paramNames = new[] { "Аналоговый вход ", "Счетный вход " }; 
    if (hexString.Length % 2 != 0) 
    hexString = hexString.Ins ert(0, "0"); 
    var result = new List<ExtraDataItem>(); 
    if (hexString.Length < 2) return result; 
    var numOfBytes = Convert.ToByte(hexString.Substring(0, 2), 16); 
     
    // номера аналогового и счетного входов 
    var aNum = 2; 
    var cNum = 0; 
    var i = 0; 
    while (i < numOfBytes) 
    { 
  // индекс в строке 
  var j = (i + 1) * 2; 
  var typeCode = (Convert.ToByte(hexString.Substring(j, 2))); 
  var type = (ExtraDataItemType) typeCode; 
  var paramName = typeCode < paramNames.Length ? paramNames[typeCode - 1] + (type == ExtraDataItemType.AnalogData ? aNum++ : cNum++): ""; 
  var recordBytes = Convert.ToByte(hexString.Substring(j + 2, 2)); 
  if (type == ExtraDataItemType.AnalogData || type == ExtraDataItemType.CountData) 
  { 
var value = recordBytes == 2 ? Convert.ToUInt16(hexString.Substring(j + 4, 4), 16) : recordBytes == 4 ? Convert.ToInt32(hexString.Substring(j + 4, : 0; 
            result.Add(new ExtraDataItem { Name = paramName, Type = type, Value = value }); 
  } 
  else 
return result; 
  i += (2 + recordBytes); 
    } 
    return result; 
}
версия XE5

Последний раз редактировалось Stilet; 08.01.2015 в 07:41.
Antony41 вне форума Ответить с цитированием
Старый 08.01.2015, 01:52   #10
WinCoder
Заблокирован
 
Регистрация: 24.11.2014
Сообщений: 721
По умолчанию

Передавайте пакеты не как строку, а как поток (TMemoryStream). Ещё советую попробовать вместо ClientSocket TWSocket от Overbyte. Там приём пакетов организован значительно проще. Не нужно вручную склеивать отдельные пакеты.
WinCoder вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Работа с протоколом ModBus (C++) Olegvarmy Помощь студентам 21 19.09.2017 23:22
Delphi работа с протоколом sla va-1 Работа с сетью в Delphi 1 07.05.2014 11:48
Работа с протоколом SIP в С++ Mazorrmo Общие вопросы C/C++ 1 21.09.2008 22:21
Работа с протоколом GameSpy Shouldercannon Работа с сетью в Delphi 8 08.03.2008 12:51
Работа с чужим приложением ERASERROR Win Api 2 28.01.2008 15:22