Windows http post request

cURL POST command line on WINDOWS RESTful service

My problem: Using the command line tool to curl my localhost server while sending some data along with my POST request is not working.

What seems to be causing the error: Imagine something like this

Result of the returning data

After some searching i figured out that problem couldn’t be the sintax used for the request since it works on UNIX shells.

Are you possibly using Windows? That so looks like a completely broken shell that doesn’t properly deal with single-quotes vs double-quotes. I just tried that command line and it worked fine on my linux box. http://curl.haxx.se/mail/archive-2011-03/0066.html

I tried to work around with those » escaping it \» but it still didn’t work

So i gave up. Windows seems to messing up with the JSON object sent on POST

7 Answers 7

I ran into the same issue on my win7 x64 laptop and was able to get it working using the curl release that is labeled Win64 — Generic w SSL by using the very similar command line format:

Which only differs from your 2nd escape version by using double-quotes around the escaped ones and the header parameter value. Definitely prefer the linux shell syntax more.

Another Alternative for the command line that is easier than fighting with quotation marks is to put the json into a file, and use the @ prefix of curl parameters, e.g. with the following in json.txt:

then in my case I issue:

Keeps the json more readable too.

Alternative solution: A More Userfriendly solution than command line:

If you are looking for a user friendly way to send and request data using HTTP Methods other than simple GET’s probably you are looking for a chrome extention just like this one http://goo.gl/rVW22f called AVANCED REST CLIENT

For guys looking to stay with command-line i recommend cygwin:

I ended up installing cygwin with CURL which allow us to Get that Linux feeling — on Windows!

Using Cygwin command line this issues have stopped and most important, the request syntax used on 1. worked fine.

Useful links:

I hope it helps someone because i spent all morning on this.

How to make an HTTP POST web request

Canonical
How can I make an HTTP request and send some data using the POST method?

I can do a GET request, but I have no idea of how to make a POST request.

15 Answers 15

There are several ways to perform HTTP GET and POST requests:

Method A: HttpClient (Preferred)

Available in: .NET Framework 4.5+ , .NET Standard 1.1+ , .NET Core 1.0+ .

It is currently the preferred approach, and is asynchronous and high performance. Use the built-in version in most cases, but for very old platforms there is a NuGet package.

Setup

It is recommended to instantiate one HttpClient for your application’s lifetime and share it unless you have a specific reason not to.

Method B: Third-Party Libraries

It is a newer library sporting a fluent API, testing helpers, uses HttpClient under the hood, and is portable. It is available via NuGet.

Available in: .NET Framework 1.1+ , .NET Standard 2.0+ , .NET Core 1.0+ . In .NET Core, it is mostly for compatibility — it wraps HttpClient , is less performant, and won’t get new features.

This is a wrapper around HttpWebRequest . Compare with HttpClient .

Available in: .NET Framework 1.1+ , NET Standard 2.0+ , .NET Core 2.0+

Simple GET request

Simple POST request

This is a complete working example of sending/receiving data in JSON format, I used Visual Studio 2013 Express Edition:

There are some really good answers on here. Let me post a different way to set your headers with the WebClient(). I will also show you how to set an API key.

This solution uses nothing but standard .NET calls.

  • In use in an enterprise WPF application. Uses async/await to avoid blocking the UI.
  • Compatible with .NET 4.5+.
  • Tested with no parameters (requires a «GET» behind the scenes).
  • Tested with parameters (requires a «POST» behind the scenes).
  • Tested with a standard web page such as Google.
  • Tested with an internal Java-based webservice.

To call with no parameters (uses a «GET» behind the scenes):

To call with parameters (uses a «POST» behind the scenes):

Simple (one-liner, no error checking, no wait for response) solution I’ve found so far:

Use with caution!

If you like a fluent API you can use Tiny.RestClient. It’s available at NuGet.

When using Windows.Web.Http namespace, for POST instead of FormUrlEncodedContent we write HttpFormUrlEncodedContent. Also the response is type of HttpResponseMessage. The rest is as Evan Mulawski wrote down.

Why is this not totally trivial? Doing the request is not and especially not dealing with the results and seems like there are some .NET bugs involved as well — see Bug in HttpClient.GetAsync should throw WebException, not TaskCanceledException

I ended up with this code:

This will do a GET or POST depends if postBuffer is null or not

if Success is true the response will then be in ResponseAsString

if Success is false you can check WebExceptionStatus , HttpStatusCode and ResponseAsString to try to see what went wrong.

Yet another way of doing it:

This way you can easily post a stream.

In .net core you can make post-call with following code, here I added some extra features to this code so can make your code work behind a proxy and with network credentials if any, also here I mention that you can change the encoding of your message. I hope this explains all and help you in coding.

Here’s what i use in .NET 4.8 to make a HTTP POST request. With this code one can send multiple POST requests at a time Asynchronously. At the end of each request an event is raised. And also at the end of all request another event is raised.

Читайте также:  Файл закачки windows 10

The one bellow is the core class:

the AeonLabs.Envoriment is a class with a collection or fields and properties.

And the one bellow is for making a POST request:

Создание GET и POST-запросов с помощью Curl

cURL — это программное обеспечение, состоящее из утилиты командной строки и библиотеки, предназначенное для передачи данных по разнообразным протоколам (HTTP, FTP, POP3, IMAP, SMTP и мн. др.). Мы рассмотрим только утилиту командной строки, название которой мы будем записывать как «curl». Навыки, полученные при ее использовании, позволяют легко освоить работу с библиотекой libcurl. Кроме того, мы ограничимся формированием HTTP-запросов: GET и POST.

Предварительные сведения

Протокол HTTP

HTTP — это протокол, используемый при обмене данных между веб-сервером и программой-клиентом (например, браузером). Он состоит из строк ASCII-текста, отсылаемых от клиента к серверу для запроса какого-либо действия. При получении запроса сервер отвечает клиенту несколькими служебными текстовыми строками, а затем выдает запрошенное содержимое.

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

URL (Uniform Resource Locator — единый указатель ресурса) задает адрес определенного ресурса в Интернет. Например, URL веб-страницы cURL, записывается так: http://curl.haxx.se .

Формы

Формы представляют собой наборы полей, размещенные на веб-странице. Пользователь вводит в эти поля данные, затем нажимает на кнопку «OK» или «Отправить», после чего данные отправляются на сервер. Сервер принимает данные и решает, как поступить дальше: искать ли информацию в базе данных, показать ли введенный адрес на карте или использовать информацию для аутентификации пользователя. Разумеется, «решает» — означает, что на стороне сервера должна быть какая-то программа, которая принимает и обрабатывает присланные данные. Простейший пример: форма запроса поисковика Google.

Справка

Справку по curl можно получить, набрав в командной строке

$ — приглашение командной строки.

Получить содержимое страницы (GET)

Самый простой и обычный HTTP-запрос — получить содержимое заданного URL. URL может ссылаться на веб-страницу, изображение или какой либо-другой файл. Клиент отсылает GET-запрос на сервер и получает запрашиваемый документ. Если выполнить команду

вы получите веб-страницу, выведенную в окно терминала (точнее, в стандартный вывод). Чтобы сохранить эту страницу в файл curl.html , нужно указать

Все HTTP-ответы содержат набор заголовков, которые обычно скрыты. Чтобы увидеть эти заголовки вместе с самим документом, используйте ключ -i .

Отправить GET-запрос из формы

Форма запроса может использовать метод GET. Например, так:

Если вы откроете этот код в браузере, то увидите форму с текстовым полем и кнопкой с надписью «OK». Если вы, например, введете в форму ‘1990’ и нажмете «OK», то браузер создаст новый URL, по которому и проследует. Этот URL будет являться строкой, состоящей из предыдущего URL и строки запроса, вроде следующей: foo.cgi?year=1990&press=OK . Так, если форма располагалась по адресу www.foo.com/year.html (адрес взят «с потолка»!), то при нажатии на кнопку «OK» вы попадете на URL www.foo.com/foo.cgi?year=1990&press=OK .

Для формирования GET-запроса, введите то, что ожидалось от формы:

Метод POST

Метод GET приводит к тому, что вся введенная информация отображается в адресной строке браузера. Очевидно, что это не самый лучший способ в тех случаях, когда нужно отправить конфиденциальные данные или когда объем введенной информации очень велик. Для решения этой проблемы протокол HTTP предоставляет пользователю еще один метод — POST. С его помощью клиент отправляет данные отдельно от URL, и поэтому в адресной строке браузера вы их не увидите.

Форма, генерирующая POST-запрос, отличается от предыдущей лишь методом отправки:

curl сформирует POST-запрос с теми же данными следующим образом:

Обратите внимание на то, что данные, которые вы отправляете серверу, должны быть правильно закодированы. curl не сделает этого за вас. К примеру, если вы хотите, чтобы данные содержали пробел, то вам нужно заменить этот пробел на %20 и т. п. Это одна из самых распространенных ошибок, в результате чего данные передаются не так, как нужно.

Загрузка файлов с помощью POST

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

Заметьте, что тип содержимого Content-Type установлен в multipart/form-data .

Чтобы с помощью curl отослать данные в такую форму, введите:

Скрытые поля формы

Скрытые поля в формах являются одним из наиболее распространенных способов передачи информации о состоянии HTML-приложения. Такие поля не заполняются, они невидимы для пользователя, но передаются так же, как и обычные поля.

Пример формы с одним видимым и одним скрытым полями:

Чтобы отправить POST-запрос с помощью curl, вам не нужно задумываться о том, скрыто поле или нет — для curl все поля одинаковы:

Как выглядит POST-запрос «изнутри»

Когда с помощью curl вы заполняете форму и отсылаете ее данные на сервер, вы наверняка хотите, чтобы сформированный curl POST-запрос выглядел так же, как если бы он был выполнен с помощью браузера.

Простейший способ увидеть свой POST-запрос заключается в следующем:

  1. сохранить веб-страницу с формой на диск;
  2. изменить метод на GET;
  3. нажать кнопку «Отправить» (вы можете также изменить URL, которому будет передаваться данные).

Вы увидите, что данные присоединились к URL и разделены символами ? , как это и предполагается при использовании GET-форм.

Читайте также

Комментарии

Дмитрий Храмов
Компьютерное моделирование и все, что с ним связано: сбор данных, их анализ, разработка математических моделей, софт для моделирования, визуализации и оформления публикаций. Ну и за жизнь немного.

POST запросы к нашему HTTP серверу

Зачем нам может понадобится вообще говоря POST запросы. Причина банальна : хочется передавать данные объемом побольше чем позволяет один GET запрос.

Все ,что ниже описано связано с несколькими сегментами (составляющими post запрос) , которые отправляет клиент серверу.

И тут выясняется ,что реализации поддержки POST запросов в LWIP 1.4.1 нет , объявления post функций вроде есть , а реализации нет . А вот в LWIP 2.1.2 уже реализация post есть, но поскольку у нас с 2.1.2 отношения никак не складываются, будем переносить код из 2.1.2 в 1.4.1.

Поддержка post запросов находится в 2 файлах post.c и post.h. Author: Joakim Myrland.

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

Тестировать работу сервера удобно программой Postman.

Вот такое содержание приходит при post запросе (тип передаваемых данных — json) . Это случай когда вся посылка влезет в размер одного сегмента:

И тут есть проблема с content-length :, так как LWIP проверяет Сontent-length : с большой буквы. Сравнение срок в LWIP происходит в самописной функции strnstr .

Исправляем строку
if ( ( * p == * token ) && ( strncmp ( p , token , tokenlen ) == 0 ) )
на
if ( ( * p == tolower(* token ) ) && ( strncmpi ( p , token , tokenlen ) == 0 ) )
и все начинает работать нормально. То есть content-length нормально отрабатывается. Похоже Postman посылает некорректно. Браузер вроде нормально.

Читайте также:  Hacking wifi from linux

Чтобы реально въехать в логику работы надо изучить вот такую (из LWIP) структуру tcp_pcb. Каждый элемент имеет важное значение. Для каждого TCP соединения создается отдельная своя структура tcp_pcb.

Пересылка больших данных

Если данные не помещаются в одном пакете (сегменте) [1460 байт], то логично , что отправитель (клиент-браузер) разбивает их на несколько пакетов (сегментов).

Передача клиентом ведется пачками (порциями сегментов) . Одна такая пачка равна размеру окна (TCP_WND), который сервер устанавливает клиенту в ответе SYN/ACK. После такой пачки сервер должен ответить ACK пакетом с нулевой длиной, чтобы дать клиенту понять ,что он (сервер принял) пачку нормально. В этом же пакете сервер может указать новый размер окна (новый размер порции) для следующей пачки. Сервер может также указать размер окна равным 0 , чтобы проинформировать клиента , что он (сервер) занят чем-то и не готов принимать данные.

В tcp обмене пакетами , когда клиент хочет послать их много, происходит полная непоследовательность получения на стороне сервера. Это происходит в основном благодаря тому , что клиент не дождавшись ответа от сервера начинает повторно их посылать (retransmission) и даже уже в другом порядке. И даже уже может разбивать их контент по другому. В результате сервер остается один на один c частью пакетов , которые реально дошли и пытается их собрать в единую посылку (в пределах текущего окна TCP_WND).

Рекомендуемое время задержки для ретрансмиссии пакетов вроде 25 ms (но это не точно). Для нас это очень маленькое время и мы (сервер) не успеваем ответить .

Теперь о главном — чтобы понять логику работы tcp соединения на стороне сервера (то есть в LWIP 1.4.1) надо понять когда и в каких регионах памяти выделяется память для отработки обмена по tcp. И вот тут выясняется , что в этом процессе (по крайней мере на прием) участвует несколько несколько регионов памяти : [TCP_PCB] , [TCP_PCB_LISTEN] , [TCP_SEG] , [PBUF_POOL] . Эти регионы обязательно надо ослеживать и тогда складывается такая примерно картина:

TCP_PCB_LISTEN

Сначала при инициализации tcp мы создаем в [TCP_PCB_LISTEN] блок памяти (со структурой tcp_pcb_listen) , в которой мы просто указываем какой порт мы слушаем.

PBUF_POOL

Каждый приходящий пакет из сети ethernet сначала попадает в регион памяти PBUF_POOL . Блок памяти предваряется структурой struct pbuf . Размер блока памяти равен PBUF_POOL_BUFSIZE
#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN)
1460+40+14=1514 байт.

То есть в PBUF_POOL сохраняется содержание всего пакета (заголовки , данные).

TCP_PCB

Далее сервер создает в регионе [TCP_PCB] блок памяти (со структурой tcp_pcb), что является фисксацией нового соединения по tcp с клиентом.

Еще при создании нового соединения мы создаем отдельный таймер для нашего tcp соединения. То есть добавляем в регион [SYS_TIMEOUT] новый блок памяти. Интервал устанавливается TCP_TMR_INTERVAL, то есть 250мс. Этот таймер обслуживает только открытые tcp соединения.

Сервер отвечает клиенту SYN/ACK пакетом.

Далее Сервер получает от клиента ACK пакет нулевой длины . Но не факт на самом деле. Сервер получает тот пакет , который успевает получить , а перед этим идущие пакеты могут для сервера пропасть, просто сервер не успевает их принять , так как чем-то занят. То есть сервер может и не получить ACK нулевой длины (как бы он пропадет), а получить следующий пакет уже с данными и даже не первый по порядку (в пределах текущего окна) например с флагами PSH/ACK. PSH — это в последнем сегменте посылается (текущего окна).

В любом случае после получения ACK от клиента сервер создает блок памяти в регионе [HTTPD_STATE], чем обозначает открытие соединения http. Тут размещена структура http_state, в которой есть указатель tcp_pcb * pcb . Ему присваиваем значение адреса на блок памяти в регионе [TCP_PCB] (нашего текущего соединения).

TCP_SEG

Когда приходит очередной сегмент и он не вписывается в последовательность передаваемых сегментов клиента , то это out-of-sequence сегмент и его надо сохранить до будущих времен (см.ooseq).

Для этого есть регион [TCP_SEG]. Здесь создается блок памяти со структурой tcp_seg . В tcp_seg структуре устанавливаем ссылку на [PBUF_POOL] (где находится содержание принятого пакета). В структуре tcp_pcb есть указатель на цепочку сегментов , которые пришли не по порядку ooseq. Добавляем в цепочку ooseq ссылку на наш tcp_seg.

unacked

Также [TCP_SEG] используется для учета не подтвержденных пакетов от сервера клиенту. То есть клиент должен подвердить посланный пакет от сервера , а пока сервер при посылке разместил содержание такого сегмента в [MALLOC_256] и мы его пока не удаляем. Далее создаем в [TCP_SEG] блок памяти под tcp_seg структуру . Делаем ссылку в tcp_seg на содержание [MALLOC_256]. Добавляем в цепочку unacked tcp_pcb структуры регион [TCP_PCB] ссылку на наш tcp_seg. И ждем когда клиент подтверит ACK пакетом наш пакет от сервера.

Это происходит , например, когда приходит первый пакет SYN от клиента к серверу . Таким образом мы увидим в [TCP_SEG] ссылку на наш первый ответ ACK/SYN (в ответ на первый принятый сегмент SYN).

Таким образом в блоке памяти региона [TCP_PCB] , есть связанные цепочки tcp_seg . Цепочка указателей на неподтвержденные клиентом пакеты unacked (tcp_seg *) . Ту мы учитываем не подтвержденые пакеты от сервера клиенту. В ooseq учитываем пакеты от клиента вне последовательности.

Делаем стандартные проверки при получении пакета с ACK :

unacked

Смотрим цепочку unacked в tcp_pcb и удаляем все сегменты вместе с их контентом из [TCP_SEG] и [MALLOC_256], то есть удаляем из [TCP_SEG] сегменты , ожидавшие подтверждения и потом из unacked саму ссылку ссылку на сегмент. Причем удаляются почему-то все элементы в цепочке unacked.

unsent

Далее проверяется цепочка unsent в tcp_pcb, но у нас там еще ничего нет. Это используется для данных , отправляемых сервером клиенту.

Далее идет разбор содержания принятого сегмента (tcplen>0) и что с ним делать. Проверяется попадает ли пришедший сегмент в текущее окно по его параметру seq и переменной rcv_nxt; (next seqno expected — ожидаемое seq в очередном пакете от клиента).

Надо сказать именно эта часть кода (функция tcp_receive) подверглась сильному изменению от версии 1.4.1 к 2.1.2.

Итак сначала проверяем наш пришедший сегмент на предмет , что его seq где-то в пределах нормального (seqno). Тут не совсем понятно.

Следующая проверка уже очевидная — попадает ли принятый сегмент в текущее окно.

Если принятый сегмент попадает прямо как надо (по порядку) (и перед этим не было out-of-sequence сегментов), то есть ожидаемый номер rcv_nxt точно равен принятому seqno, то остается проверить только , что размер сегмента не выходит за пределы текущего окна. Если правый край выходит за пределы окна , то обрезаем его по значению правого края окна. Далее проверяем ooseq (если он не пустой) на предмет полученных out-of-sequence пакетов ранее и если такие имеются.

Если принятый сегмент попадает в текущее окно , но не по порядку следования сегментов, то есть seqno больше ожидаемого значения (rcv_nxt) или уже была нарушена последовательность сегментов (в пределах окна), то это сегмент out-of-sequence (ooseq), то есть сегмент вне последовательности , но в пределах окна . Это просто один из следующих (или предыдущих) пакетов. Мы сохраняем сегмент в [TCP_SEG] , а его контент в оставляем в [PBUF_POOL]. И помещаем ссылку на него в цепочку ooseg нашего соединения tcp_pcb. Причем в цепочке ooseg мы размещаем сегменты по порядку следования (по Seq).

Читайте также:  Windows 10 синий экран ntfs file system

Работа с цепочкой ooseq заключается в следующем : мы начинаем раскручивать цепочку с головы и проверяем , что seqno пришедшего сегмента равен одному из сегментов цепочки. Если длина пришедшего сегмента почему-то больше сегмента в цепочке , то мы заменяем сегмент в цепочке нашим пришедшим.

rcv_nxt

Основополагающим параметром является rcv_nxt (следующий ожидаемый seq от клиента). По-поводу него можно сказать , что изначально он устанавливается в функции tcp_listen_input по приходу SYN от клиента. Устанавливается rcv_nxt на единицу больше seq от пришедшего в SYN. Далее этот параметр строго увеличивается , но только с теми сегментами , которые четко попадают в последовательность . Сегменты вне последовательности отбрасываются в цепочку ooseq для временного хранения (до востребования).

В ooseq кстати сегменты размещаются по порядку следования параметра Seq.

Там же при инициализации устанавливается еще один параметр npcb->snd_wl1 = seqno — 1; . В последующем snd_wl1 используется вместе с rcv_nxt.

Функция tcp_update_rcv_ann_wnd устанавливает параметр размер окна для каждого посылаемого сегмента от сервера клиенту.

tcp_update_rcv_ann_wnd

Очень не просто для понимания, но это функция в результате устанавливает значение rcv_ann_wnd , которое является выходным параметром WIN каждого сегмента , отправляемого сервером клиенту.

Тут есть важная логика — надо всегда отправлять в WIN значение еще не принятой части текущего окна (это в нулевых ACK пакетах). Например окно размером у нас 1460*3. Если мы приняли первый сегмент (реально первый по номеру seq [1460]) , то мы можем ответить пустым ACkом с параметром WIN=1460*2=2920 , чтобы указать клиенту таким образом , что мы не приняли 2 и 3 последние пакеты. Вообще говоря это дополнительный контроль , так как мы в пакете уже однозначно определяем какой пакет мы подтверждаем (по параметрам Ack и Seq).

Как только мы (сервер) получаем все пакеты (1,2,3=1460*3) , то подтверждаем прием клиенту от сервера пакетом с уже новым WIN , который можно установить произвольно по итогам предыдущего обмена. Таким образом мы открываем новое окно. И все далее повторяется сначала.

Для начала надо включить #define LWIP_HTTPD_POST_MANUAL_WND 1 .

httpd_post_begin должен срабатывать один раз при приеме первого пакета данных.

После приема очередного пакета сервер должен отдавать tcp_send_empty_ack. И вот тут в параметрах tcp ответа можно указать , что мы заняты или наоборот готовы к приему стольки-то данных.

Если все данные приняты (все сегменты получены), то произойдет вызов httpd_post_finished.

Еще есть такая интересная строчка u8_t post_auto_wnd = 0; // !! 1; . К ней есть инфа в доке : post_auto_wnd.
Set this to 0 to let the callback code handle window updates by calling ‘httpd_post_data_recved‘ (to throttle rx speed) default is 1 (httpd handles window updates automatically) .

Но httpd_post_data_recved нигде не вызывается (в LWIP 1.4.1).

tcp_receive

Первое , что отрабатывает функция tcp_receive это проверяет ACK от клиента , то есть установлен ли ACK флаг в сегменте. Если установлен , то значит клиент что-то подтверждает нам (серверу), то есть отвечает на нашу посылку перед этим.

Чтобы разобраться со всем этим , делаем трассировку с выводом каждого значения , которое меняется в структуре tcp_pcb.
Плюс подключаем WireShark и видим , что не все так понятно и идеально, хотя клиент получает запрашиваемую страницу.

Пора сказать , что все приходящие пакеты так или иначе сохраняются в памяти (у нас в PBUF_POOL). Для каждого TCP соединения предусмотрены цепочки сегментов разных типов , сохраняемые как структуры tcp_seg (параметр next указатель на следующий):

unacked — неподтвержденные сегменты (здесь учитываем неподтвержденные клиентом посылки от сервера), прямо с пакета SYN/ACK сервера и начинается. По мере подтверждения клиентом пакет сервера очищается из памяти (сервера).

unsent — не посланные сегменты (кто/куда?), по-видимому это уже ответы сервера клиенту в пределах этого tcp соединения и похоже это в варианте retransmission

ooseq — принятые сегменты , но не порядку посылки клиентом. Механизм сборки включается по дефайну TCP_QUEUE_OOSEQ.

Есть несколько глобальных переменных , которые участвуют в процессе:

seqno (глобальная переменная) — она учитывает текущий номер Seq , каждый вновь принятый пакет устанавливает этот параметр (см. функцию tcp_input).
ackno — аналогично запоминает параметр Ack последнего принятого пакета (см. tcp_input).

flags — тут сохраняется состав флагов входящих пакетов
lastack — это последний (сырой) ACK, который мы (сервер) получили от клиента.
rcv_ann_wnd — именно это значение попадает в поле WIN (размер окна) у сегмента , посылаемого сервером клиенту.
rcv_nxt — ожидаемое хначение Seq в пакете от клиента серверу.

Логика проверки получения всех данных post запроса основана на контроле количества оставшихся не принятых байт post_content_len_left.

post_content_len_left

Первоначальное значение post_content_len_left естественно устанавливается равным content-length: заголовка из первого пакета.

Когда post_content_len_left станет =0 сработает функция http_handle_post_finished и далее вызовется http_send уже для отслылки данных клиенту.

LWIP_HTTPD_POST_MANUAL_WND

LWIP_HTTPD_POST_MANUAL_WND мы не используем.

http_recv

Эта функция срабатывает , когда приходит сегмент от клиента , который является следующим по порядку следования (см. seqno и rcv_nxt). На сегменты отбрасываемые в ooseq эта функция не срабатывает. Её вызов спрятан в макросе :
TCP_EVENT_RECV( pcb , recv_data , ERR_OK , err );

http_post_rxpbuf

http_post_rxpbuf — функция , куда попадают уже только данные (тело) post запроса. Здесь мы приходящие порции данных закидываем себе в приемный буфер.

Логгирование

Вообще говоря есть очень неплохая идея трассировки (логгирования) отработки функций TCP стека по принципу иерархического списка . Визуально логика очень легко читается.

Какие ошибки портят жизнь программистам ?

Например :
pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
приводит иногда к неправильному (с нашей точки зрения) результату. Надо :
pcb->rcv_ann_right_edge = pcb->rcv_nxt + (uint32_t )pcb->rcv_ann_wnd;
Думаю комментарии излишни. В чем суть понятно.

Представляем рабочий вариант отправки клиентом 5 сегментов. MSS 1460 , TCP_WND 4380 (3 сегмента) :

На картинке выше сервер после получения каждолго сегмента отвечает клиенту ACK пакетом нулевой длины (это нормально).

Ниже можно скачать лог выполнения запроса , представленного на картинке выше. Для убыстрения поиска обращайте вниминие на значение контрольной суммы пакета в WinShark. Далее в текстовом логе по этому значению можно быстро найти этот пакет.

Далее меняем размер окна MSS 1460 , TCP_WND 2920 , пересобираем проект и все опять работает нормально.

Выводы

На данном этапе можно константировать , что наш сервер стабильно принимает 5 сегментов за пару секунд (при полном логгировании) . Память не утекает — все блоки логично очищаются. И это радует !

Можно отметить , что делать TCP_WND менее двух MSS нельзя, но делать более двух MSS при наличии большого количества повторных передач тоже никакого смысла нет.

Файлы для скачивания

* USB_RNIDS_POST 5SEGMENTS_OK [zip]
лог вызовов функций post запроса на стороне сервера (LWIP 1.4.1)

Оцените статью