Forum: Mikrocontroller und Digitale Elektronik [AVR] HTTP-Client Antwort auslesen


von Hans Peter (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich möchte mit einem ATmega168 und dem ENC28J60 Lanmodul Daten über 
TCP/HTTP an einen Webserver (im lokalen Netz) senden und die Antwort 
(wenige Bytes) auslesen.

Verwendet wird der TCP/IP-Stack von tuxgraphics.org (Version 5.10). Das 
Senden des GET-Befehls funktioniert, die Anfrage wird vom Server 
beantwortet und der Statuscode wird vom Stack richtig ausgelesen, 
allerdings scheint es keine Möglichkeit zu geben, um den Inhalt 
auszulesen.

Die Funktion zum Auslesen des Statuscodes:
1
uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data){
2
        uint16_t web_statuscode=0; // tcp status is OK but we need to check http layer too
3
        uint8_t i=0; 
4
        if (fd!=www_fd){
5
                (*client_browser_callback)(500,0,0);
6
                return(0);
7
        }
8
        if (statuscode==0 && len_of_data>12){
9
                // we might have a http status code
10
                // http status codes are 3digit numbers as ascii text. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
11
                // The buffer would look like this: HTTP/1.1 200 OK\r\n
12
                // web_statuscode=0 means we got a corrupted answer 
13
                if (client_browser_callback){
14
                        if (isblank(bufptr[datapos+8]) && isdigit(bufptr[datapos+9])&& isdigit(bufptr[datapos+11])){ // e.g 200 OK, a status code has 3 digits from datapos+9 to datapos+11, copy over the web/http status code to web_statuscode:
15
                                while(i<2){
16
                                        web_statuscode+=bufptr[datapos+9+i]-'0';
17
                                        web_statuscode*=10;
18
                                        i++;
19
                                }
20
                                web_statuscode+=bufptr[datapos+11]-'0';
21
                        }
22
                        //(*client_browser_callback)(web_statuscode,((uint16_t)TCP_SRC_PORT_H_P+(bufptr[TCP_HEADER_LEN_P]>>4)*4),len_of_data);
23
                        (*client_browser_callback)(web_statuscode,datapos,len_of_data);
24
                }
25
        }
26
        return(0);
27
}

Die Antwort sieht immer so aus:
1
GET / HTTP/1.1
2
Host: 10.0.0.1
3
4
HTTP/1.1 200 OK
5
Date: Wed, 16 Nov 2016 18:04:05 GMT
6
Server: Apache/2.4.17 (Win32) OpenSSL/1.0.2d PHP/5.6.21
7
X-Powered-By: PHP/5.6.21
8
Content-Length: 8
9
Content-Type: text/html; charset=UTF-8
10
11
19:04:05
Benötigt wird nur der letzte Absatz, hier also "19:04:05". (Nein, ich 
möchte NICHT die Uhrzeit übertragen, sondern "richtige" Daten. Momentan 
dient die Uhrzeit nur als Platzhalter zum Testen.)

Die Länge der Daten wird nicht konstant sein.

Wie komme ich jetzt an diese Daten?

Die ganze Datei ip_arp_udp_tcp.c ist angehängt. Aufgerufen wird 
client_browse_url(...)


Danke für eure Hilfe!

von S. R. (svenska)


Lesenswert?

Der HTTP-Header wird mit einer Leerzeile, also zwei Zeilenumbrüchen, 
beendet. Danach folgen bei HTTP/1.0 ohne Keepalive die Daten, bis die 
Verbindung abbricht.

Für HTTP/1.1 solltest du unbedingt den "Content-Length"-Header 
auswerten, denn da steht drin, wieviele Daten (in Bytes) du bekommst.

Du musst also nur nach "\r\n\r\n" suchen ("\n\n" ist nicht 
standardkonform, kommt aber manchmal auch vor) und findest direkt 
dahinter deine Daten.

Eventuell findest du die Daten auch einfach bei
bufptr[datapos] bis bufptr[datapos+len_of_data]. Möglicherweise sind die 
Daten an der Stelle noch nicht vollständig (weil die Anfrage in mehreren 
TCP-Paketen ankam).

: Bearbeitet durch User
von Hans Peter (Gast)


Lesenswert?

Danke für die Antwort!

Da die übertragenen Daten immer im Format {Daten...} sind, müsste es 
genügen, nur nach '{' bzw. '}' zu suchen und den Bereich dazwischen in 
ein Array zu kopieren.

So ist es mir auch mit folgendem Code gelungen, die Position von '{' zu 
finden:
1
uint8_t a=datapos;
2
while(bufptr[a] != '{')
3
{
4
  a++;
5
}

Jetzt sollte man eigentlich, von a ausgehend, auch '}' finden können:
1
uint8_t b=a;
2
while(bufptr[b] != '}'){
3
  b++;
4
}

Mit diesem Code steckt der AVR in einer Endlosschleife fest, obwohl '}' 
nur wenige Bytes von a entfernt ist.

S. R. schrieb:
> Möglicherweise sind die
> Daten an der Stelle noch nicht vollständig (weil die Anfrage in mehreren
> TCP-Paketen ankam).

Das ist hier nicht der Fall. Alles kommt in einem Paket an und befindet 
sich auch im bufptr-Array.
Wenn ich alles von a bis a+50 ausgebe, erhalte ich auch alle Daten 
einschließlich der abschließenden '}'.
Da die Daten aber keine feste Länge haben, möchte ich gern die erste 
Version verwenden.

Die sollte eigentlich problemlos funktionieren. Aus irgendeinem Grund 
tut sie das aber nicht, sondern bleibt in der Schleife stecken.

Irgendjemand eine Idee?

von S. R. (svenska)


Lesenswert?

Hans Peter schrieb:
> Da die übertragenen Daten immer im Format {Daten...} sind, müsste es
> genügen, nur nach '{' bzw. '}' zu suchen und den Bereich dazwischen in
> ein Array zu kopieren.

Das hattest du nicht erwähnt, und es ist auch kein sinnvoller Ansatz.

Nach HTTP-Protokoll folgen die Daten nach der feststehenden Zeichenkette 
"\r\n\r\n" und gehen bis zu der Länge, die im Content-Length-Header 
angegeben wurde oder bis zum Verbindungsende.

Alles andere ist hingepfuscht und wird dir irgendwann mal Schmerzen 
bereiten, bevorzugt in dem Augenblick, wo du für eine Reparatur 
wirklich keine Zeit hast.

> Mit diesem Code steckt der AVR in einer Endlosschleife fest, obwohl '}'
> nur wenige Bytes von a entfernt ist.

Wie groß sind deine Daten, inklusive HTTP-Header?
Wie groß ist die größte Zahl, die du in einem int8_t speichern kannst?

> Alles kommt in einem Paket an und befindet
> sich auch im bufptr-Array.

Kannst du das garantieren oder ist das nur zufällig in deinen Tests der 
Fall? Was passiert, wenn du aus Spaß mal eine MTU von 256 einstellst?

von Hans Peter (Gast)


Lesenswert?

S. R. schrieb:
> Wie groß sind deine Daten, inklusive HTTP-Header?
> Wie groß ist die größte Zahl, die du in einem int8_t speichern kannst?

Tatsächlich, die 8 Bit des uint8_t reichen gerade noch für den Header 
ohne Daten aus. Mit uint16_t funktioniert alles einwandfrei.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.