Вы не можете предположить, что вы можете просто позвонить recv()
один раз и получить все данные, поскольку TCP-соединение будет буферизовать только ограниченную сумму. Кроме того, вы не разбираете ни один из заголовков, чтобы определить размер тела, которого вы ожидаете. You может использовать неблокирующий сокет и продолжать читать до тех пор, пока он не блокирует, что будет работать в основном, но просто ненадежна и довольно плохая практика, поэтому я не буду документировать его здесь.
HTTP имеет способы указания размера тела именно по этой причине, и правильный подход заключается в их использовании, если вы хотите, чтобы ваш код был надежным. Есть две вещи, которые нужно искать. Во-первых, если ответ HTTP имеет значение , то это указывает, сколько байтов будет присутствовать в теле ответа - вам нужно продолжать чтение, пока у вас это не получилось. Второй вариант заключается в том, что сервер может отправить вам ответ, который использует chunked encoding - он указывает на это, включив заголовок Transfer-Encoding
, значение которого будет содержать текст chunked
. Я не буду вдаваться в кодировку chunked здесь, прочитайте wikipedia article для деталей. По сути, тело содержит небольшие заголовки для каждого «куска» данных, которые указывают размер этого фрагмента. В этом случае вы должны продолжать читать куски, пока не получите пустую, что указывает на конец ответа. Этот подход используется вместо Content-Length
, когда размер тела ответа не известен серверу, когда он начинает его отправлять.
Как правило, сервер не будет использовать как , так и закодированную кодировку, но нет ничего, что могло бы фактически остановить его, так что это тоже необходимо рассмотреть. Если вам нужно только взаимодействовать с конкретным сервером, тогда вы можете просто сказать, что он делает, и работать с этим, но помните, что вы сделаете свой код менее портативным и более хрупким для будущих изменений.
Обратите внимание, что при использовании этих заголовков вам все равно нужно читать в цикле, потому что любая заданная операция чтения может возвращать неполные данные. TCP предназначен для прекращения отправки данных до тех пор, пока приложение чтения не начнет очищать буфер, поэтому это не то, что вы можете обойти. Также обратите внимание, что каждый прочитанный может даже не содержать полный фрагмент, поэтому вам нужно отслеживать состояние размера текущего фрагмента и того количества, которое вы уже видели. Вы знаете только, чтобы прочитать следующий заголовок блока, когда вы видите количество байтов, указанное предыдущим заголовком блока.
Конечно, вам не о чем беспокоиться, если вы используете огромное количество библиотек HTTP от Python. Говоря как кто-то, кто раньше должен был выполнить достаточно полный HTTP/1.1-клиент, вы действительно хотите, чтобы кто-то другой сделал это, если возможно, - есть достаточно много сложных случайных ситуаций, и ваш простой код выше будет терпеть неудачу в много случаев. Если requests
не подходит для вас, попробовали ли вы какие-либо стандартные библиотеки Python? Для интерфейсов более высокого уровня есть urllib
и urllib2
, а httplib
обеспечивает подход более низкого уровня, который вы можете найти, позволяющий обойти некоторые из ваших проблем.
Помните, что вы всегда можете изменить код в них (после копирования в ваш локальный репозиторий, конечно), если вам действительно нужно исправить проблемы или, возможно, просто импортировать их и обезвредить ваши изменения. было совершенно ясно, что это проблема в библиотеке, а не просто ошибочное ее использование.
Если вы действительно хотите реализовать HTTP-клиент, это прекрасно, но просто имейте в виду, что это сложнее, чем кажется.
В конце концов, я всегда использовал метод сокетов SSL read()
вместо recv()
. Надеюсь, они были бы эквивалентны, но вы можете попробовать, если у вас все еще есть проблемы.