Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: lwip: как принимать данные?
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
H90
Приветствую.
Есть lwip для lm3s9b92 из примера StellarisWare. В примере использован метод GET, требуется реализовать метод POST для передачи файлов. Размер файла значительно превышает размер окна. Как я понимаю, необходимо сделать примерно следующее (метод POST вписал в пример туда же, где обработка GET):
http_recv -> tcp_recved -> декодировать POST -> tcp->write(200 OK)-> [принимать данные.]
[Принимать данные]:
while(не кончился контент)
{http_recv -> tcp_recved -> tcp_write(WHAT?)}
Функция tcp_write(WHAT?) или какая-либо другая должна сообщить уровню ТСР, что можно послать АСК и принять следующую порцию данных.

ЧТО именно нужно посылать функцией tcp_write, чтобы подтвердить принятые данные и начать получать следующие? Пробовал посылать данные нулевой длины - возникает постоянный duplicate ack при анализе wireshark'ом. Если посылать 200 ОК, то рвётся соединение. Если вообще ничего не посылать - ничего не происходит, соединение рвётся по таймауту (т.е.одной tcp_recved) недостаточно. Может, надо использовать не tcp_write, а другую функцию - тогда какую?

Примеров с POST или с приёмом более одного окна я не нашёл. С lwip и сетевыми протоколами ранее не работал.

Прошу подсказать, как надо правильно это делать.
scifi
Цитата(H90 @ Dec 27 2010, 13:09) *
Функция tcp_write(WHAT?) или какая-либо другая должна сообщить уровню ТСР, что можно послать АСК и принять следующую порцию данных.
ЧТО именно нужно посылать функцией tcp_write, чтобы подтвердить принятые данные и начать получать следующие?

Функция tcp_recved() сообщает стеку, что можно посылать ACK и принимать новые данные. Не нужно делать tcp_write().

Цитата(H90 @ Dec 27 2010, 13:09) *
Если вообще ничего не посылать - ничего не происходит, соединение рвётся по таймауту (т.е.одной tcp_recved) недостаточно. Может, надо использовать не tcp_write, а другую функцию - тогда какую?

Из Вашего объяснения я так и не понял, как устроена Ваша программа. Стек lwip требует, чтобы периодически (каждые 250 мс) вызывалась функция tcp_tmr(). Иначе всё застопорится. Ваше "while(не кончился контент)" этому вопиющим образом противоречит. Данные принимает callback-функция по одному кусочку за раз, складывает их куда надо, вызывает tcp_recved() и сразу возвращается. Простой цикл while() в эту схему не вписывается.
H90
Цитата(scifi @ Dec 27 2010, 15:52) *
Из Вашего объяснения я так и не понял, как устроена Ваша программа. Стек lwip требует, чтобы периодически (каждые 250 мс) вызывалась функция tcp_tmr().

Программа устроена так, как устроен пример в StellarisWare, В т.ч. вызывается tcp_tmr, т.к.диагностические сообщения от tcp_slow_tmr выводятся на консоль. В пример добавлен разбор запроса POST, а вместо send_data была попытка посылать что-либо, описанное выше... Это прекрасно работает при приёме файла из одного окна. Параллельно с этим прекрасно работает сайт из примера. Параллельно работает UDP на своём порту. Т.е.структура нормальная. While из моего вопроса означает, что я продолжаю обрабатывать принятые данные в коллбэчной функции http_recv и пытаться выдавать что-то с целью АСК'а, затем делать return до тех пор, пока не истечёт полученная в первом запросе POST длина Content Length.

Плюс "по наитию" сделано то, чего не было в примере - выемка данных из цепочки pbuf'ов:
p = p->next; if(p){..выемка..} else pbuf_free(pbuf0);, где pbuf0 - изначальный pbuf в цепочке. Не знаю, правильно это или нет.

Цитата(scifi @ Dec 27 2010, 15:52) *
Функция tcp_recved() сообщает стеку, что можно посылать ACK и принимать новые данные. Не нужно делать tcp_write().

Да, без tcp_write получилось после переноса tcp_recvd из начала обработки (как было в примере) в конец, после выемки всей цепочки и выдачи по UARTу всех логов. Но duplicate ack всё равно остался: первый посылается tcp_recved, второй посылается после выхода из коллбэчной функции http_recv функцией tcp_input отсюда:
Код
        /* If there were no errors, we try to send something out. */
        if (err == ERR_OK) {
          tcp_output(pcb);

Выглядит это так:
Код
12    2.096778    192.168.66.50    192.168.66.1    TCP    http > 48728 [ACK] Seq=25 Ack=4381 Win=4096 Len=0
13    2.096824    192.168.66.1    192.168.66.50    TCP    [TCP segment of a reassembled PDU]
14    2.096835    192.168.66.1    192.168.66.50    TCP    [TCP segment of a reassembled PDU]
15    2.341158    192.168.66.50    192.168.66.1    TCP    http > 48728 [ACK] Seq=25 Ack=5841 Win=4096 Len=0
16    2.341205    192.168.66.1    192.168.66.50    TCP    [TCP segment of a reassembled PDU]
17    2.523333    192.168.66.50    192.168.66.1    TCP    [TCP Dup ACK 15#1] http > 48728 [ACK] Seq=25 Ack=5841 Win=4096 Len=0
18    3.564769    192.168.66.1    192.168.66.50    TCP    [TCP Retransmission] [TCP segment of a reassembled PDU]

66 1 - компьютер, 66 50 - контроллер.
Такие группы из двух нормальных АСК и одного дубликата идут при приёме всего файла. Сейчас только tcp_recved, tcp_write делается 1 раз в начале для ответа 200 ОК.

Как избавиться от дубликатов, пока не понятно.

А есть ли готовый пример с обработкой POST? Буду благодарен за помощь.

scifi
Цитата(H90 @ Dec 28 2010, 07:01) *
Плюс "по наитию" сделано то, чего не было в примере - выемка данных из цепочки pbuf'ов:
p = p->next; if(p){..выемка..} else pbuf_free(pbuf0);, где pbuf0 - изначальный pbuf в цепочке. Не знаю, правильно это или нет.

Можно подсмотреть в примерах, идущих вместе с lwip. Посмотрите echo.c, функция echo_send():
echo.c
Правда, я подозреваю, что там есть избыточные вещи (автор, наверное, от чего-то перестраховывался). Но всё равно интересно.
Цитата(H90 @ Dec 28 2010, 07:01) *
А есть ли готовый пример с обработкой POST? Буду благодарен за помощь.

Высылаю свой веб-сервер. Но там несколько заморочно. Может быть, будет чем-то полезен.
H90
Похоже, дело не в реализации http-сервера, а гораздо ниже.

Была доработана функция tcp_output, чтобы сравнивать, был ли уже отправлен такой ACK и если был, то не посылать. Исчезли DUP ACK, но точно так же через 3 кадра стали появляться [TCP retransmission] со стороны РС.
Детальный анализ отладки уровня ip (включены опции IP_DEBUG, IP_REASS_DEBUG) выявил, что если со стороны РС посылается 2 кадра подряд, то на контроллер на уровень ip второй кадр не попадает и при этом нет никаких сообщений об ошибках.

После этого сходная ситуация нашлась здесь: http://www.mail-archive.com/lwip-users@non...g/msg07401.html, но решения её нет и причина толком не описана.

29 PC -> lwIP (data)
30 PC -> lwIP (data) - этот кадр не виден в отладке lwip на уровне ip
31 lwIP -> PC (ACK for 29)
32 PC -> lwIP (data)
33 lwIP -> PC (ACK) (duplicated) - этот кадр убирается, если доработать отправку повторного АСК в tcp_output
34 PC -> lwIP (data) (retransmission) - это кадр с повтором потерянного кадра строки 30

Получается, дело в аппаратной реализации ethernet-контроллера в 9B92, либо в примере в программной обработке на уровне прерываний... Ни первое, ни второе пока не ковырял. Либо всё-таки есть какие-то опции lwip?

Проверял в т.ч. при отключенной отладке на UART. Никакой разницы.

Если вы уже успешно решили подобную проблему, подскажите...
H90
При установке в lwip_opt размера окна TCP_WND от 2048 до 2500 полностью пропадают и дубликаты, и ретрансмиты (проблема решается, в wireshark все кадры зелёного цвета - без ошибок). В примере окно стояло 4096. При 2600 начинают появляться ретрансмиты, но дубликатов нет.

Вот так.
Petr_
У меня аналогичная ситуация - не передается ACK после приема пакета.
Но у вас получилось решить проблему, перенеся tcp_recved(pcb, p->tot_len);
в конец обработки данных.
Мне это не помогло!
Приходится писать tcp_write(pcb, "A", 1, 1); чтобы работало.

Изменение размеров окна ситуацию не меняет (да и не должно).

Вы знаете что то дополнительно по этому вопросу?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.