Hallo zusammen,
ich hoffe Ihr hattet oder habt ein schönen Weihnachtsfest.
Ich beschäftige mich zur Zeit mit den WLAN-Modul ESP8266, welche mir
eine PHP auf meinen Server mit Übergabewerten aufruft und diese PHP dann
in eine XML schreibt. Der Code der PHP funktioniert tadellos. Die
gesendeten Werte des ESP8266 wurden wochenlang sauber in die XML
geschrieben. Nur seit ein paar Tagen, sendet mir der Server "Bad
Request" zurück (siehe Bild), obwohl der Code der selbe ist.
Die GET-Anfrage sieht ungefähr so aus:
Ich bin mir ziemlich sicher, dass das nicht an meiner Anfrage liegt,
weil sie ja schliesslich immer so funktioniert hat. Ich gehe eher davon
aus, dass in der Zeit neue Update des Servers daran schuld sind. Was die
Sache noch verwirrender macht ist, dass wenn ich manchmal die komplette
Schaltung neu starte, ein einziger Request erfolgreich gesendet wird,
dannach kommt wieder nur noch der Bad Request. Leider konnte kann ich
das nicht reproduzieren, mal geht es mit dem Neustart, mal nicht.
Infos zum Server:
Synology DS 413j
PHP 5.6 läuft
PHP 7.0 läuft
WebStation läuft
Apache HTTP Server 2.2 läuft
Apache HTTP Server 2.4 läuft
Hat jemand eine Idee woran das liegen kann ?
Gruss
Peter
>> b0=0&b0=1&b0=2&b0=3&b4=4>Kommt mir irgendwie komisch vor.
Das man komisch sein, ist aber sicher nicht die Problemursache. Es ist
sogar offiziell erlaubt, einen Parameter mehrmals zu wiederholen. Ich
würde es allerdings nicht tun.
P. F. schrieb:> Die GET-Anfrage sieht ungefähr so aus:> ...
Und wie sieht sie wirklich aus?
Lass dir die URL doch mal ausgeben oder schau in den Logfiles vom
Server, vermutlich wird die einfach nur falsch zusammengebaut weil eine
der Variablen aus denen sie wohl besteht leer ist (irgend ein sensor
ausgestiegen?).
In etwa so:
> b0=&b0=1&b0=2&b0=3&b4=4
Ich würde auch ganz stark auf einen kaputten GET-Request tippen. Der
Server wird sicherlich ausgereifter sein, als dein Code, und mit
Sicherheit würde ein Server nicht über ein Update kaputt gepatcht. Lass
dir den vom ESP zusammengebauten GET-Request über's Terminal ausgeben
oder sieh dir die Kommunikation in Wireshark an, da wirst du sicher
schnell fündig.
Gruß
Jim M. schrieb:> Das ist ein HTTP/1.0 Request.
Wo siehst du das? Er schickt doch klar HTTP/1.1 im Request mit.
Jim M. schrieb:> Für HTTP 1.1 muss da IMO noch ein "Connection: close" Header mit rein,> siehe Kapitel 6.1 in RFC 7230
Nein, braucht es nicht. HTTP/1.1 geht grundsätzlich von persistenten
Verbindungen aus, siehe Kapitel 6.3 im RFC 7230.
Abgesehen davon wäre das überhaupt unlogisch, da es ja wochenlang
funktioniert hat. Wieso sollte sich daher der Server jetzt plötzlich an
einem fehlenden Header-Feld stören, welches vorher auch schon gefehlt
hat?
> HTTP/1.1 geht grundsätzlich von persistenten Verbindungen aus
Eben das könnte der Haken sein. Wenn jetzt weder der Server noch der
Client die Verbindung nach der Response schließt, dann wird der zweite
Request nicht richtig erkannt, denn es gibt auch keinen Content-Length
Header und keinen Chunked-Mode.
P. F. schrieb:> Die GET-Anfrage sieht ungefähr so aus:
Zeig uns doch einfach den Teil Code, der deine Anfrage konkret
zusammenbaut.
P. F. schrieb:> Ich bin mir ziemlich sicher, dass das nicht an meiner Anfrage liegt,> weil sie ja schliesslich immer so funktioniert hat.
Häufig ist es dann aber so, dass es ziemlich sicher am eigenen Code
liegt :)
P. F. schrieb:> Was die Sache noch verwirrender macht ist, dass wenn ich manchmal die> komplette Schaltung neu starte, ein einziger Request erfolgreich> gesendet wird, dannach kommt wieder nur noch der Bad Request.
Das untermauert meine Aussage noch.
Stefan U. schrieb:>> HTTP/1.1 geht grundsätzlich von persistenten Verbindungen aus>> Eben das könnte der Haken sein. Wenn jetzt weder der Server noch der> Client die Verbindung nach der Response schließt, dann wird der zweite> Request nicht richtig erkannt, denn es gibt auch keinen Content-Length> Header und keinen Chunked-Mode.
Das könnte es natürlich sein und war auch eine meiner Ideen. Ich würde
immer einen sauberen Request mit zumindest einem Content-Length- und
einem Connection-Header mitsenden.
Aber die Ideen hier widersprechen der Aussage des TO, wonach es ja
anfänglich problemlos funktioniert hat. Entweder stimmt es, was wir
vermuten, dann dürfte es aber noch nie sauber funktioniert haben. Oder
der TO hat uns nicht alles erzählt und es hat sich zwischendurch noch
etwas mehr getan.
Es wäre daher gut, wenn der TO sich wieder einklinken würde und neuen
Input lieferte, was denn in der Zwischenzeit geschehen ist.
Stefan U. schrieb:> Untersuche den Netzwerk-Verkehr mit Wireshark, insbesondere den HTTP> Request.
Danke, teste ich mal aus.
Jim M. schrieb:> Das ist ein HTTP/1.0 Request. Für HTTP 1.1 muss da IMO noch ein> "Connection: close" Header mit rein, siehe Kapitel 6.1 in RFC 7230.
Sobald ich ein "OK" empfange, schliesse ich auc die Verbindung, siehe
Bild.
Rufus Τ. F. schrieb:> P. F. schrieb:>> b0=0&b0=1&b0=2&b0=3&b4=4>> Kommt mir irgendwie komisch vor. viermal wird b0 ein unterschiedlicher> Wert zugewiesen. Soll das so sein?
Kopierfehler. Soll b0, b1, b2, usw heissen.
Jürgen K. schrieb:> P. F. schrieb:>> Die GET-Anfrage sieht ungefähr so aus:>> ...>> Und wie sieht sie wirklich aus?>> Lass dir die URL doch mal ausgeben oder schau in den Logfiles vom> Server, vermutlich wird die einfach nur falsch zusammengebaut weil eine> der Variablen aus denen sie wohl besteht leer ist (irgend ein sensor> ausgestiegen?).
Hmm, ich hab auch testeweise die URL nicht zusammen gesetzt sondern
direkt in die "putString" geschrieben. Da hab ich das selbe Problem.
Jan L. schrieb:> Was sagt denn das nginx error Log? Wenn es nichts sagt, dann aktiviere> es, am besten mit Loglevel ‚debug‘ (falls möglich), oder zumindest> ‚info‘.
Uii....wie kann ich die denn auslesen ?
A.. P. schrieb:> P. F. schrieb:>> Die GET-Anfrage sieht ungefähr so aus:>> Zeig uns doch einfach den Teil Code, der deine Anfrage konkret> zusammenbaut.
Hier baue ich den Request zusammen, zähle ich Zeichenlänge und sende
dann:
Unter "TCP_senden" gibt es noch die auskommentierte Zeile die ioch
testweise sende und damit das zusammenbauen umgehe. Geht aber auch
nicht.
1
voidTCP_Laenge(uint8_tvar0,uint8_tvar1,uint8_tvar2,uint8_tvar3,uint8_tvar4,uint8_tvar5,uint8_tvar6,uint8_tvar7)//Zeichenzählen für Anfrage ermitteln
2
{
3
charwert_chr[3];//Variable zum wandeln
4
5
/*Komplette GET-Anfrage, Beispiel:
6
GET /esp8266/haawrite.php?b0=333 HTTP/1.1\r\nHost: 192.168.1.100\r\n\r\n
7
*/
8
9
//Array zusammensetzten
10
charget[GET_GROESSE]="GET ";//Array beginnen
11
strcat(get,PHP_ZIEL);//an Array anhängen
12
13
// strcat(get, "?b0="); //an Array anhängen
14
// itoa(byte, wert_chr, 10); //Variable wandeln sonst wird die Länge der Zeichen falsch gezählt
15
// strcat(get, wert_chr); //an Array anhängen
16
// strcat(get, "="); //an Array anhängen
17
18
strcat(get,"?b0=");//an Array anhängen
19
itoa(var0,wert_chr,10);//Variable wandeln sonst wird die Länge der Zeichen falsch gezählt
20
strcat(get,wert_chr);//an Array anhängen
21
strcat(get,"&");
22
23
strcat(get,"b1=");//an Array anhängen
24
itoa(var1,wert_chr,10);//Variable wandeln sonst wird die Länge der Zeichen falsch gezählt
25
strcat(get,wert_chr);//an Array anhängen
26
strcat(get,"&");
27
28
strcat(get,"b2=");//an Array anhängen
29
itoa(var2,wert_chr,10);//Variable wandeln sonst wird die Länge der Zeichen falsch gezählt
30
strcat(get,wert_chr);//an Array anhängen
31
strcat(get,"&");
32
33
strcat(get,"b3=");//an Array anhängen
34
itoa(var3,wert_chr,10);//Variable wandeln sonst wird die Länge der Zeichen falsch gezählt
35
strcat(get,wert_chr);//an Array anhängen
36
strcat(get,"&");
37
38
strcat(get,"b4=");//an Array anhängen
39
itoa(var4,wert_chr,10);//Variable wandeln sonst wird die Länge der Zeichen falsch gezählt
40
strcat(get,wert_chr);//an Array anhängen
41
strcat(get,"&");
42
43
strcat(get,"b5=");//an Array anhängen
44
itoa(var5,wert_chr,10);//Variable wandeln sonst wird die Länge der Zeichen falsch gezählt
45
strcat(get,wert_chr);//an Array anhängen
46
strcat(get,"&");
47
48
strcat(get,"b6=");//an Array anhängen
49
itoa(var6,wert_chr,10);//Variable wandeln sonst wird die Länge der Zeichen falsch gezählt
50
strcat(get,wert_chr);//an Array anhängen
51
strcat(get,"&");
52
53
strcat(get,"b7=");//an Array anhängen
54
itoa(var7,wert_chr,10);//Variable wandeln sonst wird die Länge der Zeichen falsch gezählt
> char wert_chr[3]; //Variable zum wandeln
Das ist ein Byte zu wenig. Du brauchst 4 Bytes wegen dem abschließenden
Null-Byte das den String terminiert.
P. F. schrieb:>> Was sagt denn das nginx error Log? Wenn es nichts sagt, dann aktiviere>> es, am besten mit Loglevel ‚debug‘ (falls möglich), oder zumindest>> ‚info‘.>> Uii....wie kann ich die denn auslesen ?
?? du hast irgendwo ("Server"?) einen nginx laufen. Dieser liefert Error
400. Recherchiert man da mal ein paar Sekunden im Netz erfährt man, dass
nginx üblicherweise bei einem Error 400 die (vielfältigen) Ursachen
dafür in sein Error-Log schreibt. Das Error-Log muss man *evtl.*
aktivieren - dafür gibt es auf dem "Server" die Datei nginx.conf - oder
vermutlich eher sowas wie /etc/nginx/conf.d/meinserver.conf o.ä.
Da sollte sowas wie hier stehen:
1
http{
2
server{
3
error_log/path/to/logdebug;
"debug" wird nicht überall vorgesehen sein, dann einfach "info" nehmen.
Die Fehlermeldungen stehen dann in "/path/to/log"...
Google Treffer 1:
Stefan U. schrieb:> Das ist ein Byte zu wenig. Du brauchst 4 Bytes wegen dem abschließenden> Null-Byte das den String terminiert.
Ok, danke. Habs angepasst.
Jan L. schrieb:> ?? du hast irgendwo ("Server"?) einen nginx laufen. Dieser liefert Error> 400. Recherchiert man da mal ein paar Sekunden im Netz erfährt man, dass> nginx üblicherweise bei einem Error 400 die (vielfältigen) Ursachen> dafür in sein Error-Log schreibt. Das Error-Log muss man *evtl.*> aktivieren - dafür gibt es auf dem "Server" die Datei nginx.conf - oder> vermutlich eher sowas wie /etc/nginx/conf.d/meinserver.conf o.ä.>> Da sollte sowas wie hier stehen:http {> server {> error_log /path/to/log debug;>> "debug" wird nicht überall vorgesehen sein, dann einfach "info" nehmen.> Die Fehlermeldungen stehen dann in "/path/to/log"...
Oh man, das scheint kompliziert zu sein. Ich kenne mich damit nicht aus.
Also, ich hab unter /etc/nginx eine "nginx.conf" gefunden. Ist das die
richtige ?
>> Oh man, das scheint kompliziert zu sein. Ich kenne mich damit nicht aus.> Also, ich hab unter /etc/nginx eine "nginx.conf" gefunden. Ist das die> richtige ?
Jein; das ist zwar die "Startconfig", die Config deiner Domain liegt
aber in einem dieser Verzeichnisse:
include app.d/www.*.conf;
include app.d/alias.*.conf;
include /usr/syno/share/nginx/conf.d/www.*.conf;
include conf.d/www.*.conf;
Ist aber egal, die nginx.conf tut's hierfür erstmal auch - einfach z.B.
unter "server_name" noch reinschreiben:
1
error_log/var/log/nginx/error.loginfo;
und nginx neu starten. Danach liegt unter /var/log/nginx ein
"error.log".
Falls es /var/log/nginx noch nicht geben sollte, und du dich vermutlich
mit Dateirechten nicht recht auskennst :), kannst du einfach auch
"/tmp/nginx_error.log" nehmen:
A.. P. schrieb:> Jim M. schrieb:>> Das ist ein HTTP/1.0 Request.>> Wo siehst du das? Er schickt doch klar HTTP/1.1 im Request mit.>> Jim M. schrieb:>> Für HTTP 1.1 muss da IMO noch ein "Connection: close" Header mit rein,>> siehe Kapitel 6.1 in RFC 7230>> Nein, braucht es nicht. HTTP/1.1 geht grundsätzlich von persistenten> Verbindungen aus, siehe Kapitel 6.3 im RFC 7230.
Letztlich habt ihr doch beide Recht:
Laut 6.3 der besagten Spec ist das "Connection: close" bei HTTP/1.1
nicht zwingend nötig, der Server geht in diesem Fall dann eben von einer
persistenten Verbindung aus.
Wenn der Client (wie im vorliegenden Fall) aber eben keine persistenten
Verbindungen unterstützt, dann MUSS er nach Kapitel 6.1 eben doch einen
"Connection: close"-Header senden.
So gesehen sind die von Arduino-Code erzeugten HTTP-Requests
strenggenommen ja tatsächlich nicht HTTP/1.1-konform, und die "Bad
request"-Fehlermeldung somit im Grunde berechtigt.
@Threadstarter: Ich würde an Deiner Stelle wirklich einfach mal
ausprobieren, was passiert, wenn Du das wahlweise als HTTP 1.0-Request
sendest, oder eben zusätzlich einfach noch ein "Connection:
close"-Header-Feld mit einfügst.
Die von stefanus genannte mögliche Erklärung für das Phänomen, warum es
direkt nach einem Neustart genau einmal funktioniert, klingt für mich
ziemlich plausibel.
Joachim S. schrieb:> Letztlich habt ihr doch beide Recht
Nicht ganz. Hätte Jim M. geschrieben es "kann" noch ein Connection:
close rein, dann hätte ich dem zugestimmt. Aber er meinte es "muss" eins
rein - und das ist eben falsch ;)
Joachim S. schrieb:> So gesehen sind die von Arduino-Code erzeugten HTTP-Requests> strenggenommen ja tatsächlich nicht HTTP/1.1-konform, und die "Bad> request"-Fehlermeldung somit im Grunde berechtigt.
Strenggenommen ist es eben doch ein konformer Request und die Antwort
des Servers in so einem Fall (sofern der fehlende Connection-Header eben
der einzige Grund wäre) nicht berechtigt. In dem Moment, wo der Sender
kein "Connection: close" mitsendet, nimmt der Server automatisch eine
dauerhafte Verbindung an. Soweit ist doch alles konform. Sobald nun der
Request vollständig und korrekt beim Server ankommt und dieser seine
Antwort verschickt hat, bleibt die Verbindung einfach bestehen. Nun
schließt der Client irgendwann die TCP-Verbindung und alles ist immer
noch korrekt.
Ich würde aber an Stelle des TO doch einfach als ersten Ansatz einen
korrekten Content-Length Header setzen, um ein eventuelles Problem mit
dem Request-Body - welcher ja leer ist - auszuschließen.
A.. P. schrieb:> Joachim S. schrieb:>> Letztlich habt ihr doch beide Recht>> Nicht ganz. Hätte Jim M. geschrieben es "kann" noch ein Connection:> close rein, dann hätte ich dem zugestimmt. Aber er meinte es "muss" eins> rein - und das ist eben falsch ;)
Och, das kann man so oder so sehen, denn im konkreten Fall "muss" laut
Kapitel 6.1 der Spec doch tatsächlich ein solcher Header rein:
> A client that does not support persistent connections MUST send the> "close" connection option in every request message.
;)
Joachim S. schrieb:> Och, das kann man so oder so sehen, denn im konkreten Fall "muss" laut> Kapitel 6.1 der Spec doch tatsächlich ein solcher Header rein:>> A client that does not support persistent connections MUST send the>> "close" connection option in every request message.>> ;)
Und woher kommt deine Annahme, dass der TO mit seinem Code keine
persistente Verbindung unterstützt? Die Eigenschaft, eine persistente
Verbindung zu unterstützen, wird doch nicht durch höhere Gewalt
bestimmt, sondern einzig vom Entwickler vorgegeben und von der Weise,
wie er seinen HTTP-Code implemetiert hat. Somit signalisiert der TO
automatisch mit dem fehlenden Header, dass er eine dauerhafte Verbindung
möchte. Dass er diese vielleicht 200 ms nachdem er die Antwort des
Servers erhalten hat wieder schließt, ist kein Indiz dafür, dass er
keine persistente Verbindungen unterstützt. Eine persistente Verbindung
ist eben alles, was nach Erhalt der Server-Antwort noch offen gehalten
wird, unabhängig davon, ob es nur 10 ms sind oder bis morgen Mittag 12
Uhr.
Im Übrigen ziehe ich meine vorherige Aussage bzgl. Content-Length
zurück, da dies bei einem GET, wie in diesem Fall, nicht das Problem
sein kann, da es dazu auch im RFC 7230, Kapitel 3.3.2 heißt:
"A user agent SHOULD NOT send a Content-Length header field when the
request message does not contain a payload body and the method semantics
do not anticipate such a body."
Gruß
Joachim S. schrieb:> Och, das kann man so oder so sehen, denn im konkreten Fall "muss" laut
...im "konkreten Fall" ist das in der Praxis völlig unerheblich, wie
eigentlich jeder jederzeit höchst simpel mit irgendeinem nginx (oder
apache etc.) nachprüfen kann: Ein einfacher Telnet auf Port 80 eines
Webservers liefert einwandfreie Antworten, einziger "mandatory header"
bei HTTP/1.1 ist der Host:-Header, der allerdings halbwegs stimmen
sollte:
> Und woher kommt deine Annahme, dass der TO mit seinem Code keine> persistente Verbindung unterstützt?
Weil er weder den Content-Length Header noch den Chunked-Transfer
verwendet hat. Habe ich doch oben geschrieben. Ohne das kann er keinen
2. Request senden.
> "A user agent SHOULD NOT send a Content-Length header field when the> request message does not contain a payload body and the method semantics> do not anticipate such a body."
Ich vermute, dieser Satz stammt aus HTTP 1.0 Zeiten. Get Requests können
einen Body enthalten (auch wenn das unüblich ist). Und deswegen darf man
bei GET nicht von der Länge 0 ausgehen. Macht auch kein Server.
Der nächste Request beginnt, nachdem der vorherige beendet ist. Ein
Request endet wahlweise
a) nach Erreichen der Content-Length,
b) beim Schließen der Verbindung,
c) bei der Ende-Markierung vom Chunked Transfer (nur HTTP 1.1)
Unabhängig von diesen ganzen Erörterungen, wie der HTTP-Standard denn
nun aussieht, denke ich, dass der TO nur weiterkommt, wenn er sich mal
vor sein System setzt und sich ein paar dieser fehlerhaften Anfragen
ansieht. Ob das nun seriell über das Terminal, über das nginx-Log oder
mittels Wireshark erfolgt - irgendwas davon wird ihn sicher weiter
bringen, als alle Diskussionen über den korrekten Aufbau einer Anfrage
:)
Ich gehe stark davon aus, dass der Server schon richtig arbeitet und der
Fehler sicher in dem (m.M.n. etwas ungelenken) Zusammenbau der Anfrage
steckt. Es wird dem TO sicher selbst schnell auffallen, an welcher
Stelle sein Code unvollständige Anfragen erzeugt, wenn er sich mal ein
paar davon ansieht.
Was mir übrigens gerade eben noch aufgefallen ist:
1
//Array zusammensetzten
2
char cipsend[] = "AT+CIPSEND="; //Array beginnen
3
strcat(cipsend, wert_chr); //an Array anhängen
4
strcat(cipsend, "\r\n"); //an Array anhängen
Meine C/C++-Kenntnisse sind zugegebenermassen äusserst gering, aber ist
obiges Code-Fragment wirklich okay so? Ich hätte jetzt instinktiv
vermutet, dass durch ein
1
char cipsend[] = "AT+CIPSEND="
nur 12 Bytes reserviert werden (also genau so viele, um den String
"AT+CIPSEND=" sowie ein abschliessenden 0-Byte zu speichern). Und es
durch die anschliessenden beiden strcat-Befehle dann zu einer Art
Pufferüberlauf kommt, bei dem möglicherweise unbeabsichtigt etwas ganz
anderes überschrieben wird, mit möglicherweise unvorhergesehenen Folgen.
Ist obiges Codefragment wirklich okay so?
Joachim S. schrieb:> @Threadstarter: Ich würde an Deiner Stelle wirklich einfach mal> ausprobieren, was passiert, wenn Du das wahlweise als HTTP 1.0-Request> sendest
Mit den funktioniert das auch nicht:
Joachim S. schrieb:> zusätzlich einfach noch ein "Connection:> close"-Header-Feld mit einfügst.
Ist das nicht hiermit schon geschehen ?
1
#define TCP_DISCONNECT ("AT+CIPCLOSE\r\n")
2
3
//TCP-Verbindung schliessen
A.. P. schrieb:> Ich würde aber an Stelle des TO doch einfach als ersten Ansatz einen> korrekten Content-Length Header setzen, um ein eventuelles Problem mit> dem Request-Body - welcher ja leer ist - auszuschließen.
Das verstehe ich nicht ganz. Wie kann ich das realisieren ?
Stefan U. schrieb:> Weil er weder den Content-Length Header noch den Chunked-Transfer> verwendet hat. Habe ich doch oben geschrieben. Ohne das kann er keinen> 2. Request senden.
Ich verstehe nur Bahnhof :-(
A.. P. schrieb:> Ob das nun seriell über das Terminal, über das nginx-Log oder> mittels Wireshark erfolgt - irgendwas davon wird ihn sicher weiter> bringen, als alle Diskussionen über den korrekten Aufbau einer Anfrage> :)
Über den Terminal sieht die Zeichenlängeermittlung und die eigentliche
GET-Anfrage wie auf dem Bild aus.
Das Wireshark hab ich ausprobiert. Wenn ich das richtig verstanden habe,
brauche ich dafür WLAN am PC,
was ich nicht habe. Bleibt noch der nginx-log übrig. Da bin ich dran und
werde berichten.
Das ist echt frustierend. Ich kann den Fehler immernoch nicht richtig
reproduzieren. An einen Abend geht es nicht, am nächsten Tag
funktioniert die Übermittlung wieder. Aber auch nur bis ich die
Schaltung wieder aus- und einschalte.
Joachim S. schrieb:> Meine C/C++-Kenntnisse sind zugegebenermassen äusserst gering, aber ist> obiges Code-Fragment wirklich okay so? Ich hätte jetzt instinktiv> vermutet, dass durch einchar cipsend[] = "AT+CIPSEND="nur 12 Bytes> reserviert werden (also genau so viele, um den String> "AT+CIPSEND=" sowie ein abschliessenden 0-Byte zu speichern). Und es> durch die anschliessenden beiden strcat-Befehle dann zu einer Art> Pufferüberlauf kommt, bei dem möglicherweise unbeabsichtigt etwas ganz> anderes überschrieben wird, mit möglicherweise unvorhergesehenen Folgen.
Gute Adleraugen. Danke. Hab eine feste Größe zugewiesen:
Jan L. schrieb:> Falls es /var/log/nginx noch nicht geben sollte, und du dich vermutlich> mit Dateirechten nicht recht auskennst :), kannst du einfach auch> "/tmp/nginx_error.log" nehmen:> error_log /tmp/nginx_error.log info;
Ich kann die Datei nicht abspeichern. Was mache ich falsch ?
So, ich hab mir Root-Rechte gegeben und konnte die Datei abspeichern.
Ein Haken hab ich bei der Sache. Ich hab den ganzen Server neu gestartet
und die Datei wurde dann anscheinend neu geschrieben.
In welcher Datei dann ich das den reinschreiben was auch dann beim
neustart erhalten bleibt ?
Jan L. schrieb:> ...ein GET, HEAD oder PUT fehlt, der Request enthält nur eine URL...
Na gut da hätte ich auch drauf kommen können. Ich dachte das "GET" wird
wie die Zeilenumbrüche einfach nur nicht mit angezeigt.
Mittlerweile funktioniert es auch wieder, wenn ich zum einen die
Funktion "TCP_Laenge" durch einen festen String ersetzte und zum anderen
die Zählung der Länge anpasse. Mir ist echt schleierhaft, wie es dann
vorher funktionieren konnte. Vielleicht hab ich etwas rausgelöscht ohne
es zu merken. Naja aufjedenfall liegt es an der Zusammensetztung inkl.
Längenermittlung.
In der Funktion zählt er immer 97 Bytes. Ich hab mit den Werten die er
immer sendet die Zeichen gezählt und bin auf 102 gekommen, siehe unten.
Selbst wenn ich das fehlende "GET " lösche komme ich nicht auf 97. Dabei
habe ich immer "\r\n" als 2 Zeichen gezählt. Im kompletten String sind
das dann 108-6.
So geht's:
P. F. schrieb:> Joachim S. schrieb:>> zusätzlich einfach noch ein "Connection:>> close"-Header-Feld mit einfügst.>> Ist das nicht hiermit schon geschehen ?
Nein. Der Befehl AT+CIPCLOSE weist den ESP an die TCP-Verbindung zu
schließen. Wenn du deiner Anfrage aber noch ein "Connection: close"
hinzufügst, so teilst du dem Server mit, dass dieser die Verbindung
schließen soll, wenn die Antwort versendet wurde. Der Header ist dabei
ein Teil des HTTP-Protokolls, welches über dem TCP-Protokoll liegt.
P. F. schrieb:> A.. P. schrieb:>> Ich würde aber an Stelle des TO doch einfach als ersten Ansatz einen>> korrekten Content-Length Header setzen, um ein eventuelles Problem mit>> dem Request-Body - welcher ja leer ist - auszuschließen.>> Das verstehe ich nicht ganz. Wie kann ich das realisieren ?
Genauso, wie du es doch schon mit dem Host Header gemacht hast. Einfach
eine zusätzliche Zeile einfügen und die Content-Length angeben.
P. F. schrieb:> Stefan U. schrieb:>> Weil er weder den Content-Length Header noch den Chunked-Transfer>> verwendet hat. Habe ich doch oben geschrieben. Ohne das kann er keinen>> 2. Request senden.>> Ich verstehe nur Bahnhof :-(
Vielleicht solltest du dir nochmal ansehen, wie HTTP grundlegend
funktioniert bzw. was so die wichtigsten Header sind. Das solltest du
schon wissen, wenn du versuchst, selbstgestrickte Anfragen an deinen
Server zu senden.
Gruß
P. F. schrieb:> Gute Adleraugen. Danke. Hab eine feste Größe zugewiesen:char cipsend[15]> = "AT+CIPSEND=";
Das reicht aber immer noch nicht. Für den String "AT+CIPSEND=" (11
Zeichen), eine dreistellige Zahl, das abschließende CRLF und das
Null-Byte brauchst du 17 Zeichen Platz.
Wartest du nach dem "AT+CIPSEND" Befehl eigentlich, bis der ESP die
Eingabeaufforderung ">" für die Daten zurück gibt?
Wenn nicht ist kein Wunder, daß die Übertragung unvollständig ist.
Offensichtlich wartest du nicht, aber wer weiß wie der aktuelle
Quelltext inzwischen aussieht.
Stefan U. schrieb:> Wartest du nach dem "AT+CIPSEND" Befehl eigentlich, bis der ESP die> Eingabeaufforderung ">" für die Daten zurück gibt?
Mir ist auch schon aufgefallen, dass im obigen Code-Auszug das ">"
nirgends auftaucht.
Stefan U. schrieb:> Wenn nicht ist kein Wunder, daß die Übertragung unvollständig ist.
Das würde dann aber nicht zum Fehlerbild passen. Ich weiß gerade nicht
genau, wie sich der ESP verhält, wenn er noch auf fehlende Zeichen
wartet, aber ich glaube, dass er dann ewig wartet. Und weil er ewig
wartet, werden die Daten gar nicht erst verschickt und es kommt erst gar
keine Anfrage an den Server zustande. Und wo keine Anfrage kommt, kann
ja auch kein Server antworten (die Antwort könnte ja sowieso nicht
empfangen werden, da der ESP ja noch auf Zeichen wartet ;) )
An den TO:
Woran lag's denn nun? Hat sich zwischenzeitlich was an deinem Problem
getan? Wäre super, wenn du mit der Gemeinschaft auch deine Erfahrungen
und Lösungen teilen würdest, damit auch andere davon profitieren können.
Gruß
A.. P. schrieb:> "Connection: close"
Sende ich das in der nächsten Schleife, kommt nur "Error" zurück. Das
ist aber das Error vom ESP8266 und nicht vom Server, Bild 1.
So eine ähliche Anfrage hatte ich in einen Youtube-Video schonmal
gesehen (https://www.youtube.com/watch?v=xZePXRjQOe8), jedoch
funktioniert das "Connenction: close" bei mir an der selben Stelle nicht
so richtig.
A.. P. schrieb:> Vielleicht solltest du dir nochmal ansehen, wie HTTP grundlegend> funktioniert bzw. was so die wichtigsten Header sind. Das solltest du> schon wissen, wenn du versuchst, selbstgestrickte Anfragen an deinen> Server zu senden.
Ja, ich werde mich mal einlesen.
A.. P. schrieb:> Das reicht aber immer noch nicht. Für den String "AT+CIPSEND=" (11> Zeichen), eine dreistellige Zahl, das abschließende CRLF und das> Null-Byte brauchst du 17 Zeichen Platz.
Danke. Geändert.
Stefan U. schrieb:> Wartest du nach dem "AT+CIPSEND" Befehl eigentlich, bis der ESP die> Eingabeaufforderung ">" für die Daten zurück gibt?
Ne, ich such nach einem "OK". Danke für den Tip, werde ich mit
einpflegen.
A.. P. schrieb:> Woran lag's denn nun? Hat sich zwischenzeitlich was an deinem Problem> getan? Wäre super, wenn du mit der Gemeinschaft auch deine Erfahrungen> und Lösungen teilen würdest, damit auch andere davon profitieren können.
Wie schon ober geschrieben, klappt es ja jetzt nachdem ich den
zusammengesetzten Request durch einen festen String ersetzt habe, weil
dort anscheinend laut Server das "GET " gefehlt hat. Ich hatte mir den
zusammengesetzten String ausgeben lassen, doch der war vollstädig. Warum
die Zusammensetztung nicht geklappt hat und warum die dazu gehörige
Ausgabe trotzdem richtig aussah, hab ich noch nicht feststellen können,
dafür brauche ich noch etwas Zeit. Jetzt läuft es erstmal wieder und die
nächsten Schritte wären, den Code aus Euren Hinweißen zu verbessern und
die Zusammensetztung der Anfrage nochmal Schritt für Schritt neu
aufzubauen. Wenn es soweit ist, werde ich natürlich berichten.
"Connection: close" gehört in den Header Deines http-Requests und muss
daher VOR der Zeichenfolge "\r\n\r\n" gesendet werden.
Schon aus diesem Grund würde ich übrigens empfehlen, in Deinen
putString-Aufrufen nicht gleich den gesamten Request als langen
mehrzeiligen String zu senden, sondern für jede Zeile einen eigenen
Aufruf zu machen. z.B. so:
Mittlerweile glaube ich allerdings nicht mehr, dass das Problem
überhaupt mit dem "Connection: close" zusammenhängt. Ich tippe derzeit
eher auf irgendwelche versehentlichen Pufferüberläufe - der ganze Code
erscheint mir ehrlich gesagt ein wenig umständlich und fehleranfällig...
:(
Joachim S. schrieb:> void putLine(char *line) {> putString(line);> putString("\r\n");> }
Ich hab es ausgetestet. Es macht kein Unterschied zu meinen Request von
oben: "Wenn ich den das direkt hinter dem Request sende, kommt ein "busy
s..."
Bild 2, übernimmt aber die Daten:"
Joachim S. schrieb:> Mittlerweile glaube ich allerdings nicht mehr, dass das Problem> überhaupt mit dem "Connection: close" zusammenhängt. Ich tippe derzeit> eher auf irgendwelche versehentlichen Pufferüberläufe - der ganze Code> erscheint mir ehrlich gesagt ein wenig umständlich und fehleranfällig...> :(
Recht hast du. Es ist gerade die v1.0 welche stetig verbessert wird.
Natürlich versuche ich schon da den Code so gut wie möglich zu
gestalten, jedoch sollen die hauptsächlichen Dinge, wie erfolgreiches
Senden der Daten, funktionieren. In der Zeit hänge ich die Schaltung an
meine Alarmanlage während der Code damit getestet und verbessert werden
soll. Ich bin eigentlich ganz froh, wenn jemand einen Fehler entdeckt
während ich den Wald vor lauter Bäumen nicht sehe.
Hallo zusammen,
nach stundenlanger suche bin ich jetzt doch fündig geworden. Ich habe
ein Array für empfangene UART-Daten "_uart_string[50]". Anscheindend war
der reserierte Speicherplatz nicht ausreichend und ist beim überlauf auf
den Speicherbreich der Variable des zusammengesetzten GET-Request
gefallen. Dabei wurde mal die erste 4 Zeichen "GET " überschrieben und
mal wieder nicht, was die Fehlersuche deutlich erschwert hat. Ich hatte
zwar ein Überlaufschutz eingebaut, nur leider war der fehlerhaft:
1
ISR(USART_RXC_vect)//Interrupt wird ausgelöst sobald neue Daten im USART-Empfangspuffer liegen
2
{
3
volatilestaticuint8_tcount=0;//Char-Zähler
4
5
//String ist noch nicht komplett
6
if(!_uart_str_complete)
7
{
8
if(UDR!='\n'&&count<50)//kein Abschlusszeichen vorhanden oder Arraylänge erreicht TODO Länge selbst ermitteln
9
{
10
_uart_string[count]=UDR;//in Array schreiben
11
count++;//Array-Zähler inkrementieren
12
}
13
else
14
{
15
_uart_string[count]='\0';//String abschliessen
16
count=0;//Zähler zurücksetzten
17
_uart_str_complete=true;//String komplett
18
}
19
}
20
21
}
Jetzt sieht er besser aus und der Fehler ist bis jetzt nicht mehr
aufgetaucht:
1
ISR(USART_RXC_vect)//Interrupt wird ausgelöst sobald neue Daten im USART-Empfangspuffer liegen
Und bei diesem Beispiel zeigt sich wieder, dass es nur hilfreich ist,
den gesamten Code zu posten und nicht nur Teile, von denen man selbst
"denkt", dass da der Fehler drin steckt. Wie nun deutlich zu erkennen
war, haben alle Beteiligten die ganze Zeit an der falschen Stelle
gesucht und die wildesten Annahmen getroffen, ohne zu wissen, dass
irgendwo in diesem Spaghetti-Code noch die ominöse Variable
"_uart_string" auf eigenartige Weise zusammengesetzt wird -_-