[php] socket tcp - data lezen duurt lang...

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Beste lezers,

Via een PHP script proberen we een externe bluetooth zender aan te sturen. Dit kan (getest en werkt!) via de welbekende telnet verbinding (tcp).
Om dit via php voor elkaar te krijgen hebben we het volgende script:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
<?php
    $fp = fsockopen("tcp://192.168.0.23", 10101);
    fwrite($fp, '****\r\n'); // pass
    fwrite($fp, 'inquiry 5\r\n\r\n'); // command
    
    while(!feof($fp)){
        $data[] = fread($fp, 1024);    
    }
    fclose($fp);
    
    print_r($data);
?>


Zoals je kunt zien maken we een verbinding, doen we een login en vervolgens doen we een zogenaamde "inquiry" naar bluetooth apparaten.
In telnet resulteert dit in een regeltje of 8. Maar om een of andere reden duurt dit script ongeveer 30 sec. a 1 minuut. Dit komt mogelijk omdat er geen feof bestaat.
We hebben al veel gezocht naar mogelijke oplossingen (uiteraard ook op tweakers), maar niets wil ons in de goede richting helpen.

Wat we al hebben geprobeerd (en niets heeft opgeleverd):
- feof verranderen in iets als "true" etc.
- time outs manipuleren: stream_set_timeout($fp, 5)
- ssh, welliswaar zonder foutmelding, maar ook traag en alles in 1 array veld.

Als we alleen de eerste regel willen hebben met bijv deze code:

PHP:
1
$data = fread($fp, 128);

dan krijg je binnen 1 seconde de resultaten op het scherm (zonder while loop).

als we al dit proberen gaat het mis:
PHP:
1
2
$regel1 = fread($fp, 128);    
$regel2 = fread($fp, 128);


hij gaat blijkbaar niet automatisch naar de volgende regel oid.
Wie heeft een idee? We zitten al 2 dagen hopeloos naar ons monitortje te staren :)
alvast bedankt voor de reacties.

Acties:
  • 0 Henk 'm!

  • Jaap-Jan
  • Registratie: Februari 2001
  • Laatst online: 17:19
fsockopen() opent een stream in blocking mode. Op die manier blijft fread() net zolang hangen totdat er weer data beschikbaar is. Zet de stream in non-blocking mode met stream-set-blocking(). Als een lege string geretourneerd wordt door fread() is er geen data meer beschikbaar.

Die 30 sec. á 1 minuut komt doordat PHP een instelling heeft die ervoor zorgt dat een script maximaal zoveel seconden loopt. Na die tijd wordt het script afgeschoten.

[ Voor 19% gewijzigd door Jaap-Jan op 01-12-2008 10:44 ]

| Last.fm | "Mr Bent liked counting. You could trust numbers, except perhaps for pi, but he was working on that in his spare time and it was bound to give in sooner or later." -Terry Pratchett


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 02:21

Janoz

Moderator Devschuur®

!litemod

Het lijkt me voor de hand dat de connectie niet gesloten wordt. Het kan immers best dat je nog veel meer communicatie wilt doen. Je script doet er nu lang over omdat hij het afsluiten van de communicatie laat afhangen van de timeout.

Je zult op een andere manier moeten achterhalen wanneer de communicatie afgerond is. Hiervoor zul je het protocol in moeten duiken. Ik neem aan dat er op 1 of andere manier wel te achterhalen is dat het bericht daadwerkelijk 8 regels lang is. In dat geval lees je niet tot je een eof hebt, maar lees je de data totdat je weet dat je alle bytes binnen hebt.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 02:21

Janoz

Moderator Devschuur®

!litemod

Jaap-Jan schreef op maandag 01 december 2008 @ 10:40:
fsockopen() opent een stream in blocking mode. Op die manier blijft fread() net zolang hangen totdat er weer data beschikbaar is. Zet de stream in non-blocking mode met stream-set-blocking(). Als een lege string geretourneerd wordt door fread() is er geen data meer beschikbaar.
Dat is niet gegarandeerd. Zeker wanneer de communicatie traag is kan het best zijn dat de fread even niks (meer) ziet omdat de buffer even leeg is terwijl het device nog bezig is data te versturen. Je script gaat dan gewoon verder zonder alle data gelezen te hebben.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
    $fp = fsockopen("tcp://192.168.0.23", 10101);
    fwrite($fp, '****\r\n'); // pass
    fwrite($fp, 'inquiry 5\r\n\r\n'); // command
    
    stream_set_blocking($fp, true);

    while(!feof($fp)){
        $line = fread($fp, 128);
        
        if($line != "")
            $data[] = $line;
        else
            break;
    }
    fclose($fp);
    
    print_r($data);
?>


in dit geval krijgen we een time out error na ruim 30 seconden.

Acties:
  • 0 Henk 'm!

  • Jaap-Jan
  • Registratie: Februari 2001
  • Laatst online: 17:19
Janoz schreef op maandag 01 december 2008 @ 10:44:
[...]


Dat is niet gegarandeerd. Zeker wanneer de communicatie traag is kan het best zijn dat de fread even niks (meer) ziet omdat de buffer even leeg is terwijl het device nog bezig is data te versturen. Je script gaat dan gewoon verder zonder alle data gelezen te hebben.
Heb je gelijk in, was 'n beetje kortzichtig van me. :)

Lijkt me dat het zaak is om uit te zoeken op welke manier het apparaat aangeeft dat alle data is verzonden. Dat het geen EOF is lijkt me op zich wel logisch, de communicatie is immers nog niet afgelopen.
Verwijderd schreef op maandag 01 december 2008 @ 10:52:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
    $fp = fsockopen("tcp://192.168.0.23", 10101);
    fwrite($fp, '****\r\n'); // pass
    fwrite($fp, 'inquiry 5\r\n\r\n'); // command
    
    stream_set_blocking($fp, true);

    while(!feof($fp)){
        $line = fread($fp, 128);
        
        if($line != "")
            $data[] = $line;
        else
            break;
    }
    fclose($fp);
    
    print_r($data);
?>


in dit geval krijgen we een time out error na ruim 30 seconden.
Raar hè, je zet de stream ook op blocking. Bovendien moet je er 0 of 1 naartoe schrijven en geen boolean. ;)

[ Voor 32% gewijzigd door Jaap-Jan op 01-12-2008 10:54 ]

| Last.fm | "Mr Bent liked counting. You could trust numbers, except perhaps for pi, but he was working on that in his spare time and it was bound to give in sooner or later." -Terry Pratchett


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
ook met 0 of 1 werkt het helaas niet :(

maar ook als we dus controleren op != "" ... geen data meer komt hij niet zelf uit de loop middels break;

Acties:
  • 0 Henk 'm!

  • Observer
  • Registratie: April 2001
  • Laatst online: 16:24
Verwijderd schreef op maandag 01 december 2008 @ 10:56:
ook met 0 of 1 werkt het helaas niet :(

maar ook als we dus controleren op != "" ... geen data meer komt hij niet zelf uit de loop middels break;
Weet je zeker dat een lege regel bestaat uit "" en niet uit "\n" of "\r\n"? Da's meestal wel 't geval namelijk. Probeer eens:
code:
1
 if(trim($line) != "")

There are 10 kinds of people in the world: those that understand binary and those that don't


Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

(jarig!)
Heb jij de garantie dat je altijd blokjes van 128 bytes krijgt? Wachten tot je geen data meer krijgt is precies hetzelfde als wachten tot de verbinding is gesloten.

Die laatste byte komt er gewoon niet en je moet dus ook gewoon domweg niet voorbij de data proberen te lezen. Je zal dus op een of andere manier een stuk intelligenter naar de output van de andere kant moeten kijken. Als het afgesloten wordt met een lege regel, dan werkt alsnog fread niet (tenzij je single-byte reads doet), maar kom je met fgets wel een heel eind.

[ Voor 7% gewijzigd door ACM op 01-12-2008 11:11 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
volgens mij zit het in het meer dan 1 keer fgets/fread aanroepen.

ook met deze code
PHP:
1
2
if(trim($line) != "")
            $data[] = $line;


en deze werkt ook niet

PHP:
1
2
if(strlen($data) != 0)
  $data[] = $line;


als we namelijk de while loop weg laten en zelf 2 keer fread aanroepen gaat het al mis:
PHP:
1
2
$line1 = fread($fp, 128);
$line2 = fread($fp, 128);


toch kunnen we maar 1 regel lezen met fread... en bij de 2e regel hangt het zooitje.
kunnen we op 1 of andere manier in 1 alle data in 1 keer laden zonder while oid?

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 02:21

Janoz

Moderator Devschuur®

!litemod

Nogmaals.....

Achterhaal het protocol. Dan weet je welke gelezen byte de laatste is ipv dat je het gaat lopen gokken middels timeouts. Als je maar gokt op het niet meer binnen krijgen van data zul je nooit een 100% werkend product krijgen. Sterker nog, je zult juist heel vaak erg onverklaarbaar en niet reproduceerbare problemen krijgen.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • cytherea
  • Registratie: Oktober 2003
  • Laatst online: 12-09 10:22
trouwens, als je PHP in CLI draait is de maximum execution time van 30 seconden niet van toepassing.

Die execution time kun je eventueel ook nog verhogen in je php.ini

Acties:
  • 0 Henk 'm!

  • Jaap-Jan
  • Registratie: Februari 2001
  • Laatst online: 17:19
Waarom zou je dat in dit geval willen? Zodat je oneindig lang kunt wachten? :+

| Last.fm | "Mr Bent liked counting. You could trust numbers, except perhaps for pi, but he was working on that in his spare time and it was bound to give in sooner or later." -Terry Pratchett

Pagina: 1