Решил написать простенькую статейку по следам реализации небольшой программки на С++ под Виндоус, которая содержит в себе TCP сервер. Мы получаем от клиента http запрос (соединение не защищенное).

На чем реализован клиент нам неизвестно: может на php (curl,socket,stream_contex_create,...), может на js (ajax), вообще может быть на чем угодно.

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

Наша задача реализовать на сервере http парсинг запроса, выполнить задание (на каком-то подключенном к серверу оборудовании) и ответить клиенту о результате.

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

Прием байт

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

Отделяются заголовки от тела четырьмя байтами \r\n\r\n. Первое вхождение в принятом потоке байт \r\n\r\n это разделитель. Все что после разделителя - это тело, причем в теле конечно же также могут попадаться последовательности байт \r\n\r\n, в теле вообще может быть все что угодно.

Надо сразу понимать, что связь может быть и очень плохой. На что это повлияет? Сразу заметите, что байты начнут поступать меньшими порциями. Возникнет вопрос сколько вообще надо ждать прихода всех байт? И тут есть нюанс: в tcp передаче сначала устанавливается соединение. QTcpServer выпускает нам сигнал newConnection() и сразу tcp сервер начинает получать данные, которые мы должны считывать через вызов readAll() сокета.

Можно уже догадаться, что QTcpServer создает отдельный поток для каждого клиента, но речь не об этом.

Нам надо как-то ждать в нашем потоке программы, не блокируя желательно нашу программу. Для этого у сокета есть например waitForReadyRead(int timeout).

И вот мы подходим к вопросу какой timeout нам использовать. Но надо ответить на вопрос, что мы хотим за этот таймаут получить: весь запрос или только заголовки.

Получение заголовков

Так вот логично сначала получить полностью все заголовки, то есть начальные данные запроса, где найдется первое вхождение \r\n\r\n. Почему? Потому, что в заголовках мы должны получить значение Content-Length, то есть длину тела. И соответственно мы будем далее знать сколько байт точно нам должно прийти в теле.

Для реализации этого первого этапа, на наш взгляд, лучше использовать waitForReadyRead с небольшим таймаутом, например 2000мс, но вызывать его несколько раз, например 30 раз.

Почему так делаем? Если данные не придут вообще от клиента, мы будем ждать соответственно 2000*30 = 60000мс. Этого по-моему достаточно даже для самой плохой сети. Если заголовки, как это обычно происходит, придут в первые 2000мс, то мы распарсим найдем \r\n\r\n, определим Content-Length и перейдем к следующему этапу: получение тела (или остатка от тела, как получится).

Если в первые 2000мс мы не получим \r\n\r\n, то далее мы будем ждать еще 2000мс и если разделитель \r\n\r\n придет в этот раз, то общая продолжительность первого этапа получится от 2000мс до 4000мс. Вообще заголовки небольшие по объему и приходят обычно практически сразу после установки tcp соединения, но подстраховаться таким образом обязательно надо на случай плохой сети.

Да хотел отметить, что наш сервер это приложение под виндоус, которое пользователь устанавливает где-то себе на компьютере и какие там условия по скорости в сети нам не известно. И клиента пользователь реализует как его душе удобно. Поэтому мы вообще не знаем на каких скоростях там все будет работать (vpn,proxy,брандмауэры,роутеры...).

Получение тела запроса

Если мы не получим Content-Length в заголовках, такое тоже может быть, когда например клиент реализован на сокетах и разработчик клиента просто не передал Content-Length , то мы очевидно должны ждать всех данных какое-то длительное неопределенное время, ну например 30000мс, чтобы предположить, что клиент послал все свои данные. Но лучше в своем АПИ для клиентов четко прописать, что Content-Length обязателен и если его нет возвращать например 400 Bad Request.

Кстати, если кто-то не знал, 400-тые возвраты тоже могут содержать тело, например с описанием ошибки, и лучше конечно информацию об ошибке передавать.

Далее рассматриваем только ситуацию, когда мы знаем размер тела. Это в байтах если что. Почему я это отмечаю, потому что если в теле вы увидите в логах например русские буквы, то знайте, что на русский символ (в UTF-8) приходится 2 байта.

Я уверен вам захочется посчитать длину пакета. Чтобы было вам проще можно открыть лог в Notepad++, выделить тело и посмотреть Summary (где-то в меню есть), там будет значение в байтах.

Также там же например в notepad++ можно включить просмотр всех символов и обратите внимание на переводы строк в запросе: вместо \r\n\r\n вы можете увидеть только \n\n. Я не знаю надо ли поддерживать в сервере вариант \n\n в качестве разделителя заголовков и тела, но чисто технически это не проблема сделать. Пока мы у себя поддерживаем только вариант \r\n\r\n.

Лог сервера

Мы подошли к важному моменту в понимании проблем с кодированием данных в теле запроса. При чем тут лог сервера спросите вы?

По нашему скромному мнению, чтобы быстро, раз и навсегда, разобраться c пониманием как работает кодирование тела запроса через разные варианты типа urlencoded, json, base64 и даже hex в разных клиентах на php (или js) надо обязательно смотреть сырые логи сервера.

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

Вот сразу пример из жизни: у пользователя нашей программы клиент был реализован на старом  наборе php 5.6 curl 7.29.0,  который менять ему было нельзя (кастомный сервер). И оказалось, что сервер отдает в теле json строку примерно такую "[ {...}, {...} ]", а curl клиента теряет где-то при приеме первую квадратную первую скобку и исправить это оказалось нельзя никак как вы понимаете. Но проблему решили просто - переходом на сокет в php, все заработало сразу нормально.

Теперь почему на нормальном сервере (например апаче) трудно посмотреть сырые http логи? Я не уверен, но наверное это прямая уязвимость в защите данных?...

В общем отрабатывать кодирование тела рекомендую на любых серверах с открытым http логом. Например как в нашем случае. Я бы мог здесь разместить ссылку на нашу коммерческую прогу, с бесплатным 2 недельным периодом, где это реализовано, но боюсь меня забанят (такое уже было раз).

Если вы знаете такие сервисы просьба в комментариях ознакомить хабровчан. Но вообще проще написать самому. Если кому-то понадобится могу открыто выложить на гитхабе пример такого тестового сервера (на С++ Qt).

Так вот теперь на данном этапе мы точно видим логи сервера и можем дальше изучать какие варианты кодирования можно реализовать в обмене нашего сервера и клиента.

Кодирование

А далее вдруг все становится просто до не приличия. Именно на этом этапе вы быстро разберетесь почему может не работать ваш запрос. Рассмотрим клиента на php.

Надо отметить, что на php много реализаций http клиентов curl не единственный, мне по крайней мере известен еще stream_context_create, socket_create.

Content-Type

В примере ниже мы реализуем 4 варианта кодирования на выбор.

Наш сервер по заголовку Content-Type определяет тип передаваемого контента, далее сервер знает что-делать. Если Content-Type не поддерживается сервер возвращает 4хх ответ.

Скрытый текст
<?php
$unic_id = mt_rand();

$postdata = array( 
	array(
	'name'=>'2. Фискализируем чек',
	'type'=>'kktReceiptFiscalization',
	'data'=>array(
		'1059'=>array(
			array(
				'productName_1030'=>'Отладка программы ',
				'price_1079'=>0,
				'qty_1023'=>1,
				"amount_1043"=>0,
				'unit_2108'=>0,
				'paymentFormCode_1214'=>4,
				'productTypeCode_1212'=>1,
				'tax_1199'=>6
			)
			array(
				'productName_1030'=>'Отладка программы ',
				'price_1079'=>0,
				'qty_1023'=>1,
				"amount_1043"=>0,
				'unit_2108'=>0,
				'paymentFormCode_1214'=>4,
				'productTypeCode_1212'=>1,
				'tax_1199'=>6
			)
		),		
		'cashierName_1021'=>'Пупкин Иван Трофимович',
		'cashierInn_1203'=>'',
		'payments'=>[
					  'cash_1031'=>0,
					  'ecash_1081'=>0,
					  'prepayment_1215'=>0,
					  'credit_1216'=>0,
					  'barter_1217'=>0
					],
		'taxationType_1055'=>1,
		'receiptType_1054'=>1,
		'sendToEmail_1008'=>'kkmspb2008@yandex.ru',
		'printDoc'=>true
	)
	)
);


$ch = curl_init(  'http://109.188.142.134:44736' );

$CntType = "json";
//$CntType = "urlencoded";
//$CntType = "base64";
//$CntType = "hex";

if( $CntType == "json" )
{
	$ContentType = 'Content-Type: application/json; charset=UTF-8';
	//$encoded = json_encode( $postdata ,  JSON_PRETTY_PRINT ); так теперь можно тоже 
	$encoded = json_encode( $postdata , JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); // так теперь можно 
	//$encoded = json_encode( $postdata ); // так ОК  // все переносы, табуляции, русские символы экранируются , и все в одну строку получается
}
else if( $CntType == "urlencoded" )
{
	$ContentType = 'Content-Type: application/x-www-form-urlencoded';
	// сначала array в строку
	$encoded = json_encode( $postdata , JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); // так теперь можно 
	// потом строку еще кодируем
	$encoded = rawurlencode($encoded);
}
else if( $CntType == "base64" )
{
	$ContentType = 'Content-Type: text/plain base64';
	// сначала array в строку
	$encoded = json_encode( $postdata , JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); // так теперь можно 
	// потом строку еще кодируем
	$encoded = base64_encode($encoded);
}
else if( $CntType == "hex" )
{
	$ContentType = 'Content-Type: text/plain hex';
	// сначала array в строку
	$encoded = json_encode( $postdata , JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); // так теперь можно 
	// потом строку еще кодируем
	$encoded = bin2hex($encoded);
}


curl_setopt( $ch, CURLOPT_POSTFIELDS, $encoded );

curl_setopt( $ch, CURLOPT_POST, 1);

curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );

curl_setopt( $ch, CURLOPT_HTTPHEADER, array(
			$ContentType,
			'Action: command_list',
			'BIT_ENCODE_TYPE: PHP', 
			'BIT_ORDER_ID: 122',
			'BIT_KKT_TOKEN: 435cb88c28fc49bd419d58d4b60680b5' // атол 1.05 435cb88c28fc49bd419d58d4b60680b5  
        ) );

$result = curl_exec($ch);

curl_close($ch);

echo "
	<h2>Ответ</h2>
	<pre>$result</pre>";


echo "
	<h2>\n послали:</h2>
	<pre>" . $encoded."</pre>";
?>

В примере выше на php мы подготавливаем тело запроса изначально в виде объекта как массив array(..), но если быть точным у нас все-таки список, а в нем уже элементы это ассоциативные массивы. Потом мы кодируем этот объект в json строку. Но тут очень сильно влияют опции функции json_encode JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE:

Но если на сервере вы используете правильный парсинг json строки, то вам это безразлично. JSON_UNESCAPED_UNICODE будет менять русские символы на вариант \uxxxx, значит сервер должен это правильно декодировать обратно в русские символы.

JSON_PRETTY_PRINT добавляет переносы и табуляции, красиво разворачивая дерево json для более приятного отображения. Но если на вашем сервере декодер принимаемой json строки умеет парсить \n\t, то это тоже не проблема.

Тут важно понимать еще, что в CURLOPT_POSTFIELDS мы пишем просто строку или набор байт (другими словами). Это проще для понимания. Хотя можно в CURLOPT_POSTFIELDS и array(..) пихать, curl это понимает и чего-то там кодирует дополнительно. Но вот меня лично это больше путает.

Если мы кодируем передаваемый объект в строку так (без опций): $encoded = json_encode( $postdata ); ,то на сервере примем примерно так:

Скрытый текст
"POST / HTTP/1.1
Host: 109.16.14.2:44735
Accept: */*
Content-Length: 992
Content-Type: application/json; charset=UTF-8

[{"name":"2. \u0424\u0438\u0441........"}]

В примере выше вся содержание тела только в отображаемых символах ASCII, никаких управляющих байтов или русских символов.

Если мы кодируем в строку с JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)

$encoded = json_encode( $postdata , JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

то на сервере мы увидим и русские буквы и табуляцию и переносы , примерно так:

Скрытый текст
"POST / HTTP/1.1
Host: 10.168.14.2:44735
Accept: */*
Content-Type: application/json; charset=UTF-8
Content-Length: 1052
Expect: 100-continu

[
    {
        "name": "2. Фискализируем чек",
        "type": "kktReceiptFiscalization",
        "data": {
            "cashierName_1021": "аааааааааа-ббббббббббб сссссссс ыыыыыыыыыы",
            "cashierInn_1203": "007826152874",
            "payments": {
                "cash_1031": 0,
                "ecash_1081": 0,
                "prepayment_1215": 0,
                "credit_1216": 0,
                "barter_1217": 0
            },
            "taxationType_1055": 1,
            "receiptType_1054": 1,
            "printDoc": true,
            "1059": [
                {
                    "productName_1030": "Отладка программы код№2",
                    "price_1079": 0,
                    "qty_1023": 1,
                    "amount_1043": 0,
                    "unit_2108": 0,
                    "paymentFormCode_1214": 4,
                    "productTypeCode_1212": 1,
                    "tax_1199": 6
                }
            ]
        }
    }
]" 

И первый и второй варианты работать будут нормально, так как тело запроса это просто байты. Но проблема может быть на сервере, сервер должен уметь декодировать json строку правильно. То есть декодер сервера должен понимать все экранирование и кодирование символов в соответствиe со стандартом json. Но это отдельная тема.

rawurlencode

И вот далее мы можем нашу строку еще дополнительно как бы закодировать через rawurlencode или base64 и даже через hex. Эти варианты делаются, чтобы хоть какой-то вариант заработал у клиента.

Что касается base64 и hex то в Content-Type: мы указываем примерно так Content-Type:text/plain base64 или так Content-Type: text/plain hex. Я сразу скажу, что я не знаю как правильно указывать, но могу сказать что наш сервер просто ищет в значении Content-Type вхождение urlencoded, base64, json, hex и если находит, то воспринимает принятое тело соответственно.

Какие неожиданные проблемы у нас возникали в обмене

Надо добавить, что если у вас все работает (на вашем макете): сервер на вашем ПК, клиент на каком-то вашем хостинге, то это не значит, что у пользователя все будет также прекрасно работать.

Вот пример заголовка, который заставил переписать алгоритм приема запроса сервером:

Вдруг оказалось, что у пользователя клиент передает какой-то неопознанный заголовок Expect: 100-continue. И далее, как я понял, клиент ждет от сервера разрешение дальнейшей передачи данных. Пришлось дополнительно анализировать приходящие заголовки и при получении Expect: 100-continue отдавать клиенту HTTP/1.1 100 Continue.

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

Заголовок Accept

Наблюдательные читатели конечно заметили заголовок

Accept: */*

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

Accept: application/json

Например заголовок выше означает, что клиент хочет получить данные именно в формате json. И по хорошему сервер должен анализировать просьбу клиента и если это возможно отдавать данные в том типе кодирования, которое попросил клиент.

В случае если сервер не может отдать ответ в таком типе, надо возвращать соответствующую ошибку из набора 4хх вариантов (например 406 Not Acceptable). И очень желательно в теле ответа посылать описание ошибки и что должен попробовать исправить клиент.

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

Еще раз хочу отметить, что эта статейка не претендует на какую-то исчерпывающую информацию о HTTP протоколе и автору откровенно говоря некогда и лень изучать все нюансы протокола HTTP. Поэтому прошу еще раз не судить строго. Может описанные здесь некоторые наблюдения из практического опыта будут полезны начинающим программистам.

Если у кого есть конструктивные замечания по протоколу http буду признателен. В особенности по вопросу какие еще проблемы могут появится у меня на этом пути.

Примечание: автор реализует http сервер на устаревшем Qt4, используем QTcpServer и QTcpSocket (но для нашего http сервера это не принципиально).

Комментарии (36)


  1. x89377
    17.05.2025 17:13

    Просто машина времени ! 2005 год.


    1. kkmspb Автор
      17.05.2025 17:13

      Это вы про Qt 4 или curl 7.29.0?


      1. fcoder
        17.05.2025 17:13

        про небезопасный к перехвату траффика протокол http, устаревшей версии 1.1 например


        1. kkmspb Автор
          17.05.2025 17:13

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

          К тому же мы передаём токен кассового аппарата для идентификации оборудования.

          И можем ещё хэш код проверки целостности данных передавать и на сервере проверять.


          1. zlat_zlat
            17.05.2025 17:13

            Вообще, довольно любопытно полагаться на «кто захочет пробивать чеки на наших ккт». Ну, предположим, что я захочу, и пробью вам 8 миллиардов рублей, удачи с налоговой.


            1. kkmspb Автор
              17.05.2025 17:13

              Вообще, довольно любопытно полагаться на «кто захочет пробивать чеки на наших ккт». Ну, предположим, что я захочу, и пробью вам 8 миллиардов рублей, удачи с налоговой.

              А кож проверки целостности данных как узнаете, он только в ЛК у пользователя виден.


              1. zlat_zlat
                17.05.2025 17:13

                Так я и говорю, что желание-то может возникнуть. А вы пишете, «зачем и кто захочет».


                1. kkmspb Автор
                  17.05.2025 17:13

                  Так я и говорю, что желание-то может возникнуть.

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

                  Кстати можно не больше 21миллиона пробить.

                  Ну допустим пробили, пользователь увидел, сделал возврат конечно.

                  Далее начал пользовать использовать код проверки данных из ЛК и добавлять в данные чека непонятный хеш. Сервер проверяет этот хеш, он знает как он вычисляется , а вы нет.

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


                  1. zlat_zlat
                    17.05.2025 17:13

                    То есть по умолчанию хеш не передается, а только когда «пользователь увидел»?

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


                    1. kkmspb Автор
                      17.05.2025 17:13

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

                      Вы представляете "обычного" пользователя, ну с оборотом до 10млн.р/год. Вот у него одна касса.

                      Кому он конкурент? И кто ему на кассе захочет миллиард пробить? Кинокомедия.

                      Я программу для таких пользователей пишу.


                      1. zlat_zlat
                        17.05.2025 17:13

                        Я знаю конкурентов, у которых выручка полмиллиона в год на кассу. Правда, это на торговых автоматах, но там касса-то тоже есть. И там применяются любые методы, типа «случайно выдернуть из розетки».


              1. randomsimplenumber
                17.05.2025 17:13

                Если речь идёт о миллиардах, найдутся способы.


                1. kkmspb Автор
                  17.05.2025 17:13

                  Если речь идёт о миллиардах, найдутся способы.

                  О каких миллиардах? Не смешите...


  1. Alex5Anc
    17.05.2025 17:13

    автор реализует http сервер на устаревшем Qt4

    Сервер... http сервер... на кутях... Мсье знает толк (с)

    По тексту. Весь не осилил, но отдельные части доставляют.

    и разработчик клиента просто не передал Content-Length , то мы очевидно должны ждать всех данных какое-то длительное неопределенное время, ну например 30000мс

    Время не "неопределенное" и не "ну например", а оно четко задано в системе для tcp соединения. При неактивности сокета превышающей этот timeout, сокет закрывается системой. Вводить свои таймауты имеет смысл только в случае если у вас есть острая необходимость дропать соединение раньше.

    а curl клиента теряет где-то при приеме первую квадратную первую скобку и исправить это оказалось нельзя никак

    Ну да... curl сволочь такая теряет... исправить нельзя... Виновато всё вокруг за исключением своих рук.

    Дальше лень ЭТО комментировать, тут слишком много перлов и боюсь что если прокомментировать всё, то по обьему оно превысит размер статьи.


    1. Sazonov
      17.05.2025 17:13

      автор реализует http сервер на устаревшем Qt4

      Это последствия обсуждений к предыдущей статье, там я старался уговорить автора указывать то, что он принципиально не хочет закапывать стюардессу :). Жаль что это в конце написано, а не вначале. Но всё равно спасибо хотя бы за это.

      По сабжу. Сравните подачу материала про разработку встраиваемого с++ сервера тут, и вот здесь: https://habr.com/ru/articles/773608/ , комментарии думаю излишни.


    1. randomsimplenumber
      17.05.2025 17:13

      Новичок, который взял qt4 и давай херячить ПО для кассы, какой то странный.


      1. kkmspb Автор
        17.05.2025 17:13

        Новичок, который взял qt4 и давай херячить ПО для кассы, какой то странный.

        У меня пользователь только-что установил этот сервер себе на ПК , сделал сам себе товароучетку в облаке на php/js/mysql и бьёт чеки из товароучетки и все у него норм.


        1. randomsimplenumber
          17.05.2025 17:13

          У меня пользователь только-что установил этот сервер себе на ПК

          Ну, ваш пользователь молодец. И вы молодец, если смогли пропихнуть HelloWorld на прод ;)


          1. kkmspb Автор
            17.05.2025 17:13

            Ну, ваш пользователь молодец. И вы молодец, если смогли пропихнуть HelloWorld на прод 

            Почему Hello world? Поддержка атол, штрих, меркуриев по одному протоколу. Банковское терминалы протокол аркус.

            Курьеры со смартфона чеки бить могут.

            Маркировка в разрешительном режиме на днях будет готова.

            По моему все варианты управления кассой реализованы.

            Плагины есть для админки ВордПресс, джумла, Битрикс.

            Чего забыли?


            1. randomsimplenumber
              17.05.2025 17:13

              Почему Hello world?

              Ну, в статье что то про новичков.. или нет?

              Чего забыли?

              Починить сервер, чтобы от неожиданного запроса не крашился


              1. alex5jb
                17.05.2025 17:13

                Починить сервер, чтобы от неожиданного запроса не крашился 

                Очень мудрое замечание!


              1. kkmspb Автор
                17.05.2025 17:13

                Починить сервер, чтобы от неожиданного запроса не крашился

                Вы вообще читаете? Чей сервер? Это у пользователя для клиента используется php на его линакс сервере, где его база товароучетки хранится и куча сопутствующих библиотек задействовано. И он не может свой сервер обновлять, вот тогда он крэшится.


                1. randomsimplenumber
                  17.05.2025 17:13

                  Я уже запутался, кто кому у вас curl, а кто сервер ;) Ваша система, ваши костыли. Работает? Ну и ок. Можно пробить каких то чеков на 21 млн? Не проблема? Ок. Курьер с мобильника может чего то пробивать? Тоже хорошо. И всё это можно уронить необычным запросом? Nice.


        1. alex5jb
          17.05.2025 17:13

          У меня пользователь только-что установил 

          и все у него норм.

          У вас несчастные случаи на стройке были? Будут. (с) Шурик


    1. kkmspb Автор
      17.05.2025 17:13

      Ну да... curl сволочь такая теряет... исправить нельзя... Виновато всё вокруг за исключением своих рук.

      Я бы хотел поверить, что curl 7.29.0? нормальный и я что-то не так делаю, но точно такой же запрос на сокете работает полноценно, а на curl-е точно такой же (вижу на логе сервера) не работает. Как об'яснить?


      1. randomsimplenumber
        17.05.2025 17:13

        Как об'яснить?

        У curl changelog длиннее бороды Карла Маркса. Читайте, что там за issues и где они исправлены


        1. kkmspb Автор
          17.05.2025 17:13

          У curl changelog длиннее бороды Карла Маркса. Читайте, что там за issues и где они исправлены

          Вы не внимательно читали. Ситуация была такова, что пользователь не мог переустановить curl на другую версию, т.к. у него кастомный линакс тянул обновления всех зависимостей и потом сервер крэшился. Надо было выкручиваться как-то по другому. Через сокет все нормально заработало.

          То есть задача стояла помочь пользователю избежать лишних проблем.


          1. alex5jb
            17.05.2025 17:13

            Ситуация была такова, что пользователь не мог переустановить curl на другую версию, т.к. у него кастомный линакс тянул обновления всех зависимостей и потом сервер крэшился.

            Ну кроме curl есть ещё wget.

            Ещё варианты:

            1. Для сильных. Собрать curl статиком.

            2. Попробовать не тянуть все зависимости, а обновить только curl.


      1. alex5jb
        17.05.2025 17:13

        Вот я вам про "теряет" и толкую, ваше "теряет" оно сферично. Я сагрился именно на символ '[' в зависимости от того куда вы потом перенаправляете он может интерпретироваться как символ regex.


    1. kkmspb Автор
      17.05.2025 17:13

      Сервер... http сервер... на кутях... Мсье знает толк (с)

      Надо пояснить, что http сервер является частью приложения под виндой, написан на С++, как все приложение соответственно. Сервер принимает по сути одно соединение и не предназначен для приема нескольких соединений одновременно.


      1. alex5jb
        17.05.2025 17:13

        Понял. В таком варианте "под виндой и далее по тексту..." принимается полностью! Открытие сокета под шиндой это совсем не socket в *nix системах, там условно говоря 100 параметров и 500 функций которые к тому же могут (не обязательно, но могут) различаться в зависимости от версии шинды. И если есть что-то готовое для работы, то лучше использовать его, а не колхозить свое неповторимое.


      1. alex5jb
        17.05.2025 17:13

        Сервер принимает по сути одно соединение и не предназначен для приема нескольких соединений одновременно.

        И я даже догадываюсь почему :) Это в продолжение моего поста выше. Одно дело вызвать банальный fork, а другое работа с thread-ми под шиндой. Вроде как оно конечно тоже не слишком заморочное, но если сравнивать с fork это лютый комбайн получается :)


  1. Politura
    17.05.2025 17:13

    А почему в заголовке написано первый сервер, в чем первость-то? Или просто первый у автора?


  1. isumix
    17.05.2025 17:13

    Ктож серверы под Виндой гоняет? Там же ГУЙ, на него надо еще пару-тройку (админы поправят) гигов памяти. А зачем?


    1. kkmspb Автор
      17.05.2025 17:13

      Ктож серверы под Виндой гоняет? Там же ГУЙ, на него надо еще пару-тройку (админы поправят) гигов памяти.

      Каких гигов памяти, у меня до 100МБ все приложение в памяти.


      1. kkmspb Автор
        17.05.2025 17:13

        Посмотрел, на самом деле 34Мб (в памяти).