У меня есть проблема, которую я почти решил, но застрял на «последнем этапе». Я работаю над этим уже несколько дней, и хотя у меня были прорывы, чувствую, что я все еще далек от работоспособного решения - из-за нескольких белых пятен.
У меня есть встроенное мобильное приложение прошивки (которое я не могу редактировать напрямую), которое не может повторно подключиться к нашему серверу через TCP/IP/UMTS/GSM. Прошивка использует AT-команды только для инициации и управления соединением через модем. Первоначальная версия нашего продукта работает в производстве около 10 лет, однако «новая» версия оборудования периодически испытывает проблемы.
Есть много переменных, но я исключил все, кроме одного: чипа модема.
- У нас есть новые версии прошивки с различными изменениями, но обширное A/B-тестирование показало, что все прошивки имеют одну и ту же проблему, поэтому я ее исключил.
- У нас есть устаревший сервер VB6, использующий Winsock. Недавно я написал более простую версию Node.js, которая почти так же демонстрирует ту же проблему (удивительно).
- Мы используем различные телекоммуникационные компании: Optus, Vodafone, Telstra. Опять они кажутся не относящимися к делу.
- Мы используем прямые телефонные компании или реселлеров с их собственной частной сетью (APN). Опять оказалось неактуальным.
- У нас разные хостинговые/сетевые установки, но, опять же, я протестировал множество конфигураций, включая нашу корпоративную сеть, производственный хостинг, AWS dev EC2, и все они работают практически одинаково.
- У нас также есть автоматизированные тесты с эмулированными/фиктивными версиями аппаратного/микропрограммного обеспечения, которые показали, что различные серверы и сети работают хорошо и одинаково.
- Последний вариант, показывающий существенную, а точнее абсолютную, разницу между модемом Telit UC864 и HE910. Прежний, более старый модем работает отлично, но с новым HE910 проблема возникает постоянно. Я проверил все возможные перестановки, и проблема связана с HE910.
Конечно, я бесконечно читал документацию Telit (в частности, «Easy IP», AT-команду и справочники по программному обеспечению), но не вижу большой разрекламированной разницы между двумя продуктами. HE910 находится в физической упаковке от Glynn, но я не думаю, что это влияет на его поведение.
Проблема:
Наше приложение прошивки подключается к нашему серверу по определенному порту/адресу. Сервер инициирует исходящий прикладной протокол, на который микропрограмма должна реагировать. (Так что на самом деле прошивка является «сервером», но это не имеет значения.) Это очень легко и просто, порядка 10 байтов на команду/ответ.
Проблема возникает, когда прошивка повторно подключается из-за события «поверх» существующего подключения к сокету. Прошивка всегда проходит один и тот же процесс: демонтаж, настройка, подключение - независимо от того, есть ли существующее соединение (это из-за расплывчатости "подключено" как по TCP, так и по 3G, лучше либо "попытаться отправить данные", либо повторно подключитесь, чтобы обеспечить соединение). Как я уже сказал, я не могу изменить это поведение в F/W.
Шаги подключения:
- а) Отключение сокета AT#SH б) Подождите 1 с
- Настройка контекста PDP AT+CGDCONT
- Настройка стека TCP/IP: AT#SCFG
- Активируйте контекст PDP: AT#SGACT
- Подключить TCP/IP к серверу: AT#SD
(Вы можете спросить, почему шаги конфигурации 2,3,4 выполняются при каждом подключении: потому что они могут быть обновлены через протокол нашего приложения. Но обычно настройки неизменны.)
Проблема, с которой мы сталкиваемся, заключается в том, что иногда устройства подключаются, но не могут обмениваться данными. Соединения кажутся "застрявшими" - иногда проходят несколько байтов, иногда - целый цикл команды/ответа, но часто ничего. При подключении обычно обслуживается около 10 команд, и если они терпят неудачу, сервер разрывает соединение.
Что я нашел до сих пор:
Я использовал анализатор протокола, чтобы установить, что происходит неправильно: это потому, что процесс TCP FIN AT#SH перекрывается с рукопожатием TCP SYN следующего соединения, инициированного AT#SD. По-видимому, это происходит из-за того, что окончательные пакеты FIN-ACK приходят с опозданием — после того, как инициируется следующее соединение.
В приведенной ниже трассировке видно, что кадр 8799 приходит с опозданием, как ACK на 8788, но он приходит не по порядку в середине следующего рукопожатия SYN-ACK!
После этого модем фактически «сломан». Все последующие соединения испытывают потерю пакетов полезной нагрузки, с большим количеством повторных передач, и обе стороны повторно отправляют свои пакеты полезной нагрузки, как если бы они не получали ACK полезной нагрузки.
После шага 1. (AT#SH) в F/W есть 1-секундная пауза. Мы добавили это из-за проблем с повторным подключением, когда мы впервые установили HE910 (я знаю, что это кладж, но это было больше связано с задержкой базы данных в нашем состоянии сервера, чем для TCP / IP). Однако кажется, что эта задержка в 1 с даже не влияет на трафик TPC, как видно из приведенной ниже трассировки. ATSH «вызывает» кадр 8786 в 17:16:51.585, а ATSD «вызывает» SYN в кадре 8792 в 17:16:51.595, но между ними всего 10 мс. Я изучил прошивку и могу подтвердить, что код задержки 1 с выглядит правильно, и наблюдая за прошивкой в терминале, есть задержка в 1 секунду между выводом отладки этих шагов, но TCP говорит о другом.
Я могу отправлять AT-команды в прошивке ограниченным образом — только одну команду каждые пять секунд, что, очевидно, искажает ручные тесты, делая их хорошими и медленными. Если я отправлю +++, AT#SH во время подключения A, а затем вызову событие подключения, устройство будет успешно подключено. Это интригует, так как шаг 1 подключения в любом случае выполняет AT#SH. Таким образом, я могу сделать один из двух выводов: либо нам нужно отправить AT#SH «раньше» (т.е. ждать дольше, прежде чем соединиться с AT#SD), либо отправить два из них (что менее вероятно).
Я понимаю, что это область общих проблем в приложениях. Даже на странице Википедии, посвященной TCP/IP, упоминаются риски, связанные с тем, что приложения нарушают модель OSI и нарушают рукопожатие разрыва TCP, если оно используется для завершения сеанса на уровне приложения. Я хотел бы добавить приятное «До свидания» к нашему протоколу нашего приложения, но это нежизнеспособный вариант в среднесрочной перспективе ... Я хотел бы выровнять стеки TCP наших двух модемов.
Сервер открывает новый сокет для входящего соединения с той же комбинацией IP/порт. Node и VB создают новый ресурс сокета из прослушивающего родительского порта, поэтому я почти уверен, что на стороне сервера ничего не «повторно используется» (это было подозрение). К сожалению, у меня есть ограниченное представление о стеке TCP модема, я использую только AT#SI и SO, которые дают базовую и, по-видимому, информацию не в реальном времени. например когда сокет закрыт на стороне сервера, AT#SO по-прежнему показывает IP/порт локально на модеме, как будто он не получил разрыв FIN.
При новой загрузке устройство может успешно «повторно подключиться» около четырех раз, пока не произойдет «застревание». Это странно надежно для 4 +-1 соединений. Его также можно «сбросить», подождав несколько часов. Итак, (извините) такое ощущение, что модем просто занервничал. (Опять же, я подозревал, что наша телефонная компания «ограничивает» наши соединения, но тесты между телефонными компаниями слишком похожи. Учтите, что в производстве у нас есть тысячи таких устройств, и только одно из них может вызвать такое поведение. Это просто статистически маловероятно. они могли «заметить» четыре соединения среди сотен других?)
Последняя часть поведения — когда соединение «заблокировано», то есть данные не передаются, сервер получает RST (ECONNRESET в узле, просто ошибка в VB) через 30-60 секунд. (Только если мы отключим наше более быстрое завершение соединения на уровне приложения, так что это долгое время оставалось незамеченным). Однако после этого сброса AT#SO по-прежнему показывает, что он подключен (с IP-адресом), поэтому сначала я снова почувствовал, что какой-то 3G/AP/шлюз разрывает соединение посередине, но не сообщает клиенту. Однако из-за постоянства проблемы в телекоммуникационных компаниях и сетях мне теперь трудно в это поверить, и я просто думаю, что ответы AT не соответствуют действительности.
Эта типичная трассировка TCP показывает (до сих пор моя интерпретация, которая может быть неверной)
- Соединение A, Syn/Ack OK
- Протокол нашего приложения начинается и завершается в кадре 8197 17:16:01.
- Затем происходит событие, которое инициирует другое соединение B
- Первый шаг 1. пытается выполнить разрыв, кадр 8786 запускает рукопожатие FIN.
- Разборка доходит до кадра 8788 17:16:51.58
- Затем следующее соединение B начинается с кадра 8792 17:16:51.59.
- Это идет нормально, пока не будет прервано последним подтверждением Fin Ack от соединения A в кадре 8799 (возможно, это игнорируется?)
- Протокол приложения пытается запуститься снова, но первое 13-байтовое сообщение не получает ACK и повторно передается много раз...
- Наш сервер повторно отправляет первую команду, удваивая размер буфера на кадре 8978.
- Наш сервер истекает по таймауту и разрывает соединение, отправляя Fin на 9016.
След
Frame TIME REL TIME SOURCE DEST SEQ ACK FLAGS WINDOW PAYLOAD BYTES
8003 17:15:55.9409634 703.7329764 CLIENT_IP SERVER_IP 0 0 (0x0) ......S. 65000 0 Flags=......S., SrcPort=17790, DstPort=50008, PayloadLen=0, Seq=316432024, Ack=0, Win=65000 ( ) = 65000 vb6.exe
8004 17:15:55.9410316 0.0000682 SERVER_IP CLIENT_IP 0 1 ...A..S. 8192 0 Flags=...A..S., SrcPort=50008, DstPort=17790, PayloadLen=0, Seq=319387462, Ack=316432025, Win=8192 ( Scale factor not supported ) = 8192 vb6.exe
8016 17:15:58.5707216 2.6296900 CLIENT_IP SERVER_IP 1 1 ...A.... 65000 0 Flags=...A...., SrcPort=17790, DstPort=50008, PayloadLen=0, Seq=316432025, Ack=319387463, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8037 17:15:58.8632583 0.2925367 SERVER_IP CLIENT_IP 1 - 14 1 ...AP... 65000 13 Flags=...AP..., SrcPort=50008, DstPort=17790, PayloadLen=13, Seq=319387463 - 319387476, Ack=316432025, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8039 17:15:59.0105830 0.1473247 CLIENT_IP SERVER_IP 1 14 ...A.... 65000 0 Flags=...A...., SrcPort=17790, DstPort=50008, PayloadLen=0, Seq=316432025, Ack=319387476, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8041 17:15:59.1006112 0.0900282 CLIENT_IP SERVER_IP 1 - 7 14 ...AP... 65000 6 Flags=...AP..., SrcPort=17790, DstPort=50008, PayloadLen=6, Seq=316432025 - 316432031, Ack=319387476, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8068 17:15:59.2997681 0.1991569 SERVER_IP CLIENT_IP 14 7 ...A.... 64994 0 Flags=...A...., SrcPort=50008, DstPort=17790, PayloadLen=0, Seq=319387476, Ack=316432031, Win=64994 (scale factor 0x0) = 64994 vb6.exe
8071 17:15:59.3590861 0.0593180 SERVER_IP CLIENT_IP 14 - 19 7 ...AP... 64994 5 Flags=...AP..., SrcPort=50008, DstPort=17790, PayloadLen=5, Seq=319387476 - 319387481, Ack=316432031, Win=64994 (scale factor 0x0) = 64994 vb6.exe
8076 17:15:59.5106103 0.1515242 CLIENT_IP SERVER_IP 7 19 ...A.... 65000 0 Flags=...A...., SrcPort=17790, DstPort=50008, PayloadLen=0, Seq=316432031, Ack=319387481, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8078 17:15:59.6005478 0.0899375 CLIENT_IP SERVER_IP 7 - 28 19 ...AP... 65000 21 Flags=...AP..., SrcPort=17790, DstPort=50008, PayloadLen=21, Seq=316432031 - 316432052, Ack=319387481, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8100 17:15:59.7989459 0.1983981 SERVER_IP CLIENT_IP 19 28 ...A.... 64973 0 Flags=...A...., SrcPort=50008, DstPort=17790, PayloadLen=0, Seq=319387481, Ack=316432052, Win=64973 (scale factor 0x0) = 64973 vb6.exe
8110 17:15:59.9164691 0.1175232 SERVER_IP CLIENT_IP 19 - 37 28 ...AP... 64973 18 Flags=...AP..., SrcPort=50008, DstPort=17790, PayloadLen=18, Seq=319387481 - 319387499, Ack=316432052, Win=64973 (scale factor 0x0) = 64973 vb6.exe
8114 17:16:00.0005725 0.0841034 CLIENT_IP SERVER_IP 28 37 ...A.... 65000 0 Flags=...A...., SrcPort=17790, DstPort=50008, PayloadLen=0, Seq=316432052, Ack=319387499, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8120 17:16:00.2005077 0.1999352 CLIENT_IP SERVER_IP 28 - 34 37 ...AP... 65000 6 Flags=...AP..., SrcPort=17790, DstPort=50008, PayloadLen=6, Seq=316432052 - 316432058, Ack=319387499, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8127 17:16:00.2509607 0.0504530 SERVER_IP CLIENT_IP 37 - 42 34 ...AP... 64967 5 Flags=...AP..., SrcPort=50008, DstPort=17790, PayloadLen=5, Seq=319387499 - 319387504, Ack=316432058, Win=64967 (scale factor 0x0) = 64967 vb6.exe
8131 17:16:00.5004721 0.2495114 CLIENT_IP SERVER_IP 34 42 ...A.... 65000 0 Flags=...A...., SrcPort=17790, DstPort=50008, PayloadLen=0, Seq=316432058, Ack=319387504, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8132 17:16:00.5004721 0.0000000 CLIENT_IP SERVER_IP 34 - 43 42 ...AP... 65000 9 Flags=...AP..., SrcPort=17790, DstPort=50008, PayloadLen=9, Seq=316432058 - 316432067, Ack=319387504, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8160 17:16:00.6603237 0.1598516 SERVER_IP CLIENT_IP 42 - 49 43 ...AP... 64958 7 Flags=...AP..., SrcPort=50008, DstPort=17790, PayloadLen=7, Seq=319387504 - 319387511, Ack=316432067, Win=64958 (scale factor 0x0) = 64958 vb6.exe
8162 17:16:00.7505392 0.0902155 CLIENT_IP SERVER_IP 43 49 ...A.... 65000 0 Flags=...A...., SrcPort=17790, DstPort=50008, PayloadLen=0, Seq=316432067, Ack=319387511, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8168 17:16:00.9005226 0.1499834 CLIENT_IP SERVER_IP 43 - 55 49 ...AP... 65000 12 Flags=...AP..., SrcPort=17790, DstPort=50008, PayloadLen=12, Seq=316432067 - 316432079, Ack=319387511, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8197 17:16:01.1093181 0.2087955 SERVER_IP CLIENT_IP 49 55 ...A.... 64946 0 Flags=...A...., SrcPort=50008, DstPort=17790, PayloadLen=0, Seq=319387511, Ack=316432079, Win=64946 (scale factor 0x0) = 64946 vb6.exe
8786 17:16:51.5853712 50.4760531 CLIENT_IP SERVER_IP 55 49 ...A...F 65000 0 Flags=...A...F, SrcPort=17790, DstPort=50008, PayloadLen=0, Seq=316432079, Ack=319387511, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8787 17:16:51.5854172 0.0000460 SERVER_IP CLIENT_IP 49 56 ...A.... 64946 0 Flags=...A...., SrcPort=50008, DstPort=17790, PayloadLen=0, Seq=319387511, Ack=316432080, Win=64946 (scale factor 0x0) = 64946 vb6.exe
8788 17:16:51.5860033 0.0005861 SERVER_IP CLIENT_IP 49 56 ...A...F 64946 0 Flags=...A...F, SrcPort=50008, DstPort=17790, PayloadLen=0, Seq=319387511, Ack=316432080, Win=64946 (scale factor 0x0) = 64946 vb6.exe
8792 17:16:51.5954781 0.0094748 CLIENT_IP SERVER_IP 0 0 (0x0) ......S. 65000 0 Flags=......S., SrcPort=39522, DstPort=50008, PayloadLen=0, Seq=1817440308, Ack=0, Win=65000 ( ) = 65000 vb6.exe
8793 17:16:51.5955252 0.0000471 SERVER_IP CLIENT_IP 0 1 ...A..S. 8192 0 Flags=...A..S., SrcPort=50008, DstPort=39522, PayloadLen=0, Seq=4287314964, Ack=1817440309, Win=8192 ( Scale factor not supported ) = 8192 vb6.exe
8799 17:16:51.7152606 0.1197354 CLIENT_IP SERVER_IP 56 50 ...A.... 64999 0 Flags=...A...., SrcPort=17790, DstPort=50008, PayloadLen=0, Seq=316432080, Ack=319387512, Win=64999 (scale factor 0x0) = 64999 vb6.exe
8801 17:16:51.7351969 0.0199363 CLIENT_IP SERVER_IP 1 1 ...A.... 65000 0 Flags=...A...., SrcPort=39522, DstPort=50008, PayloadLen=0, Seq=1817440309, Ack=4287314965, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8821 17:16:52.0333514 0.2981545 SERVER_IP CLIENT_IP 1 - 14 1 ...AP... 65000 13 Flags=...AP..., SrcPort=50008, DstPort=39522, PayloadLen=13, Seq=4287314965 - 4287314978, Ack=1817440309, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8846 17:16:52.4579548 0.4246034 SERVER_IP CLIENT_IP 1 - 14 1 ...AP... 65000 13 [ReTransmit #8821]Flags=...AP..., SrcPort=50008, DstPort=39522, PayloadLen=13, Seq=4287314965 - 4287314978, Ack=1817440309, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8855 17:16:53.3003681 0.8424133 SERVER_IP CLIENT_IP 1 - 14 1 ...AP... 65000 13 [ReTransmit #8821]Flags=...AP..., SrcPort=50008, DstPort=39522, PayloadLen=13, Seq=4287314965 - 4287314978, Ack=1817440309, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8875 17:16:54.9852348 1.6848667 SERVER_IP CLIENT_IP 1 - 14 1 ...AP... 65000 13 [ReTransmit #8821]Flags=...AP..., SrcPort=50008, DstPort=39522, PayloadLen=13, Seq=4287314965 - 4287314978, Ack=1817440309, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8890 17:16:56.6729734 1.6877386 SERVER_IP CLIENT_IP 1 - 14 1 ...AP... 65000 13 [ReTransmit #8821]Flags=...AP..., SrcPort=50008, DstPort=39522, PayloadLen=13, Seq=4287314965 - 4287314978, Ack=1817440309, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8909 17:16:58.3734032 1.7004298 SERVER_IP CLIENT_IP 1 - 14 1 ...AP... 65000 13 [ReTransmit #8821]Flags=...AP..., SrcPort=50008, DstPort=39522, PayloadLen=13, Seq=4287314965 - 4287314978, Ack=1817440309, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8921 17:17:01.7429692 3.3695660 SERVER_IP CLIENT_IP 1 - 14 1 ...AP... 65000 13 [ReTransmit #8821]Flags=...AP..., SrcPort=50008, DstPort=39522, PayloadLen=13, Seq=4287314965 - 4287314978, Ack=1817440309, Win=65000 (scale factor 0x0) = 65000 vb6.exe
8978 17:17:08.4665817 6.7236125 SERVER_IP CLIENT_IP 1 - 27 1 ...AP... 65000 26 [ReTransmit #8821]Flags=...AP..., SrcPort=50008, DstPort=39522, PayloadLen=26, Seq=4287314965 - 4287314991, Ack=1817440309, Win=65000 (scale factor 0x0) = 65000 vb6.exe
9016 17:17:12.2738575 3.8072758 SERVER_IP CLIENT_IP 27 1 ...A...F 65000 0 Flags=...A...F, SrcPort=50008, DstPort=39522, PayloadLen=0, Seq=4287314991, Ack=1817440309, Win=65000 (scale factor 0x0) = 65000 vb6.exe
Наблюдения (с ограниченным знанием TCP)
- Все пакеты данных и Fin имеют установленный бит подтверждения, что мне кажется необычным.
- Рукопожатие fin является четырехсторонним, но похоже, что сервер пытается выполнить трехстороннее рукопожатие, включив Fin в Ack клиентского Fin. Возможно, это связано с вышеизложенным, но, возможно, это вызывает проблему, когда модем выполняет четырехстороннее отключение, а сервер пытается выполнить трехстороннее отключение? Но он также отправляет автономный Ack.
- Я не понимаю, как ReTransmit происходит так быстро? Кадр 8846 17:16:52.45 отправляется только через 420 мс после оригинала. Конечно, этого недостаточно, чтобы тайм-аут получить ACK? Возможно, я пропустил базовый пакет ответа об ошибке IP, потому что я отслеживаю только уровень TCP? Иначе как он узнал о повторной передаче?
Итак, напоследок - мой вопрос.
Как я могу, исключительно с помощью AT-команд, надежно отключить сокет TCP/IP и установить новое соединение на новом сокете с тем же портом/IP-адресом, не допуская, чтобы задержанные пакеты разрыва поступали не по порядку. >
Или, точнее, как я могу заставить модем HE910 делать то же самое, что и UC864, который вполне успешно это делает.