Seite 1 von 5
Webserver bzw. Problem mit SendNetworkData()
Verfasst: 29.12.2019 11:01
von Gimbly
Hallo in die Runde!
Bin nicht so der Linux-Experte aber habe mir über die Feiertage mal vorgenommen, den Atomic-Webserver in der echten Welt des Internetz zum Laufen zu kriegen.
Unter Ubuntu inner VirtualBox kein Problem (localhost). Da schickt er mir problemlos auch große Dateien in einem Rutsch per SendNetworkData().
Aber auf meinem extra dafür angemieteten V-Server (VPS Entry Linux V3 von strato mit Ubuntu 18.04.3 LTS "Anbindung bis zu 100 MBit/s") krieg ich mit SendNetworkData() nur maximal 79520 Bytes verschickt.
Mehr geht nur mit einer Schleife, die SendNetworkData() so oft ausführt, bis alles verschickt ist.
Woran kann das liegen?
Ah, gerade hab ich gesehen, dass beim wiederholten Abruf einer Seite die gesamten 242552 Bytes in einem Durchgang geschickt werden. Gestern Abend ging das generell nur in mehreren SendNetworkData()-Durchgängen. Liegt es vielleicht daran, dass im Moment weniger los ist im Netz/bei strato?
Vielen Dank schon mal für die Antworten!
Re: Webserver bzw. Problem mit SendNetworkData()
Verfasst: 29.12.2019 11:48
von STARGÅTE
Laut Hilfe kannst du bei einer TCP Verbindung generell maximal 65536 Bytes verschicken und musst so oder so in einer Schleife eine größere Menge an Daten abarbeiten.
Das SendNetworkData() u.u. auch mit einem "rutsch" mehr als diese Länge verschickt ist nicht immer gegeben.
https://www.purebasic.com/documentation ... kdata.html
Re: Webserver bzw. Problem mit SendNetworkData()
Verfasst: 29.12.2019 12:37
von Gimbly
Hallo STARGÅTE,
Danke für die schnelle Antwort.
Das mit den 65536 Bytes hatte ich zwar gelesen, aber nicht ernst genommen, da im localhost-Betrieb scheinbar gar keine Grenze besteht u. im "echten" Internet zumindest nicht diese (65536).
Dieser Wert scheint wohl eher ein Relikt aus früheren Zeiten zu sein?
Im Atomic-Webserver als "offizielles Purebasic-Beispiel" wird SendNetworkData() ja auch nur einmal aufgerufen.
Würdest Du diese Schleife für geeignet halten, oder sollte da noch ein Delay() rein?:
Code: Alles auswählen
SendLen.l = *BufferOffset - *FileBuffer + FileLength : *FileBuffer_ = *FileBuffer
While SendLen : gesendet = SendNetworkData(ClientID, *FileBuffer_, SendLen)
If gesendet > 0 : SendLen - gesendet : *FileBuffer_ + gesendet : EndIf
Wend
FreeMemory(*FileBuffer)
Re: Webserver bzw. Problem mit SendNetworkData()
Verfasst: 29.12.2019 13:21
von mk-soft
Das ist kein Relikt, sondern das kommt aus dem TCP Protokoll.
Ausserdem solltest du den Rückgabewert von SendNetworkData prüfen und bei bedarf mit einem kleinen Delay(20) auch mal passieren.
Es kann nämlich auch mal sein das der Empfangsbuffer vom Partner voll ist.
Man besten mal das TCP-Protokoll und das ISO-Schichtmodell nachlesen.
Re: Webserver bzw. Problem mit SendNetworkData()
Verfasst: 29.12.2019 14:32
von NicTheQuick
Es gibt sowohl Sende- als auch Empfangspuffer. Die können schnell mal voll sein, wenn man ohne Pause Daten rausschiebt.
Als erweiterte Sendefunktion würde ich sowas hier machen:
Code: Alles auswählen
Procedure.i sendNetworkDataEx(hClient.i, *buffer, length.i, timeout.i = 10000)
Protected result.i, time.i = ElapsedMilliseconds() + timeout, bytesSent.i = 0
Repeat
result = SendNetworkData(hClient, *buffer + bytesSent, length - bytesSent)
If result = -1 Or result = 0
; Warte, wenn nichts gesendet werden konnte
Delay(10)
Continue
Else
; Ansonsten resette das Timeout und aktualisiere bytesSent
time.i = ElapsedMilliseconds() + timeout
bytesSent += result
EndIf
If ElapsedMilliseconds() > time
; Wurde das Timeout erreicht, brich ab und gib zurück wie viel gesendet werden konnte.
ProcedureReturn bytesSent
EndIf
Until bytesSent = length
ProcedureReturn bytesSent
EndProcedure
Falls du auch ReceiveNetworkData nutzt, musst du damit genauso verfahren. Denn damit erhältst du die Daten auch nur Päckchenweise und nicht alle auf einmal.
'localhost' ist übrigens ein Spezialfall unter Linux. Das ist nur das
Loopback-Device, also kein echtes physisches Netzwerk. Das hat keine spezifischen Sende- und Empfangspuffer.
Gestern Abend ging das generell nur in mehreren SendNetworkData()-Durchgängen. Liegt es vielleicht daran, dass im Moment weniger los ist im Netz/bei strato?
Da du eine VM hast, teilst du dir deine Resourcen natürlich auch mit anderen VMs auf dem selben Host. Vielleicht hat nicht jede VM ihren eigenen physischen 100 MBit/s-Port, sondern alle laufen gemeinsam über einen 10 GBit/s-Port oder ähnliches. Dann kann das natürlich zu starken Schwankungen führen. Abgesehen davon tätigt auch Linux von sich aus immer wieder externe Anfragen um zum Beispiel nach neuen Updates zu suchen. Das kann sich alles auswirken. Und deswegen musst du für alle Eventualitäten gewappnet sein. Auch Timeouts sind wichtig, falls jemand mal in Mitten einer Übertragung den Stecker zieht oder das WLAN verliert. Dann kann keine der beiden Seiten der anderen mitteilen, dass sie jetzt weg ist. Dafür braucht es dann Timeouts um die Verbindung selbst nach einer Weile zu kappen.
Re: Webserver bzw. Problem mit SendNetworkData()
Verfasst: 29.12.2019 15:05
von Gimbly
Vielen herzlichen Dank für die ausführlichen Antworten!
Also doch immer ne Schleife mit Delay(), dachte ich mir schon fast.
Der Atomic-Webserver-Code sollte da vielleicht mal ensprechend angepasst werden, sonst ist das ein bisschen irreführend.
Jetzt hab ich noch ein anderes Problemchen, und zwar sowohl auf meinem lokalen Linux, als auch auf dem strato-Server:
Ich starte den Webserver ja im Terminal und beende ihn irgendwann mit Strg-C.
Wenn ich ihn dann wieder starten will, muss ich das manchmal (aber nicht immer) sehr oft wiederholen, weil dauernd "Error: can't create the server (port in use ?)." kommt.
Laut "sudo fuser 80/tcp" ist da aber nichts, was den Port 80 blockiert.
Hat jemand ne Ahnung was da los ist?
Re: Webserver bzw. Problem mit SendNetworkData()
Verfasst: 29.12.2019 15:55
von mk-soft
Kein wunder...
Programm ordnungsgemäß schließen mit CloseNetzworkServer...
Re: Webserver bzw. Problem mit SendNetworkData()
Verfasst: 29.12.2019 16:20
von NicTheQuick
Das passiert, wenn ein Prozess hart abgebrochen wird ohne die Verbindung korrekt zu schließen. Dann gibt es ein festgelegtes Timeout im Kernel, das den Port erst nach einer Weile wieder freigibt.
Besser wäre es auf STRG+C korrekt zu reagieren und dann 'CloseNetworkServer()' aufzurufen um die Verbindung sauber zu schließen. Allerdings weiß ich gerade nicht genau wie man das abfangen könnte. Bestimmt gibt es da schon fertige Codes im englischen Forum, wenn du danach suchst.
Auf die Schnelle hab ich das hier gebastelt:
Code: Alles auswählen
Procedure terminateHandler(signal.i)
PrintN("Terminated with signal " + signal)
CloseNetworkServer(1)
CloseConsole()
End
EndProcedure
; SIGINT (STRG+C)
signal_(2, @terminateHandler())
; SIGTERM
signal_(15, @terminateHandler())
InitNetwork()
CreateNetworkServer(1, 8081)
OpenConsole()
Repeat
Print("Gimme input: ")
Input()
ForEver
CloseNetworkServer(1)
CloseConsole()
Re: Webserver bzw. Problem mit SendNetworkData()
Verfasst: 29.12.2019 17:30
von Gimbly
Hallo NicTheQuick,
vielen Dank für den Code.
Hab ihn jetzt mal eingebaut und krieg nach Strg-C auch die Meldung "Terminated with signal 2".
Trotzdem ist Port 80 manchmal noch einige Zeit blockiert und zwar scheinbar immer dann, wenn vorher eine Datei angefragt wurde, die nicht vorhanden ist.
Hab für den Fall jetzt "CloseNetworkConnection(ClientID)" eingebaut (im Browser kommt dann: "Fehler: Verbindung unterbrochen").
Aber das bringts auch nicht.
Re: Webserver bzw. Problem mit SendNetworkData()
Verfasst: 29.12.2019 17:43
von NicTheQuick
Wenn du noch offene Client-Verbindungen hast, solltest du diese auch in terminateHandler() schließen.
Abgesehen davon, kann es auch sein, dass Purebasic innerhalb von CreateNetworkServer() das Flag SO_REUSEADDR nicht setzt.
Laut strace könnte das sogar stimmen:
Code: Alles auswählen
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
ioctl(3, FIONBIO, [1]) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(8081), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 100)
Ich sehe hier keinen Parameter, der irgendwas mit 'REUSE' zu tun hat.
SOCKET(7) hat geschrieben:Linux will allow port reuse only with the SO_REUSEADDR option when
this option was set both in the previous program that performed a
bind(2) to the port and in the program that wants to reuse the port.