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
BeitragVerfasst: 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:
ausblenden 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//5 Sekunden
begin
  d:=now;
  pt:=0;
  while (pt<bufsize) and (now-d*86400<timeout) do begin
    ln:=Send(s, buf[pt], bufsize-pt, 0); //ln = Bytes, die erfolgreich (!) gesendet wurden
    if ln>0 then begin //Wenn gesendet werden konnte
      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
ontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic starofftopic star
Beiträge: 1150

Win XP

BeitragVerfasst: Fr 21.01.05 11:59 
poste doch mal den code von deinem server!!

_________________
Aus dem Urlaub zurück!
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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 Threadstarter
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
BeitragVerfasst: 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:

ausblenden volle Höhe 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:
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;

(*
  Benötigt um diesen Code hier einzufügen und auszuführen:
  1 ClientSocket "ClientSocket1"
  1 ServerSocket "ServerSocket1"
  2 Labels "Label1" "Label2"
  1 Timer "Timer1"
*)


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
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  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//5 Sekunden
begin
  d:=now;
  pt:=0;
  while (pt<bufsize) and ((now-d)*86400<timeout) do begin
    ln:=Send(s, buf[pt], bufsize-pt, 0); //ln = Bytes, die erfolgreich (!) gesendet wurden
    if ln>0 then begin //Wenn gesendet werden konnte
      Inc(pt, ln);
      d:=now;
    end else begin
      //showmessage(syserrormessage(WSAGetLastError));break;
    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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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 Threadstarter
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
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Fr 21.01.05 17:58 
die daten gehen verloren??? bzw. der client buffert die solange irgendwo ka ;>
Noop Threadstarter
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
BeitragVerfasst: 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 8)
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 :(