Webserver bzw. Problem mit SendNetworkData()

In dieser Linux-Ecke dürfen nur Themen rund um Linux geschrieben werden.
Beiträge, die plattformübergreifend sind, gehören ins 'Allgemein'-Forum.
Gimbly
Beiträge: 169
Registriert: 28.12.2005 14:26
Wohnort: NRW

Webserver bzw. Problem mit SendNetworkData()

Beitrag 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!
Gruß
Markus
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6996
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Webserver bzw. Problem mit SendNetworkData()

Beitrag 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
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Gimbly
Beiträge: 169
Registriert: 28.12.2005 14:26
Wohnort: NRW

Re: Webserver bzw. Problem mit SendNetworkData()

Beitrag 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)
Gruß
Markus
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Webserver bzw. Problem mit SendNetworkData()

Beitrag 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.
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Webserver bzw. Problem mit SendNetworkData()

Beitrag 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.
Bild
Gimbly
Beiträge: 169
Registriert: 28.12.2005 14:26
Wohnort: NRW

Re: Webserver bzw. Problem mit SendNetworkData()

Beitrag 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?
Gruß
Markus
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Webserver bzw. Problem mit SendNetworkData()

Beitrag von mk-soft »

Kein wunder...

Programm ordnungsgemäß schließen mit CloseNetzworkServer...
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Webserver bzw. Problem mit SendNetworkData()

Beitrag 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()
Bild
Gimbly
Beiträge: 169
Registriert: 28.12.2005 14:26
Wohnort: NRW

Re: Webserver bzw. Problem mit SendNetworkData()

Beitrag 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.
Zuletzt geändert von Gimbly am 29.12.2019 17:44, insgesamt 1-mal geändert.
Gruß
Markus
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Webserver bzw. Problem mit SendNetworkData()

Beitrag 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.
Bild
Antworten