Opened 8 years ago

Closed 8 years ago

Last modified 7 years ago

#482 closed задача (invalid)

Алгоритм

Reported by: san Owned by: dimag
Priority: critical Milestone:
Component: ПО MC04-Dispatcher. Пульт диспетчера/техника Keywords:
Cc: alx

Description (last modified by san)

Описать алгоритм взаимодействия программы с ES.
(как отправляются команды, принимаются ответы, таймауты, и прочие особности работы)

Пока в алгоритме обнаружены следующие недостатки:

  1. Следуюшую команду на сервер нельзя отправлять не дождавшись ответа, а программа так делает
  2. Зачем ждать ивента 60мс, если и так всё складывается в очередь. Нет объяснения.
  3. Каков смысл отправки двух команд "проверки связи". Нет объяснения.

Change History (91)

comment:1 by dimag, 8 years ago

Прежде всего есть 2 потока: поток комманд и поток сообщений.
Работа потока команд организованна следующим образом:
1)Приходит сообщение по которому генерируется команда для FreeSwitch сервера.
2)формируется команда, которая отправляется через ESL на FreeSwitch сервер с помощью функции esl_send_recv, определённую из функции esl_send_recv_timed:
#define esl_send_recv(_handle, _cmd) esl_send_recv_timed(_handle, _cmd, 0)
где handle - идентификатор соединения, _cmd-команда, 0-время ожидая, означающее что ждать пока от FreeSwtich не поступит ответ о поставке переданной команды на обработку. Если не будет ответа от сервера, например, связь разорвана, то будет возвращена ошибка.
3)Если в сообщение требует сделать 2 и более команды, то снова формируется команда как пункте 2, пока все команды не будут отосланы.
4)Сообщение обработано, поток команд переходит в ожидание нового сообщения

Last edited 8 years ago by dimag (previous) (diff)

comment:2 by dimag, 8 years ago

Работа потока сообщений организованна следующим образом:
Таймер вызывает функцию в потоке для считывания команд с интервалом 1 раз в 50 миллисекунд.
1)Раз в пять секунд проверяем наличие связи с сервером, если связь есть:
Для этого:
Отправляем первый запрос bgapi status, ответ на него не смотрим, смотрим принята ли команда на FreeSwitch сервере к обработке. Запрос отправляется с помощью функции ниже:
esl_send_recv_timed(handle, request.c_str(), iDelay),
где handle - идентификатор соединения, _cmd-команда, iDelay-время ожидания, для данного равно 200, что означает, что ждать ответа 200 мс, если нет, то считать команду не выполненной.
Если первый запрос был получен FreeSwitch сервером, неудачен, то топравляем повторный запрос но со временем ожидания 600 мс, если и этот запрос неудачен, то считаем что соединения нет, выводим соответствующее сообщение, выходим
2) Если нет соединения, то каждую секунду пытаемся его восстановить. Если соединение успешно восстановилось, скрываем ошибку, обновляем информацию о текущих пользователях и конференциях. Если не восстановилось, то ожидаем в течение 1 секунды, делаем новую попытку соединения.
3)Если соединение есть, то считываем накопленные сообщения с помощью
esl_recv_event_timed(handle, 60, 1, NULL), где handle - идентификатор соединения, 60 - время ожидания ответа с сервером, 1 - возвращать последнее сохранённое сообщение, NULL - не сохранять сообщение в специально отведённой структуре, а сохранять в handle.
Если сообщение считано то пробуем считать другие с помощью той же функции, пока все сообщения не будут выбраны из очереди.
Далее приступаем к обработке сообщений.

Last edited 8 years ago by dimag (previous) (diff)

comment:3 by san, 8 years ago

Cc: alx added

comment:4 by san, 8 years ago

Уточняю comment:1 "Поток команд"
пункт 2) Что произойдёт если ответ от сервера не придёт ?

comment:5 by dimag, 8 years ago

Дополнительные разъяснения в тексте первых двух комментариев.

in reply to:  2 ; comment:6 by san, 8 years ago

comment:2

1 раз в 50 секунд.

  1. или 50раз в секунду?

ждать ответа 200 мс, если нет, то считать команду не выполненной.
топравляем повторный запрос но со временем ожидания 600 мс,

  1. Запросы, я так понимаю, идут друг за другом в рамках одного соединения?

Какой смысл в различных тамаутах?

  1. Есть ли вообще смысл в двух запросах "проверки соединения" подряд в рамках одного TCP-соединения? не тоже-самое будет, если я отправлю один запрос с длинным таймаутом?

если и этот запрос неудачен, то считаем что соединения нет, выводим соответствующее сообщение, выходим

  1. Соединение разрываем?

esl_recv_event_timed(handle, 60, 1, NULL)

  1. Я так понимаю что эта функция просмотрит сначала очередь сообщений, и если там пусто то будет 60мс ждать не придёт ли что от сервера. Так?

Алексей, обращаюсь отдельно к тебе, ответь пожалуста тоже, на мой вопрос №3.

in reply to:  1 comment:7 by san, 8 years ago

Replying to dimag:

Если не будет ответа от сервера, например, связь разорвана, то будет возвращена ошибка.

  1. Каков таймаут ожидания ответа от сервера в этом случае?
  2. Если соединение разорвано и вызов функции возвратит ошибку, какова реакция программы на эту ошибку?

comment:8 by dimag, 8 years ago

2)Для улучшения реагирования на команды пользователя
4)Пробуем создать новое ESL соединение, после успешного создания ESL соединения старое разрывается, во всех дальнейших запросах используется новое соединение
5)Функция ждёт новых сообщений в течение 60 мс, если сообщение есть, то возвращает его с результатом функции ESL_SCUESS, если нет сообщений и не появилось, то возвращает ESL_FAIL.
6)Интервал 0 - время ожидания определяет ESL
7)Ничего дальше не делаем, выходим из обработчика сообщений в потоке команд.

in reply to:  6 comment:9 by alx, 8 years ago

Replying to san:

  1. Есть ли вообще смысл в двух запросах "проверки соединения" подряд в рамках одного TCP-соединения? не тоже-самое будет, если я отправлю один запрос с длинным таймаутом?

Алексей, обращаюсь отдельно к тебе, ответь пожалуста тоже, на мой вопрос №3.

Я смысла в такой "двойной" проверке не нахожу. Из каких соображений применен такой алгоритм, не могу себе представить.

Более того, когда мы беседовали устно в Зале Совещаний, без ответа остался вопрос о том, допускает ли вообще протокол event-socket'а передавать новый запрос до того как получен ответ на предыдущий. Если протокол этого не допускает (как, например, протокол HTTP), то описанный алгоритм просто не соответствует спецификации протокола и, следовательно, не будет работать.

comment:10 by dimag, 8 years ago

В любой данный момент времени проводиться только один запрос к ESL. Вызов второго запроса блокируется с помощью мутексов. Все обращение к ESL у меня проводятся внутри мутекса.

in reply to:  8 comment:11 by san, 8 years ago

До мьютексов мы ещё не добрались
Цитирую тебя

Отправляем первый запрос ... ждать ответа 200 мс, если нет, то считать команду не выполненной... то топравляем повторный запрос

  1. Правильно я понимаю суть:
  • Посылаем запрос, если ответа нет более 200мс, то посылаем новый запрос.

????

Если да, то:

2)Для улучшения реагирования на команды пользователя

  1. Каким образом реагировани на команды пользователя улучшится, благодаря отправке двух сообщений подряд?
  2. Повторю вопрос Алексея: Допускает ли вообще протокол event-socket'а передавать новый запрос до того как получен ответ на предыдущий?

comment:12 by dimag, 8 years ago

9)Есть и вторая причина о которой я уже говорил, Wi-Fi - иногда бывают разрывы связи, которые длятся несколько сотен миллисекунд. Если такой разрыв придётся на момент вызова, то будет ложная индикация разрыва связи, для этого предусмотрен второй длинной по времени ожидания запрос.
10)В документации которая попалась мне об этом не было написанно, но в примерах запрос выполнялся вместе с мутексами, то есть, скорее всего, в данный текущий момент времени можно посылать только 1 запрос.

in reply to:  12 comment:13 by san, 8 years ago

Replying to dimag:

9)Есть и вторая причина о которой я уже говорил, Wi-Fi - иногда бывают разрывы связи, которые длятся несколько сотен миллисекунд. Если такой разрыв придётся на момент вызова, то будет ложная индикация разрыва связи, для этого предусмотрен второй длинной по времени ожидания запрос.

  1. Чем в этом случае два идущих подряд запроса 200+600мс будут лучше чем один запрос с таймаутом 800мс ?

comment:14 by san, 8 years ago

  1. Уточни, как всё-таки ты считаешь, можно передавать новый запрос до того как получен ответ на предыдущий или нет?

comment:15 by dimag, 8 years ago

Если в этот момент пользователь отправит какую-то команду, то могут быть задержки.

comment:16 by dimag, 8 years ago

9)Быстрее будет обрабатываться команды, так как если делать запрос с ожиданием 800 мс, то это может затормозить выполнение команды которую послал пользователь и получение сообщений, то есть получение сообщение может задержаться и программа станет обновлять состояние пользователей с заметной задержкой.

comment:17 by san, 8 years ago

  1. Если ответ на команду не приходит 800мс, следовательно и другие ивенты не придут,

так? тогда о каком обновлении статусов речь?

in reply to:  14 ; comment:18 by alx, 8 years ago

Replying to san:

  1. Уточни, как всё-таки ты считаешь, можно передавать новый запрос до того как получен ответ на предыдущий или нет?

Я попробую ответить исходя из логики. Ответ сервера на команду имеет такой формат:

Content-Type: api/response
Content-Length: xxx

bla-bla-bla

+OK

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

  1. Клиент отправляет серверу запрос 1.
  2. Сервер получает из сокета запрос 1 и приступает к его обработке.
  3. Возникает таймаут, esl_send_recv_timed() завершается с ошибкой.
  4. Клиент отправляет серверу запрос 2.
  5. Сервер закончил обработку запроса 1 и передал клиенту ответ на запрос 1.
  6. Сервер читает из сокета запрос 2 и приступает к его обработке.
  7. Клиент получил ответ на запрос 1, и трактует его как ответ на запрос 2. esl_send_recv_timed() завершается успешно.
  8. Клиент отправляет серверу следующий запрос 3.
  9. Сервер закончил выполнение запроса 2 и передал клиенту ответ на запрос 2.
  10. Клиент получает ответ на запрос 2 и трактует его как ответ на запрос 3.

... и так далее...

in reply to:  16 comment:19 by alx, 8 years ago

Replying to dimag:

если делать запрос с ожиданием 800 мс, то это может затормозить выполнение команды

Команда выполняется сервером. Это другой компьютер! Каким образом ваша программа может повлиять на время выполнения сервером команды???

comment:20 by dimag, 8 years ago

Если ответ не придёт в течение 800 мс, то можно считать что соединение отсутствует, но зачем такая большая задержка, лучше быстро проверить наличие соединения за 200 мс, потом, при отсутствие, которое может быть вызвано проблемами Wi-Fi или неполадками на проводной линии, ждать ответа в течение 600 мс.

in reply to:  18 ; comment:21 by dimag, 8 years ago

Replying to alx:

Replying to san:

  1. Уточни, как всё-таки ты считаешь, можно передавать новый запрос до того как получен ответ на предыдущий или нет?

Я попробую ответить исходя из логики. Ответ сервера на команду имеет такой формат:

Content-Type: api/response
Content-Length: xxx

bla-bla-bla

+OK

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

  1. Клиент отправляет серверу запрос 1.
  2. Сервер получает из сокета запрос 1 и приступает к его обработке.
  3. Возникает таймаут, esl_send_recv_timed() завершается с ошибкой.
  4. Клиент отправляет серверу запрос 2.
  5. Сервер закончил обработку запроса 1 и передал клиенту ответ на запрос 1.
  6. Сервер читает из сокета запрос 2 и приступает к его обработке.
  7. Клиент получил ответ на запрос 1, и трактует его как ответ на запрос 2. esl_send_recv_timed() завершается успешно.
  8. Клиент отправляет серверу следующий запрос 3.
  9. Сервер закончил выполнение запроса 2 и передал клиенту ответ на запрос 2.
  10. Клиент получает ответ на запрос 2 и трактует его как ответ на запрос 3.

... и так далее...

Ответы на запросы, приходящие в виде сообщений, распознать можно. Ответ сервера на команду - это сообщение.

in reply to:  20 comment:22 by alx, 8 years ago

Replying to dimag:

Если ответ не придёт в течение 800 мс, то можно считать что соединение отсутствует, но зачем такая большая задержка,

Для исключения ложного срабатывания.

Насколько я помню, заказчик требует определять отсутствие связи с сервером за время, не превышающее 10 секунд. Следовательно, если Вы сделаете таймаут 9 секунд (9000 миллисекунд), требование заказчика будет выполнено. Я не вижу причины делать таймаут меньше 9 секунд.

лучше быстро проверить наличие соединения за 200 мс,

Такой короткий таймаут будет давать много ложных срабатываний.

потом, при отсутствие, которое может быть вызвано проблемами Wi-Fi или неполадками на проводной линии, ждать ответа в течение 600 мс.

То есть Вы признаете тот факт, что таймаут 200 мс слишком короткий. В таком случае непонятно, нафига он используется.

in reply to:  21 comment:23 by alx, 8 years ago

Replying to dimag:

Ответы на запросы, приходящие в виде сообщений, распознать можно. Ответ сервера на команду - это сообщение.

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

  1. Клиент отправляет запрос 1:
    api command1
    
    
  1. Клиент отправляет запрос 2:
    api command2
    
    
  1. Клиент получает ответ:
Content-Type: api/response
Content-Length: xxx

...какие-то-данные...

comment:24 by dimag, 8 years ago

То что получает клиент в пункте 3, это сообщение, сообщения и команды разные вещи. Одна команда может сгенерировать много сообщений.
Посылка каждой команды всегда возвращает ответ в виде возвращаемого значения функции, показывающего, успешно доставлена ли команда и если успешно, то ответ сервера начинающегося с OK, если команда успешно принята сервером или ERR, если команда ошибочна в поле handle->last_sr_event->body.
На некоторые команды я жду ответа, например api list_users, api create_uuid, результат возвращается в том же поле.

comment:25 by dimag, 8 years ago

Я сделаю дальнейшее уточнение. В процессе работы я отсылаю на сервер команды, запросы и получаю сообщения.
Команда - то что я посылаю на сервер и жду на неё ответ в виде определённого сообщения, сообщений, например bgapi originate. Я посылаю эту команду и жду на неё ответа, по результатам ответа я обновляю структуры данных и перерисовываю картинку с состоянием конференций
Запрос - команда, результат которой возвращается и ответ на которую я жду в теле идентификатора соединения, это ответ обрабатывается и используется в дальнейшем, никакие сообщения на запрос не ожидаются, пример запроса - например api create_uuid. Жду пока появиться ответ, беру ответ в handle->last_sr_event->body и использую.
Сообщения - результат выполнения моих команд и команд других абонентов или операторов, по результатам сообщений я обновляю структуры данных и перерисовываю картинку с состоянием конференций.

comment:26 by dimag, 8 years ago

Если не будет дальнейших вопросов, то завтра 26.10.2016 закрою тикет.

in reply to:  1 ; comment:27 by san, 8 years ago

:-D

По какой причине?

comment:28 by san, 8 years ago

Replying to dimag:

2)формируется команда, которая отправляется через ESL на FreeSwitch сервер с помощью функции esl_send_recv, определённую из функции esl_send_recv_timed:
#define esl_send_recv(_handle, _cmd) esl_send_recv_timed(_handle, _cmd, 0)
где handle - идентификатор соединения, _cmd-команда, 0-время ожидая, означающее что ждать пока от FreeSwtich не поступит ответ о поставке переданной команды на обработку. Если не будет ответа от сервера, например, связь разорвана, то будет возвращена ошибка.

  1. Сколько будет длится ожидание ответа на команду? бесконечно?
  2. Что будет если в момент пока ответ ещё ожидается тикнет таймер "проверки связи"?

comment:29 by san, 8 years ago

Description: modified (diff)

Добавил в тело тикета описание выявленных недостатков алгоритма.
Алексей, может быть у тебя есть что добавить?

in reply to:  27 ; comment:30 by alx, 8 years ago

Replying to san:

По какой причине?

Видимо, задача выполнена - алгоритм описан. :-)

Мне более интересен другой вопрос: почему закрытие тикета поставлено в зависимость от появления новых вопросов. :)

Впрочем, судя по появлению нового вопроса ("По какой причине?"), 26.10.2016 тикет закрыт не будет. :)

Last edited 8 years ago by alx (previous) (diff)

in reply to:  30 ; comment:31 by san, 8 years ago

Replying to alx:

Replying to san:

По какой причине?

Видимо, задача выполнена - алгоритм описан. :-)

Логично)))

Алексей, кроме описаного в теле тикета, осталось что-то что тебе кажется неправильным в алгоритме?

Дмитрий, жду ответы на comment:28

Last edited 8 years ago by san (previous) (diff)

in reply to:  31 ; comment:32 by alx, 8 years ago

Replying to san:

Алексей, кроме описаного в теле тикета,

"в теле" - это в описании?

осталось что-то что тебе кажется неправильным в алгоритме?

Тут все завязано в один клубок, где одно цепляет другое. :) Я не готов формально утверждать, что что-то сделано неправильно, так как мне неизвестно, из каких соображений было принято то или иное решение, чем оно мотивировано. Неизвестно, например, чем мотивировано решение сначала складывать поступающие от сервера события в какое-то хранилище, а потом их обрабатывать - почему не обрабатывать сразу? Или из каких соображений поток, принимающий информацию от FS, не постоянно поллит сокет, а с какими-то перерывами (упоминавшийся тамер, срабатывающий не то 20, не то 50 раз в секунду)...

Мне кажется, как минимум, странным выбор таймаутов (не знаю, можно ли формально называть его неправильным) - 60 мс, 200 мс, 600 мс, 5 с... Все таймауты должны вытекать из задачи. Я не вижу, каким образом из условий задачи выведены все эти значения. Если начинать выводить разумные значения таймаутов из условий поставленной задачи, мы упираемся в странности архитектуры, о которых я написал выше...

in reply to:  32 ; comment:33 by san, 8 years ago

Я не готов формально утверждать, что что-то сделано неправильно.

Формальную "неправильность" ты сам нашёл: исходя из формата ответов сервера ESL на команды, можно сделать вывод что команды нельзя отправлять не дождавшись ответа на предыдущие, а программа позволяет себе такое.

in reply to:  33 comment:34 by alx, 8 years ago

Replying to san:

Формальную "неправильность" ты сам нашёл:

Так об этом уже упомянуто в описании тикета. Ты же спрашивал о том, что в тикете еще не написано...

in reply to:  33 ; comment:35 by dimag, 8 years ago

Replying to san:

Я не готов формально утверждать, что что-то сделано неправильно.

Формальную "неправильность" ты сам нашёл: исходя из формата ответов сервера ESL на команды, можно сделать вывод что команды нельзя отправлять не дождавшись ответа на предыдущие, а программа позволяет себе такое.

Можно отправлять команды не дождавшись ответа на предыдущее если они отправляются с префиксом bg_api.
Но нельзя отправлять из разных потоков новую команду, пока старая команда не обработана, для это вызов функций ESL всегда обрамлён мутексами, то есть функции ESL нереентрантные.

comment:36 by san, 8 years ago

Можно отправлять команды не дождавшись ответа на предыдущее если они отправляются с префиксом bg_api.

Интересно на основании чего ты делаешь такой вывод?

В докементации о bgapi:
Команда:
bgapi <command> <arg>

Пример ответа:
Content-Type: command/reply
Reply-Text: +OK Job-UUID: c7709e9c-1517-11dc-842a-d3a3942d3d63

Формат запроса-ответа совпадает с тем что описал Алексей в comment:21, соответственно и выводы те-же:

Применять описанный Димой алгоритм нельзя, так как программа не имеет возможности определить, на какой из запросов получен ответ

Ответ Дмитрия, который он вставил в мой комент:
Возвратиться идентификатор запроса в очереди запросов на сервере, можно не дожидаясь обработки запроса поставленного в очередь отправить новый.
Я могу определять на какой запрос получен ответ, например по UUID, определить на какой запрос получен ответ по содержимому сообщения.

Last edited 8 years ago by san (previous) (diff)

comment:37 by dimag, 8 years ago

11)Нет, время ожидания будет зависеть от Winsock
12)Сработает мутекс, и пока выполнение запроса не будет выполнено, соответствующий участок кода в потоке сообщений будет ждать пока мутекс не прекратит действовать.
Когда мутекс прекратит действовать, таймер проверки связи сразу начнёт работать.

in reply to:  35 ; comment:38 by san, 8 years ago

Дмитрий, ты опять правишь чужие комментарии!

in reply to:  38 ; comment:39 by dimag, 8 years ago

Replying to san:

Дмитрий, ты опять правишь чужие комментарии!

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

in reply to:  35 ; comment:40 by alx, 8 years ago

Replying to dimag:

Можно отправлять команды не дождавшись ответа на предыдущее если они отправляются с префиксом bg_api.

Тогда я несколько изменю свой вопрос в comment:23: объясните, пожалуйста, как в приведенном ниже сценарии клиент может определить, какое из заданий (command1 или command2) начало выполняться с UUID c7709e9c-1517-11dc-842a-d3a3942d3d63. Сценарий такой:

  1. Клиент отправляет запрос 1:
    bgapi command1
    
    
  1. Клиент отправляет запрос 2:
    bgapi command2
    
    
  1. Клиент получает ответ:
    Content-Type: command/reply
    Reply-Text: +OK Job-UUID: c7709e9c-1517-11dc-842a-d3a3942d3d63
    
    

in reply to:  37 ; comment:41 by alx, 8 years ago

Replying to dimag:

11)Нет, время ожидания будет зависеть от Winsock

Что "нет" и время ожидания чего? В предыдущем комментарии я не вижу вопросов о времени ожидания. Вы здесь кому вообще отвечаете?

in reply to:  39 comment:42 by alx, 8 years ago

Replying to dimag:

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

Почему же Вы этого не написали?

in reply to:  41 comment:43 by san, 8 years ago

Это Димин ответ на comment:28

Replying to dimag:

11)Нет, время ожидания будет зависеть от Winsock

Что "нет" и время ожидания чего? В предыдущем комментарии я не вижу вопросов о времени ожидания. Вы здесь кому вообще отвечаете?

Version 0, edited 8 years ago by san (next)

in reply to:  40 ; comment:44 by dimag, 8 years ago

Replying to alx:

Replying to dimag:

Можно отправлять команды не дождавшись ответа на предыдущее если они отправляются с префиксом bg_api.

Тогда я несколько изменю свой вопрос в comment:23: объясните, пожалуйста, как в приведенном ниже сценарии клиент может определить, какое из заданий (command1 или command2) начало выполняться с UUID c7709e9c-1517-11dc-842a-d3a3942d3d63. Сценарий такой:

  1. Клиент отправляет запрос 1:
    bgapi command1
    
    
  1. Клиент отправляет запрос 2:
    bgapi command2
    
    
  1. Клиент получает ответ:
    Content-Type: command/reply
    Reply-Text: +OK Job-UUID: c7709e9c-1517-11dc-842a-d3a3942d3d63
    
    

Для команды 1, данное значение в пункте 3 будет возвращено в поле handle->last_sr_reply, после успешной доставке сообщения, что будет означать что команда принята к обработке на сервере. Команда 2 не начнёт выполняться пока сервером не будет обработана команда 1 и возвращено сообщение 3, что команда не запустилась из другого потока, вызов команда 1 будет в мутексе.

То есть функция esl_send_recv посылает сообщения, если дошло, ждёт ответа от сервера об результатах анализа команды, если команда принята, то вернётся сообщение OK Job_UID, если нет то ERR. Далее сервер обрабатывает команду из очереди и генерирует сообщение об результатах выполнения команды, которые получаются в функции esl_recv_event_timed.


Допустим ответ 3 должен приходить на команду 1. Тогда что происходит при отправке команды 1 - команда отправляется, если сообщение отправлено успешно, то ожидается ответ от сервера в виде сообщения 3. Запрос 2 не может быть отправлен до получения ответа на запрос 1, пока не вернётся ответ на запрос 3, не будет выполняться запрос 2, для того, что из другого потока не был случайно послана команда 2 до получения ответа на отправку команды 1, используется мутекс во время вызова функции esl_send_recv.
То есть описанная вами последовательность действий не возможна будет следующая последовательность действий
1.Клиент отправляет запрос 1 - bgapi command1
2)3.Клиент получает ответ на запрос 1, о поставке команды в очередь выполнения на сервере FreeSwitch. - Content-Type: command/reply
Reply-Text: +OK Job-UUID: c7709e9c-1517-11dc-842a-d3a3942d3d63
3)2.Клиент отправляет запрос 2 - bgapi command2
Команды 1 и 2 могут вызвать посылку сообщений таких как conference_create, их обрабатывать будут в отдельном потоке событий.

Last edited 8 years ago by dimag (previous) (diff)

in reply to:  44 comment:45 by san, 8 years ago

Replying to dimag:

Запрос 2 не может быть отправлен до получения ответа на запрос 1

Отлично. Но в изложении алгоритма(comment:2) ты пишешь:

Отправляем первый запрос bgapi status...
...iDelay-время ожидания, для данного равно 200, что означает, что ждать ответа 200 мс, если нет, то считать команду не выполненной.Если первый запрос был получен FreeSwitch сервером, неудачен, то топравляем повторный запрос.

Исходя из этого объяснения в программе возможен такой вариант:

bgapi command1
...ожидаем 200ms
bgapi command2
Content-Type: command/reply
Reply-Text: +OK Job-UUID: c7709e9c-1517-11dc-842a-d3a3942d3d63

Правильно я понял?

comment:46 by dimag, 8 years ago

Нет - bgapi command1 и bgapi status будут вызваться из разных потоков, при этом bgapi command 1 будет вызываться в мутексе и bgapi status будет ждать пока не будет закрыт мутекс в котором выполняется bgapi command1.
Последовательность действий будет такой
мутекс начат - поток команд
bgapi command1 - поток команд

...ожидаем 200ms - поток сообщений, ожидает завершение мутекса

получаем сообщение о доставке команды на сервер и идентификатор задачи которая будет обрабатывать команду - поток команд
Content-Type: command/reply
Reply-Text: +OK Job-UUID: c7709e9c-1517-11dc-842a-d3a3942d3d63
мутекс закрыт - поток команд

...ожидаем 200ms - ожидает завершение мутекса, дождались начинаем выполнять - поток сообщений
мутекс начат поток сообщений
выполняем - bgapi stustus, ждем ответа до 200 мс - поток сообщений
мутекс закрыт - поток сообщений

мутекс начат - поток комманд
выполняем bgapi command2 - поток команд
получаем ответ
мутекс закрыт.

comment:47 by san, 8 years ago

Ничего не понял из последнего коментария, какая-то каша.

Перефразирую свой вопрос:
В алгоритме контроля "наличия связи" программа делает так:

bgapi status попытка1
...ожидаем 200ms, ответа нет
bgapi status попытка2
...ожидаем 600ms

Правильно?

comment:48 by dimag, 8 years ago

Да

comment:49 by san, 8 years ago

Из этого следует что программа отправляет следующую команду bgapi status не дождавшись ответа на предыдущую команду bgapi status.

Правильно?

comment:50 by dimag, 8 years ago

Ответ не нужен, нужно только сообщение о доставке на сервер.

in reply to:  50 comment:51 by san, 8 years ago

Replying to dimag:

сообщение о доставке на сервер

Такого термина в описании не нашёл.

Вот что говорит описание команды (ссылка):

bgapi

Send an api command (non-blocking mode) this will let you execute a job in the background and the result will be sent as an event with an indicated uuid to match the reply to the command)

bgapi <command> <arg> 

The same API commands available as with the api command, however the server returns immediately and is available for processing more commands.

Example return value:

Content-Type: command/reply
Reply-Text: +OK Job-UUID: c7709e9c-1517-11dc-842a-d3a3942d3d63

Вижу здесь термины "Ответ" и "Результат"

comment:52 by dimag, 8 years ago

Функция esl_send_recv_timed возвращает значение типа esl_status_t, если оно равно ESL_SUCCESS, то сообщение успешно доставлено на сервер.
В ответе содержится идентификатор задачи которая будет обрабатывать запрос (вроде Content-Type: command/reply
Reply-Text: +OK Job-UUID: c7709e9c-1517-11dc-842a-d3a3942d3d63)
или сообщение о том что была послана ошибочная команда, но ответ в данном случае не интересует, меня интересует возвращаемое значение функции, что оно равно ESL_SUCCESS.

in reply to:  52 comment:53 by san, 8 years ago

Replying to dimag:

Функция esl_send_recv_timed возвращает значение типа esl_status_t, если оно равно ESL_SUCCESS, то сообщение успешно доставлено на сервер.


ESL_SUCCESS возвращается только в том случае если от сервера пришёл ответ на команду.

Правильно?

comment:54 by dimag, 8 years ago

Да, иначе возвращается ESL_FAIL, что означает что команду не удалось доставить, и значит соединение разорвано.

comment:55 by san, 8 years ago

Можно ли отправлять следующую команду если на предыдущую не получен ответ ESL_SUCCESS, а просто истёк таймаут?

comment:56 by dimag, 8 years ago

Если истёк тайм-аут, то будет возвращено значение ESL_FAIL или другое не равное ESL_SUCCESS функцией esl_send_recv_timed, другую команду отправлять можно после отправки первой.

comment:57 by san, 8 years ago

Это означает что ты ответил "Да" на comment:55 ?

comment:58 by dimag, 8 years ago

Да, после того как завершилась первая команда, можно послать вторую, независимо от того, как она завершилась.

comment:59 by san, 8 years ago

Да, после того как завершилась первая команда, можно послать вторую, независимо от того, как она завершилась.

А в comment:18 Алексей приводит аргументы доказывающие, что так делать нельзя.

В рассуждениях comment:18 есть какая-то ошибка или они верны?

comment:60 by dimag, 8 years ago

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

in reply to:  60 comment:61 by alx, 8 years ago

Replying to dimag:

То что написано в комментарии 18 для однопоточного режима обработки, но у меня посылка команда и приём сообщений идут в разных потоках и всё обрабатывается совсем не так как в комментарии 18.

??? По-моему я в comment:18 ничего не писал ни про потоки, ни про то, как все обрабатывается. Я писал только о последовательности обмена данными между пультом и сервером.

Хорошо. Скажите, пожалуйста, начиная с какого из десяти пунктов, изложенных в comment:18, описанный сценария неверен.

comment:62 by dimag, 8 years ago

Если это сценарий обмена информацией, то он возможен.

comment:63 by san, 8 years ago

что ты имеешь в виду под "возможен"?

Возможен такой ход событий при взаимодействии твоей программы с ES?

comment:64 by san, 8 years ago

Дмитрий, ответь на последний вопрос.

comment:65 by san, 8 years ago

Все ещё жду ответ на comment:63

comment:66 by dimag, 8 years ago

Данная последовательность работы в принципе возможна, но не с моей программой.
1.Клиент отправляет серверу запрос 1.
2.Сервер получает из сокета запрос 1 и приступает к его обработке.
3.Возникает таймаут, esl_send_recv_timed() завершается с ошибкой.
4.Клиент отправляет серверу запрос 2.
5.Сервер закончил обработку запроса 1 и передал клиенту ответ на запрос 1.
6.Сервер читает из сокета запрос 2 и приступает к его обработке.
7.Клиент получил ответ на запрос 1, и трактует его как ответ на запрос 2. esl_send_recv_timed() завершается успешно.
8.Клиент отправляет серверу следующий запрос 3.
9.Сервер закончил выполнение запроса 2 и передал клиенту ответ на запрос 2.
10.Клиент получает ответ на запрос 2 и трактует его как ответ на запрос 3.

... и так далее...

Прежде всего, я посылаю команды(запросы) с помощью esl_send_recv_timed и жду на них ответы-сообщения. Я обновляю состояние программы в зависимости от сообщений.
При этом возможно что ответа на команду не будет, но команда может быть доставлена на сервер, команда считается выполненной успешно если она доставленна на сервер, в этом случае esl_send_rev_timed возвращает ESL_SUCCESS(сообщение успешно доставлено), кроме того в одном из полей идентификатора соединения возвращается ответ от сервера на посланную команду, в виде +OK Job-UUID: c7709e9c-1517-11dc-842a-d3a3942d3d63 или ERR.

comment:67 by san, 8 years ago

Представь что твоя программа выполняет "проверку связи":
В comment:2 насколько я понял ты описал такой алгоритм:

1.Клиент отправляет серверу запрос 1: esl_send_recv_timed(200ms)
2.Сервер получает из сокета запрос 1 и приступает к его обработке.
3.Возникает таймаут 200ms, esl_send_recv_timed() завершается с ошибкой.
4.Клиент отправляет серверу запрос 2: esl_send_recv_timed(600ms)
...

Так делает твоя программа?

Last edited 8 years ago by san (previous) (diff)

comment:68 by san, 8 years ago

жду ответа на комент 67

in reply to:  67 ; comment:69 by dimag, 8 years ago

Replying to san:

Представь что твоя программа выполняет "проверку связи":
В comment:2 насколько я понял ты описал такой алгоритм:

1.Клиент отправляет серверу запрос 1: esl_send_recv_timed(200ms)
2.Сервер получает из сокета запрос 1 и приступает к его обработке.
3.Возникает таймаут 200ms, esl_send_recv_timed() завершается с ошибкой.
4.Клиент отправляет серверу запрос 2: esl_send_recv_timed(600ms)
...

Так делает твоя программа?

Не совсем, порядок работы следующий
1)Клиент отправляет запрос 1:esl_send_recv_timed(200ms)
2)Клиент ждёт до 200 мс, если всё успешно, то функция esl_send_recv_timed(200ms)возвращает ESL_SUCCESS, есть соединение, если нет или не хватило 200 мс откликнуться на сообщение, то возвращается ESL_FAIL:
3)Клиент отправляет серверу запрос 2: esl_send_recv_timed(600ms)
4)Клиент ждёт до 600 мс, если всё успешно, то функция esl_send_recv_timed(200ms)возвращает ESL_SUCCESS, есть соединение, если нет или не хватило 600 мс откликнуться на сообщение, то считаем что соединения с сервером нет.

in reply to:  69 ; comment:70 by san, 8 years ago

Не совсем?
Оставил в твоём коментарии то что касается случая срабатывания таймаута, получил то-же что и коммент 67

1)Клиент отправляет запрос 1:esl_send_recv_timed(200ms)
2)Клиент ждёт до 200 мс, ... если не хватило 200 мс откликнуться на сообщение, то возвращается ESL_FAIL
3)Клиент отправляет серверу запрос 2: esl_send_recv_timed(600ms)

Отсюда следует, что программа отправляет серверу второй запрос не дождавшись ответа сервера на предыдущий ?

comment:71 by alx, 8 years ago

Я тоже в comment:69 не нашел отличия между отквоченным фрагментом comment:67 и словами Димы.
Дима, в чем именно заключается "не совсем"? В каком именно пункте comment:67 расходится с реальностью?

in reply to:  70 comment:72 by dimag, 8 years ago

Replying to san:

Не совсем?
Оставил в твоём коментарии то что касается случая срабатывания таймаута, получил то-же что и коммент 67

1)Клиент отправляет запрос 1:esl_send_recv_timed(200ms)
2)Клиент ждёт до 200 мс, ... если не хватило 200 мс откликнуться на сообщение, то возвращается ESL_FAIL
3)Клиент отправляет серверу запрос 2: esl_send_recv_timed(600ms)

Отсюда следует, что программа отправляет серверу второй запрос не дождавшись ответа сервера на предыдущий ?

Для алгоритма определения подключения к серверу я посылаю вначале 1 запрос с командой api stauts с маленьким периодом ожидания, если сообщение не дошло за 200 мс, сервер не прислал ответ, возвратилось E_FAIL(доставка сообщения было неудачна), если этот запрос не удался, например подключения с помощью Wi-Fi, то посылается такой же запрос с большим временем ожидания, достаточным для восстановления связи по Wi-Fi, если и за большее время запрос не был доставлен, то считаем что соединение разорвано.

comment:73 by san, 8 years ago

Дима, ты не ответил на мой вопрос.

in reply to:  70 ; comment:74 by dimag, 8 years ago

Replying to san:

Не совсем?
Оставил в твоём коментарии то что касается случая срабатывания таймаута, получил то-же что и коммент 67

1)Клиент отправляет запрос 1:esl_send_recv_timed(200ms)
2)Клиент ждёт до 200 мс, ... если не хватило 200 мс откликнуться на сообщение, то возвращается ESL_FAIL
3)Клиент отправляет серверу запрос 2: esl_send_recv_timed(600ms)

Отсюда следует, что программа отправляет серверу второй запрос не дождавшись ответа сервера на предыдущий ?

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

in reply to:  74 ; comment:75 by alx, 8 years ago

Replying to dimag:

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

Покажите, пожалуйста, код, выполняющий отбрасывание "опоздавших" ответов на запросы.

in reply to:  75 ; comment:76 by dimag, 8 years ago

Replying to alx:

Replying to dimag:

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

Покажите, пожалуйста, код, выполняющий отбрасывание "опоздавших" ответов на запросы.

Внутри ESL.

comment:77 by san, 8 years ago

Дима, мне тоже хотелось бы увидеть доказательства к этой теории.

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

Last edited 8 years ago by san (previous) (diff)

in reply to:  76 comment:78 by alx, 8 years ago

Replying to dimag:

Внутри ESL.

Нельзя ли поточнее? Назовите, пожалуйста, имя файла и номера строк в нем.

comment:79 by dimag, 8 years ago

Не могу назвать, поведения выяснено по ходу работы.

comment:80 by san, 8 years ago

Дмитрий, интересно каким образом вы выяснили, 'по ходу работы', что:

запрос 2 он будет просто отброшен, каждый отправленный запрос на сервер однозначно идентифицируется.

?
Опишите эксперимент с помошью которого вы это выяснили.

in reply to:  80 comment:81 by alx, 8 years ago

Replying to san:

Опишите эксперимент с помошью которого вы это выяснили.

Я надеюсь, Дмитрий это выяснил анализируя исходный код, а не путем эксперимента...

comment:82 by san, 8 years ago

Last edited 8 years ago by san (previous) (diff)

comment:83 by san, 8 years ago

Дмитрий, как же вы это выяснили?

comment:84 by dimag, 8 years ago

Просто протокол по которому общается ESL позволяет однозначно идентифицировать ответ на запрос, поэтому запрос всегда получит ответ на себя или не получит, но не получит в качестве ответа ответ на другой запрос.

comment:85 by san, 8 years ago

Очередной круг.

Просто протокол по которому общается ESL позволяет однозначно идентифицировать ответ на запрос, поэтому запрос всегда получит ответ на себя или не получит, но не получит в качестве ответа ответ на другой запрос.

В comment:18 Алексей доказывает обратное.
Дмитрий, где же ошибка в его рассуждениях?

in reply to:  84 comment:86 by alx, 8 years ago

Replying to dimag:

Просто протокол по которому общается ESL позволяет однозначно идентифицировать ответ на запрос,

Предположим, от сервера пришло:

Content-Type: api/response
Content-Length: xxx

command executed successfully

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

comment:87 by dimag, 8 years ago

Например в теле заголовка сообщения можно записать уникальный идентификатор сообщения, по которому будет определяться ответ, похоже ESL так и работает.

in reply to:  87 comment:88 by alx, 8 years ago

Replying to dimag:

Например в теле заголовка сообщения можно записать уникальный идентификатор сообщения,

Что значит "можно"? Если очень захотеть, можно в космос улететь. :) Речь идет не о том, как можно сделать, а о том, как реально обстоят дела здесь и сейчас. И сейчас в ответах сервера я никаких уникальных идентификаторов не вижу. Если я неправ, покажите, пожалуйста, в ответе, пример которого приведен в comment:86, этот уникальный идентификатор.

Last edited 8 years ago by alx (previous) (diff)

comment:89 by san, 8 years ago

Дмитрий, жду вашего ответа на comment:88

comment:90 by san, 8 years ago

Resolution: invalid
Status: newclosed

comment:91 by san, 7 years ago

Milestone: Срочно!

Milestone deleted

Note: See TracTickets for help on using tickets.