Как отключить тайм-аут HTTP POST, используя urlopen по urllib2 в Python? - PullRequest
7 голосов
/ 07 ноября 2011

Обзор

Я использую urlopen из пакета Python 2.7.1 urllib2 для выполнения HTTP POST с компьютера под управлением Windows XP на удаленный веб-сервер Apache (например, встроенный общий доступ к Интернету в Mac OS X)).Отправленные данные содержат некоторый идентификатор, данные и контрольную сумму, если все данные отправлены, сервер отвечает подтверждением.Контрольная сумма в данных может использоваться, чтобы проверить, все ли прибыло в порядке.

Проблема

Обычно это работает отлично, однако иногда интернет-соединение плохое, часто потому, что клиент, отправляющийданные используют Wi-Fi или 3G-соединение.Это приводит к потере интернет-соединения в течение некоторого произвольного количества времени.urlopen содержит опцию тайм-аута, чтобы убедиться, что это не блокирует вашу программу и может продолжаться.

Это то, что я хочу, но проблема в том, что urlopen не мешает сокету продолжать отправлять любые данныеэто все еще должно было послать, когда истекло время ожидания.Я проверил это (с помощью кода, который я покажу ниже), пытаясь отправить большой объем данных на свой ноутбук, я видел бы сетевую активность на обоих шоу активности, затем я бы остановил беспроводную связь на ноутбуке, подождалпока не истечет время ожидания функции, а затем снова включите беспроводную связь, и передача данных продолжится, но программа больше не будет прослушивать ответы.Я даже пытался выйти из интерпретатора Python, и он все равно отправлял бы данные, поэтому управление им как-то передается Windows.

Причины

Тайм-аут (насколько я понимаю) работает следующим образом: Он проверяет «время ожидания простоя»
( [Python-Dev] Добавление тайм-аута сокета к urllib2 )
Если вы установите тайм-аут на 3, откроется соединение, запуститесчетчик, затем попытайтесь отправить данные и дождаться ответа, если в какой-то момент перед получением ответа таймер истекает, вызывается исключение тайм-аута.Обратите внимание, что отправка данных, по-видимому, не считается «активностью» в отношении таймера тайм-аута.
( urllib2 истекает, но не закрывает сокетное соединение )
( Закрыть соединение urllib2 )

Очевидно, где-то указано, что когда сокет закрыт / разыменован / собран мусор, он вызывает свою функцию 'close', которая ожидает отправки всех данных перед закрытиемразъем.Однако есть также функция отключения, которая должна немедленно останавливать сокет, предотвращая дальнейшую отправку данных.
( socket.shutdown против socket.close )
(http://docs.python.org/library/socket.html#socket.socket.close)

Что я хочу

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

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

from urllib import urlencode
from urllib2 import urlopen, HTTPError, URLError
import socket
import sys

class Uploader:
    def __init__(self):
        self.URL = "http://.../"
        self.data = urlencode({'fakerange':range(0,2000000,1)})
        print "Data Generated"

    def upload(self):
        try:
            f = urlopen(self.URL, self.data, timeout=10)
            returncode = f.read()
        except (URLError, HTTPError), msg:
            returncode = str(msg)
        except socket.error:
            returncode = "Socket Timeout!"
        else:
            returncode = 'Im here'

def main():
    upobj = Uploader()
    returncode = upobj.upload()

    if returncode == '100':
        print "Success!"
    else:
        print "Maybe a Fail"
        print returncode
    print "The End"

if __name__ == '__main__':
main()

Ответы [ 5 ]

1 голос
/ 24 февраля 2012

Вы можете рассмотреть возможность использования API, отличного от urllib2. httplib немного менее приятен, но часто не так уж и плох. Это, однако, позволяет вам получить доступ к базовому объекту сокета. Итак, вы можете сделать что-то вроде:

import httplib
import socket

def upload(host, path, data):
    conn = httplib.HTTPConnection(host, 80, True, 3)
    try:
        conn.request('POST', path, data)
        response = conn.getresponse()
        if response.status != 200:
            # maybe an HTTP error                                                                                    
            return response.status
        else:
            response_data = r.read()
            return response_data
    except socket.error:
        return "Socket Timeout!"
    finally:
        conn.sock.shutdown()
        conn.close()

def main():
    data = urlencode({'fakerange':range(0,2000000,1)})
    returncode = upload("www.server.com", "/path/to/endpoint", data)

    ...

(Отказ от ответственности: не проверено)

httplib имеет различные ограничения по сравнению с urllib2 - он не будет автоматически обрабатывать такие вещи, как перенаправления, например. Однако, если вы используете это для доступа к относительно фиксированному API, а не для загрузки случайных вещей из Интернета, это должно хорошо сработать.

Честно говоря, я, вероятно, не стал бы делать это сам; Как правило, я согласен позволить операционной системе работать с TCP-буферами так, как она хочет, даже если ее подход не всегда полностью оптимален ...

1 голос
/ 09 ноября 2011

Я нашел код, который может помочь вам в этой теме :

from urllib2 import urlopen
from threading import Timer
url = "http://www.python.org"
def handler(fh):
    fh.close()
    fh = urlopen(url)
    t = Timer(20.0, handler,[fh])
    t.start()
    data = fh.read()
    t.cancel()
0 голосов
/ 01 мая 2012

Оказывается, что вызов команд .sock.shutdown (socket.SHUT_RDWR) и .close () для HTTPConnection, который загружает, не останавливает загрузку.Он продолжит работать в фоновом режиме.Я не знаю о более надежных / прямых методах для уничтожения соединения из Python при использовании urllib2 или httplib.
В конце мы протестировали загрузку с использованием urllib2 без тайм-аута.Это означает, что при медленном соединении загрузка может занять очень много времени (POST), но, по крайней мере, мы узнаем, работало оно или нет.Существует вероятность того, что urlopen может зависнуть из-за отсутствия времени ожидания, но мы протестировали различные возможности плохого соединения, и во всех случаях urlopen либо сработал, либо возвратил ошибку через некоторое время.
Это означает, что мы по крайней мере узнаемна стороне клиента, что загрузка прошла успешно или не удалась, и что она не продолжается в фоновом режиме.

0 голосов
/ 09 января 2012

Вы можете порождать вторичный поток, используя multiprocessing, а затем выключать его всякий раз, когда обнаруживаете тайм-аут (URLError исключение с сообщением "истекло время ожидания ошибки urlopen").

Достаточно остановить процессзакрыть розетку.

0 голосов
/ 08 января 2012

Если вызов socket.shutdown на самом деле является единственным способом обрезать данные по таймауту, я думаю, вам нужно прибегнуть к каким-то махинациям.urllib2 на самом деле не дает вам возможности для такого мелкозернистого управления сокетами.

Проверьте Исходный интерфейс с Python и urllib2 для хорошего подхода.

...