2012-12-12 8 views
6

Używam dwóch klientów gniazdek obok siebie, zbierając dane strumieniowe http (nie Twitter, ale podobne rzeczy). Dane przechodzą przez kodowane kodowanie.Utrata 6 bajtów, ale tylko wtedy, gdy gniazdo milknie przez 60 sekund?

Jednym z klientów jest curl (w linii poleceń, a nie php-curl), gdzie zarówno http, jak i https działają poprawnie. Drugi to mój własny skrypt PHP, używający fsockopen i fgets. Działa dobrze dla https, ale mam specyficzny problem z http. Jak szczegółowe? Dzieje się tak tylko wtedy, gdy strumień cichnie przez 60 sekund. Jeśli jest tylko 50 sekund ciszy, działa dobrze. Porównywałem nagłówki http curl wysyłane i odbierane do mojego skryptu i usunąłem wszystkie różnice. Myślałem, że wiem wszystko, co trzeba wiedzieć o gniazdach PHP, a zwłaszcza kodowanym kodowaniu, ale nadszedł czas, aby zjeść skromne ciasto, ponieważ ten mnie zakłopotał.

Tak, bieganie pozwijane z „--trace - --trace-time”, widzę to przyjść przez dla pierwszego pakietu po 60 sekundowym spokojnym okresie:

05:56:57.025023 <= Recv data, 136 bytes (0x88) 
0000: 38 32 0d 0a 7b 22 64 61 74 61 66 65 65 64 22 3a 82..{"datafeed": 
0010: 22 64 65 6d 6f 2e 31 64 36 2e 31 6d 2e 72 61 6e "demo.1d6.1m.ran 
... 
0080: 34 22 7d 5d 7d 0a 0d 0a       4"}]}... 

82 hex jest dla wielkości kawałka. \ R \ n oznacza koniec linii wielkości porcji. Fragment zaczyna się od "{".

Powyżej na stronie PHP, moja pętla zaczyna się tak:

while(true){ 
    if(feof($fp)){fclose($fp);return "Remote server has closed\n";} 
    $chunk_info=trim(fgets($fp)); //First line is hex digits giving us the length 
    $len=hexdec($chunk_info); //$len includes the \r\n at the end of the chunk (despite what wikipedia says) 

https, lub z mniej niż 60 sekund przerwy, to działa dobrze, $ len jest 100 lub niezależnie od wielkości kawałek jest. Ale po tej 60 sekund przerwy, co mogę dostać w $ chunk_info jest:

datafeed":"demo.1d6.1m.ran... 

Tak, wydaje mi się, że stracił sześć pierwszych bajtów: 38 32 0d 0a 7b 22

Wszystkie kolejne kawałki są w porządku, a dokładnie tak samo jak to, co otrzymuje loki.


Wersja Szczegóły

curl 7.19.7 (x86_64-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15 Protokoły: TFTP ftp telnet DICT ldap LDAPS http https plików ftps Cechy: GSS-Negocjuj IDN IPv6 dużyplik NTLM SSL libz

PHP 5.3.2-1ubuntu4.18 z Suhosin-patch (cLI) (wbudowane: Sep 12, 2012 19:12: 47)

Server: Apache/2.2.14 (Ubuntu)

(. Przetestowałem tylko z połączeniami localhost tak daleko)


Reszta pętli wygląda następująco:

$s=''; 
$len+=2; //For the \r\n at the end of the chunk 
while(!feof($fp)){ 
    $s.=fread($fp,$len-strlen($s)); 
    if(strlen($s)>=$len)break; //TODO: Can never be >$len, only ==$len?? 
    } 
$s=substr($s,0,-2); 
if(!$s)continue; 
$d=json_decode($s); 
//Do something with $d here 
} 

(na marginesie: w drodze Przetestowałem dotąd, kod przeszła tej pętli dokładnie raz, przed 60 sekundowym spokojnym okresie.)


UWAGA: Mam wiele sposobów obejścia problemu: wymusić użycie https lub użyć curl-php. To pytanie jest spowodowane tym, że chcę wiedzieć, co się dzieje, wiedzieć, co się zmienia po 60 sekundach i dowiedzieć się, jak to się stało.A może nauczyć się nowego pomysłu na rozwiązywanie problemów. Pomyśl o tym jako o cholernej intelektualnej ciekawości :-)

+0

Z ciekawości: dlaczego nie zaczęliście z PHP CURL? –

+0

Czy możemy zobaczyć resztę twojej pętli, szczególnie część, która zużywa bufor gniazda? –

+0

@FrancisAvila Dzięki za zainteresowanie, właśnie wysłaliście to. –

Odpowiedz

1

Oto naprawić błąd:

$chunk_info=trim(fgets($fp)); //First line is hex digits giving us the length 
if($chunk_info=='')continue; //Usually means the default 60 second time-out on fgets() was reached. 
... 

Jeśli fgets ($ fp) zwraca coś wtedy masz kawałek do czytania. Jeśli to coś jest zero, to masz do zrobienia czysty kawałek. Ale kiedy zwraca nic nie jest oznacza, że ​​fgets przekroczył limit czasu. Wydaje się, że domyślny limit czasu dla tcp: // wynosi 60 sekund; mając na uwadze, że domyślny limit czasu dla ssl: // jest dłuższy (przepraszam, nie udało mi się ustalić, co to jest - może to być, że blokuje się na zawsze).

Próbując przetworzyć fragment, którego nie było do odczytania, wszystko się zsynchronizowało. Stąd skradzione 6 bajtów.

Rozwiązywanie problemów:

  1. śmiecie kod z: echo "**".date("Y-m-d H:i:s");print_r(stream_get_meta_data($fp));ob_flush();flush(); Dane meta posiada wpis do powiedzenia, gdy ostatnia akcja strumień miał time-out. Datestamp jest niezbędny.
  2. Odsyłacz z numerem tcpdump -A -i lo port http w linii poleceń. Porównaj sygnatury czasowe z tymi z linii debugowania w PHP, pozwoliło mi zauważyć podejrzane zachowanie.
1

Szczerze mówiąc, nie jestem do końca pewien, dlaczego twój kod zachowuje się w ten sposób, ale sposób, w jaki odczytuje dane, jest niepoprawny; Chciałbym przerobić go na coś takiego:

$chunkmeta = fgets($f); 
// chunked transfer can have extensions (indicated by semi-colon) 
// see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html 
$chunklen = hexdec(current(explode(';', $chunkmeta, 2))); 

// chunk length is non-zero 
if ($chunklen) { 
    $chunk = ''; 
    // only read up to chunk length 
    while (!feof($f) && $chunklen) { 
     if ($buf = fread($f, $chunklen)) { 
      $chunklen -= strlen($buf); 
      $chunk .= $buf; 
     } else { 
      // uh oh, something bad happened 
      die("Could not read chunk"); 
     } 
    } 
    if ($chunklen == 0) { 
     // read the trailing CRLF 
     fread($f, 2); // read CRLF 
     // data is ready 
    } 
} 

Aktualizacja

Powinny poszły z moim instynkt na ten jeden (chociaż powyższy kod powinien działać dobrze w obu kierunkach); Ustawienie default_socket_timeout domyślnie wynosi 60 sekund, więc wszelkie kolejne odczyty powinny zwrócić false. Nadal nie wyjaśnia, dlaczego to działa na bezpiecznych gniazd ;-)

+0

BTW, czy znasz jakieś serwery internetowe, które używają ";", czy jest to tylko przyszłość? AFAIK Apache nigdy mi nie przysłał. –

+0

Świetny kod, Jack i ja (teraz!) Widzę, że nie mam tego błędu, który miałem. –

+0

Nigdy nie widziałem używanych rozszerzeń porcji, ale składnia jest opisana [tutaj] (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html) –

Powiązane problemy