Autor |
Beitrag |
Noop
Hält's aus hier
Beiträge: 12
Win 98 SE, Win 2000, Win XP SP1, Win XP SP2, Win 2003 Server
D5 Ent., D7 Prof
|
Verfasst: Fr 21.01.05 11:56
Hallo,
ich habe Serverseitig ein Programm geschrieben, was die Daten auf einen Socket "nur" mit zB 5 000 000 Bytes pro Sekunde einliest (mit Recv) und verarbeitet - soll sozusagen eine Bremse werden, damit übertragung von jemand anders von mir weg nicht mehr meine ganze Bandbreite blockiert (alles in Non-VCL natürlich)
An der Leseschnittstelle des Programmes wird auch live mitgeloggt, wieviel Bytes gelesen wurden und das scheint auch in der Testphase prima zu funktionieren.
Nun habe ich ein "Stress-Test"-Programm geschrieben, der das Programm mal "zubomben" soll um zu gucken wie es auf solche Situationen reagiert:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:
| function BigSend(s:TSocket; buf:PChar; bufsize:integer):integer; var ln, pt: integer; d: double; const timeout: integer=5000; begin d:=now; pt:=0; while (pt<bufsize) and (now-d*86400<timeout) do begin ln:=Send(s, buf[pt], bufsize-pt, 0); if ln>0 then begin Inc(pt, ln); d:=now; end; end; end; ... procedure Test; var .. s:string; ... begin ... SetLength(s, 65432147); for i:=1 to length(s) do s[i]:=Chr(Random(256)); BigSend(MySock.SocketHandle, @s[1], Length(s)); ... end; |
Allerdings ist Delphi mit der Funktion BigSend() schon in einer Sekunde fertig statt erst in zwölf Sekunden, obwohl der Server nur fleißig wie erwartet nur die 5 000 000 Bytes in der Sekunde mit Recv abfragt und es kommen auch in laufe der Zeit auch alle Bytes an.
Wie denn das? Obwohl es ein nichtblockierender Socket ist, habe ich doch mit meiner selbstgecodeten Funktion BigSend() extra die Schleife so programmiert, das er erst wieder rauskommen soll, wenn alle Daten drüben sind oder 5 Sekunden nichts gesendet werden konnte (wo es nicht so weit kam).
Ich bin davon ausgegangen, das Send() zurückgibt, wieviele Bytes er erfolgreich gesendet hat.
Ich kann es mir dadurch erklären, das die Socket-API die mit Send() angegebene Daten irgendwo zwischenspeichert, aber mein Task-Manager zeigt weder auf dem Client- noch auf dem Serverrechner die 65 MB Extra speicherauslastung an.
Kann mir jemand erklären, ob, wie, wo und was die Socket-API zwischenspeichert (außer dem internen 64KB-Socketbuffer, deren Existenz mir schon bekannt ist)?
Ich bin davon ausgegangen, das Send() zurückgibt wieviele Bytes erfolgreich gesendet wurden und Recv() dann je nach Timing vorgibt, wieviele Bytes ich mit Recv() lese dann vorgibt, wieviele Bytes übertragen werden können, zB also Recv(s, buf, 1000, 0) jede nur jede zehntel Sekunde aufgerufen würde eine Übertragungsrate von 10 000 Bytes / s ergeben.
Zuletzt bearbeitet von Noop am Fr 21.01.05 11:59, insgesamt 1-mal bearbeitet
|
|
ScorpionKing
      
Beiträge: 1150
Win XP
|
Verfasst: Fr 21.01.05 11:59
poste doch mal den code von deinem server!!
_________________ Aus dem Urlaub zurück!
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Fr 21.01.05 16:14
"schon in einer Sekunde fertig statt erst in zwölf Sekunden"
wie kommst denn bitte auf 12 sekunden?
desweiteren weißt du schon das
(now-d*86400<timeout)
immer < timeout ist?
delphi rechnet das so wie in der mathematik üblich ->punkt vor strichrechnung
((now-(d*86400))<timeout)
|
|
Noop 
Hält's aus hier
Beiträge: 12
Win 98 SE, Win 2000, Win XP SP1, Win XP SP2, Win 2003 Server
D5 Ent., D7 Prof
|
Verfasst: Fr 21.01.05 17:20
12 Sekunden sorry war ein Rechenfehler, habs leider voreilig im Kopf gemacht - 65432147 Bytes : 5000000 Bytes/sek. = 13,086 sek.
Und die Timeout-Routine war auch fehlerhaft, allerdings selbst nach paar Korrekturen des Timeout-Mechamismus blieb das Phänomen (Timeout wäre ja nie in Anspruch genommen bei BigSend(), selbst wenn ich kein Fehler gemacht hätte).
War allerdings weil ich da gerade nicht an den Rechner drankam wo die Projekte waren.
::
Da der Server schon mehrere Tausend Zeilen Code und sogar eine Plug-In Steuerung hat, habe ich das mal einfacherhalbe jetzt Extra in einem neuen (Test-)Projekt einer Unit vereinfacht und speicherschonender nachgestellt mit:
- Einem ClientSocket und einem ServerSocket - zwar jetzt teilweise VCL geworden, allerdings das Wesentliche, die Recv() und Send()-Funktion ist immernoch die selbe - NonVCL
- 500 000 Bytes pro Sekunde (Timer auf 100 ms; jeweils 50 000 Bytes lesen)
- 5 000 000 Bytes werden mit BigSend() Patched v1.1 gesendet
Ergebnis hier:
- Empfangen korrekterweise jetzt wie erwartet 10 Sekunden
- Senden dauert komischerweise 30-50 ms;
- das selbe tritt bei mir im kleinen und auch im großen Versendungen auf, so das es egal ist, ob ich jetzt 5 MB oder 1000 MB sende.
EDIT: Ach der Quelltext:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121:
| unit unitTest;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, ScktComp, WinSock, StdCtrls;
type TForm1 = class(TForm) ServerSocket1: TServerSocket; Timer1: TTimer; ClientSocket1: TClientSocket; Label1: TLabel; Label2: TLabel; procedure ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); procedure Timer1Timer(Sender: TObject); procedure FormCreate(Sender: TObject); procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); private public end;
function BigSend(s:TSocket; buf:PChar; bufsize:integer):integer;
var Form1: TForm1; buf: string=''; bread: integer=0;
implementation
{$R *.DFM}
function BigSend(s:TSocket; buf:PChar; bufsize:integer):integer; var ln, pt: integer; d: double; const timeout: integer=5; begin d:=now; pt:=0; while (pt<bufsize) and ((now-d)*86400<timeout) do begin ln:=Send(s, buf[pt], bufsize-pt, 0); if ln>0 then begin Inc(pt, ln); d:=now; end else begin end; Application.ProcessMessages; end; result:=0; end;
procedure TForm1.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin ErrorCode:=0; end;
procedure TForm1.Timer1Timer(Sender: TObject); var i,ln,aln:integer; begin for i:=0 to ServerSocket1.Socket.ActiveConnections-1 do begin aln:=0; while aln<50000 do begin ln:=Recv(ServerSocket1.Socket.Connections[i].SocketHandle, buf[1+aln],50000-aln,0); if ln>=0 then aln:=aln+ln; Application.ProcessMessages; end; bread:=bread+aln; end; Label2.Caption:='Empfangen: '+IntToStr(bread)+' Bytes'; if bread>=5000000 then timer1.enabled:=false; end;
procedure TForm1.FormCreate(Sender: TObject); begin ServerSocket1.Port:=4444; ServerSocket1.Active:=True; Timer1.Interval:=100; SetLength(buf,5000000); ClientSocket1.Address:='127.0.0.1'; CLientSocket1.Port:=4444; ClientSocket1.Active:=True; end;
procedure TForm1.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); var s:string; i:integer; c:cardinal; begin SetLength(s, 5000000); for i:=1 to length(s) do s[i]:=Chr(Random(256)); c:=GetTickCount; BigSend(CLientSocket1.Socket.SocketHandle, @s[1],length(s)); c:=GetTickCount-c; Label1.Caption:='5000000 Bytes gesendet in '+inttostr(c)+' ms'; s:=''; end;
end. |
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Fr 21.01.05 17:37
bei deinem senden code kann ich ja noch nichtmal erkennen wo da nen timer drin sein soll der das versenden verlansgamt....
|
|
Noop 
Hält's aus hier
Beiträge: 12
Win 98 SE, Win 2000, Win XP SP1, Win XP SP2, Win 2003 Server
D5 Ent., D7 Prof
|
Verfasst: Fr 21.01.05 17:52
Das Empfangen wird ja verlangsamt und nicht das Versenden! (Andersherum würde das gehen aber ich habe auf der Anwendung, die später mir die Daten schicken soll (also ich empfangen), keinerlei Einfluss)
Muss doch gelten, das das "langsamste Glied in der Kette" die Geschwindigkeit vorgibt, oder nicht?
Also wenn ich nur mit 500 000 Bytes pro Sekunde die Daten aus dem Socket abfrage, wie kann man in unter Sekunde 5 MB Daten verschicken?
Wird der Speicher des Servers oder des Clients belastet?
Oder wie realisier ich sonst, das ich vorgebe, wie schnell der Andere sein darf?
Bei der Arbeit ist das doch auch so: wenn ich mit einem Lastwagen pro Stunde 1000 Kästen transportiert kriege aber ich bekomme am Abfahrtsort 5000 Kästen Pro Stunde zum Wegtransportieren, bleibt die Arbeitsgeschwindigkeit immernoch auf 1000 Kästen pro Stunde, die ich zur anderen Firma transportiere.
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Fr 21.01.05 17:58
die daten gehen verloren??? bzw. der client buffert die solange irgendwo ka ;>
|
|
Noop 
Hält's aus hier
Beiträge: 12
Win 98 SE, Win 2000, Win XP SP1, Win XP SP2, Win 2003 Server
D5 Ent., D7 Prof
|
Verfasst: Fr 21.01.05 18:17
uall@ogc hat folgendes geschrieben: | die daten gehen verloren??? |
Das ist ja die Sache: Bei mir geht so kein Byte verloren, das ist wenigstens eine positive Nachricht
Allerdings würde ich gerne wissen, wie die Socket-API das genau Handhabt und wie ich dem Gegenüber, wovon ich Daten bekomme, sagen kann, er solle nicht so viel Gas geben.
Fall:
Ich habe ein TCP-Wrapper (Art Proxy) gebaut.
Rechner 1 (LAN Intern): Win2K-Router, mit TCP-Wrapper (also mein Programm) @ Port XY zeigt nach Rechner 2: Port XY
Rechner 2 (LAN Intern): Da läuft Apache oder auch RealVNC usw.
Rechner F (F wie Fremd; jemand anders aus dem Internet): Ruft von Internet-IP ab
Was passiert (jetzt meine Vorstellung wenn mein Server fix und fertig ist):
Verbindung/Abfrage:
Rechner F Verbindet mit Rechner 2, Port XY über Internet
Rechner 2 Verbindet mit Rechner 1 im LAN, Port XY
Serverantwort:
Rechner 1 Service gibt Daten als Stream mit 100 Megabits (LAN) zurück
Rechner 2 bekommt Daten leider mit 100 Megabits trotz beschränkung im Recv(), der nur 90 kbit/s (DSL-Bandbreite - 38kbit/s für Reserve = 90kbit/s)
Serverantwort->Problem:
Wenn ich den Code so einbaue wie er ist und ein Wrapper draus mache, hat Rechner 1 die Datenmenge (Worse case: zB eine 650 MB große .iso-Datei) schon fertig gesendet angeblich, allerdings ist jetzt bei einem Rechner 650 MB Speicherauslastung und auch wenn Rechner 2 mit Recv() nur 90 kbit/s empfängt und 90 kbit/s sendet damit nicht meine Bandbreite ganz belegt wird, und die 650 MB werden nur langsam abgebaut.
Und mit 50 Verbindungen gleichzeitig könnte man wunderbar (wer von meinem Problem Kenntniss hat) eine DoS-Attacke fahren 
|
|
|