Autor |
Beitrag |
starsurfer
      
Beiträge: 334
Win 95, Win 98, Win XP, Win Vista, Linux
D5 Enterprise ,D2005, D6 Personal, Visual C++ Express 2005, C++ Builder 6 E, Dev-C++
|
Verfasst: Mo 05.03.07 01:20
Hallo DF´ler,
ich schreibe gerade an einem Programm was mit TServerSocket und TClientSocket arbeitet.
Im Prinizp funktioniert es ganz gut mit "OnClientRead"(Server) und "OnRead"(Client).
Nur kann es dazu kommen das zuviel Daten innerhalb kürzester Zeit gesendet und empfangen werden sollen. Das tritt auf wenn mehrere Clients angemeldet sind und "quasi" gleichzeitig Aktionen durchführen.
Irgendwie scheint der ServerSocket dann nicht mehr mit dem Verarbeiten der Daten hinterher zu kommen, und es erscheinen unschöne Fehlermeldungen.
Hab natürlich die Hilfe durchforstet und gelesen das man mit ThreadBlocking arbeiten soll.
Ich will nun mit dem Server im ThreadBlocking Mode(jeder Client hat sein eigenen Thread) arbeiten statt wie bisher im NonBlocking Mode (alle Clienten haben ein Thread).
Und genau da liegt das Problem, ich kapiers einfach nich.
So weit bin ich bis jetzt gekommen, Klasse festlegen und Thread erzeugen...und weiter gehts nicht...
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:
| type TServerThreadX = Class(TServerClientThread) public procedure ClientExecute; override; end;
procedure TServerThreadX.ClientExecute; begin
end;
procedure TForm1.ServerSocket1GetThread(Sender: TObject; ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread); begin
SocketThread := TServerThreadX.Create(False, Clientsocket);
end; |
Wie kann ich jetzt in dem Thread auf die Daten zugreifen die der Client sendet?
so hab ichs versucht:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| procedure TServerThreadX.ClientExecute; var s:string; begin
if not Terminated and ClientSocket.Connected then begin
s := ClientSocket.ReceiveText; showmessage(s);
end;
end; |
Aber wenn ich das erste mal vom Client was los schicke erscheint zwar die MessageBox aber diese ist leer und wenn ich nochmal versuche zu senden erhalte ich ein Asynchronen Socket-Fehler #10053 und werde vom Server gekickt.
Hilfe
Mit freundlichen Grüßen
Starsurfer
_________________ GEIZ IST GEIL! - Ihr Sozialamt
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Mo 05.03.07 01:27
Moin!
starsurfer hat folgendes geschrieben: | ich schreibe gerade an einem Programm was mit TServerSocket und TClientSocket arbeitet.
Im Prinizp funktioniert es ganz gut mit "OnClientRead"(Server) und "OnRead"(Client).
Nur kann es dazu kommen das zuviel Daten innerhalb kürzester Zeit gesendet und empfangen werden sollen. Das tritt auf wenn mehrere Clients angemeldet sind und "quasi" gleichzeitig Aktionen durchführen.
Irgendwie scheint der ServerSocket dann nicht mehr mit dem Verarbeiten der Daten hinterher zu kommen, und es erscheinen unschöne Fehlermeldungen. |
Normalerweise führt hohe Last aber nicht zu Fehlern, lediglich die Performance des Servers geht in die Knie...  Leider leider sind das meistens Seiteneffekte oder andere Fehler im Code, die zu den Exceptions/etc. führen...  Schau mal hier, du kannst da praktisch beliebig viel Last erzeugen, der Server ist mir noch nie abgestürzt.
starsurfer hat folgendes geschrieben: | Hab natürlich die Hilfe durchforstet und gelesen das man mit ThreadBlocking arbeiten soll.
Ich will nun mit dem Server im ThreadBlocking Mode(jeder Client hat sein eigenen Thread) arbeiten statt wie bisher im NonBlocking Mode (alle Clienten haben ein Thread). |
An genau dieser Stelle rate ich dir dringend, mit dem Indy TCP-Server zu arbeiten - auch wenn ich sonst wirklich ein "ScktComp-Freund" bin...
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
starsurfer 
      
Beiträge: 334
Win 95, Win 98, Win XP, Win Vista, Linux
D5 Enterprise ,D2005, D6 Personal, Visual C++ Express 2005, C++ Builder 6 E, Dev-C++
|
Verfasst: Mo 05.03.07 10:44
Naja dann werd ich wohl mal mit TIdTCPServer und TIdTCPClient arbeiten...
Nur wieso stellt Borland eine Komponente bereit die so voller Fehler steckt...
Danke Nares
_________________ GEIZ IST GEIL! - Ihr Sozialamt
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Mo 05.03.07 11:29
Moin!
Bitte.
starsurfer hat folgendes geschrieben: | Nur wieso stellt Borland eine Komponente bereit die so voller Fehler steckt...  |
Ähm...  mit "Fehlern und Seiteneffekten im Code" meinte ich nicht die Compos (bis auf das erneute Anstoßen eines WSAAsynSelect für FD_WRITE ist mir da kein "Fehler" bekannt), sondern deinen Code...
Die Sockets (WSA allgemein) sind asynchron schlicht und ergreifend nicht soo leicht zu handeln, wie es auf den ersten Blick aussieht; das ist blocking mit Threads einfacher, aber bei den Anfängern haperts dann immer noch mehr mit den Threads...
Naja, Borland empfiehlt seit D7 ja auch die Indies (und für "produktive" Systeme ist das auch gut so); insofern sind die Sockets eigentlich nur noch zu didaktischen Zwecken interessant.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
starsurfer 
      
Beiträge: 334
Win 95, Win 98, Win XP, Win Vista, Linux
D5 Enterprise ,D2005, D6 Personal, Visual C++ Express 2005, C++ Builder 6 E, Dev-C++
|
Verfasst: Mo 05.03.07 22:46
So mit den Indy Komponenten hab ich ein bisschen rum probiert, geht ganz gut.
Das wiederum hat mich auf eine Idee gebracht in Verbindung mit TServerSocket/TClientSocket.
Auch Narses Tuts waren hilfreich. (Gut geschrieben  )
Und zwar habe ich jeweils das Senden von Daten in ein Thread ausgelagert der in einer bestimmten Zeit nur eine bestimmte Zahl an Befehlen senden kann.
Somit hab ich (denk ich) den Netzwerktraffic zwar nicht verringert dafür aber auf eine größere Zeitspanne verteilt.
Hier mal der Code:
Client
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: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ScktComp, StdCtrls,syncobjs;
type
TSendThread = class(TThread) private protected procedure Execute; override; end;
TForm1 = class(TForm) Button1: TButton; Edit1: TEdit; ClientSocket: TClientSocket; Button2: TButton; Memo1: TMemo; Edit2: TEdit; procedure Button1Click(Sender: TObject); procedure ClientSocketConnect(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button2Click(Sender: TObject); procedure ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket); private public end;
var Form1: TForm1; SendThread:TSendThread; SendeBefehlListe:array of string; CriticalSection: TCriticalSection;
implementation
{$R *.DFM}
procedure TSendThread.Execute; var i:integer; help:string; begin
while length(SendeBefehlListe)>0 do begin
sleep(100);
help:='';
CriticalSection.Enter;
form1.ClientSocket.Socket.SendText(SendeBefehlListe[0]);
if high(SendeBefehlListe)>0 then for i:=0 to high(SendeBefehlListe)-1 do SendeBefehlListe[i]:=SendeBefehlListe[i+1];
setlength(SendeBefehlListe,high(SendeBefehlListe));
CriticalSection.Leave;
if length(SendeBefehlListe)=0 then SendThread.Suspend;
end;
end;
procedure TForm1.Button1Click(Sender: TObject); begin
if button1.Caption='Aktiv' then begin
button1.Caption:='Inaktiv'; Clientsocket.Address:=edit1.text; Clientsocket.Active:=true; memo1.Lines.add('Client aktiv'); setlength(SendeBefehlListe,0);
end else begin
button1.Caption:='Aktiv'; Clientsocket.Active:=false; memo1.Lines.add('Client inaktiv');
end;
end;
procedure TForm1.ClientSocketConnect(Sender: TObject; Socket: TCustomWinSocket); begin
memo1.Lines.add('Am Server angemeldet');
end;
procedure TForm1.ClientSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin
memo1.Lines.add('Vom Server abgemeldet');
end;
procedure TForm1.FormCreate(Sender: TObject); begin
SendThread:=Tsendthread.Create(true); CriticalSection:=TCriticalSection.create;
end;
procedure TForm1.FormDestroy(Sender: TObject); begin CriticalSection.free; SendThread.Terminate;
end;
procedure TForm1.Button2Click(Sender: TObject); begin
if sendThread.Suspended=false then Sendthread.Suspend;
setlength(SendeBefehlListe,length(SendeBefehlListe)+1);
SendeBefehlListe[high(SendeBefehlListe)]:=edit2.text;
Sendthread.Suspended:=false;
end;
procedure TForm1.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket); begin
memo1.Lines.add(Socket.ReceiveText);
end;
end. |
Server
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: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ScktComp,syncobjs;
type
TSendThread = class(TThread) private protected procedure Execute; override; end;
TForm1 = class(TForm) ServerSocket: TServerSocket; Button1: TButton; Memo1: TMemo; procedure Button1Click(Sender: TObject); procedure ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure ServerSocketClientConnect(Sender: TObject; Socket: TCustomWinSocket); procedure ServerSocketClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); private public end;
var Form1: TForm1; SendThread:TSendThread; SendeBefehlListe:Array of string; CriticalSection: TCriticalSection;
implementation
{$R *.DFM}
procedure TSendThread.Execute; var i:integer; begin
while length(SendeBefehlListe)<>0 do begin
sleep(100);
CriticalSection.Enter;
for i:=0 to Form1.ServerSocket.Socket.ActiveConnections-1 do Form1.ServerSocket.Socket.Connections[i].SendText(SendeBefehlListe[0]);
if high(SendeBefehlListe)>0 then for i:=0 to high(SendeBefehlListe)-1 do SendeBefehlListe[i]:=SendeBefehlListe[i+1];
setlength(SendeBefehlListe,high(SendeBefehlListe));
CriticalSection.Leave;
if length(SendeBefehlListe)=0 then SendThread.Suspend;
end;
end;
procedure TForm1.Button1Click(Sender: TObject); begin
if button1.Caption='Aktiv' then begin
button1.Caption:='Inaktiv'; ServerSocket.Active:=true; memo1.lines.add('Server an'); setlength(SendeBefehlListe,0);
end else begin
button1.Caption:='Aktiv'; ServerSocket.Active:=false; memo1.lines.add('Server aus');
end; end;
procedure TForm1.ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket); begin
if sendThread.Suspended=false then SendThread.Suspend; setlength(SendeBefehlListe,length(SendeBefehlListe)+1); SendeBefehlListe[high(SendeBefehlListe)]:=socket.ReceiveText; SendThread.Suspended:=false; end;
procedure TForm1.FormCreate(Sender: TObject); begin CriticalSection:=TCriticalSection.create; SendThread:=TsendThread.Create(true); end;
procedure TForm1.FormDestroy(Sender: TObject); begin CriticalSection.Free; SendThread.Terminate; end;
procedure TForm1.ServerSocketClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin memo1.Lines.Add('Neuer Client angemeldet'); end;
procedure TForm1.ServerSocketClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin memo1.lines.Add('Client abgemeldet'); end;
end. |
Gedacht hab ich mir folgendes:
Die Sendethreads werden bei Programmstart erstellt, aber sofort supendiert.
Sollten sich jetzt Befehle in der Warteschlange befinden werden die Threads so lange ausgeführt bis die Warteschlange leer ist.
Danach werden sie wieder suspendiert.
Mit dem "sleep()" im Thread kann ich festlegen wie viel Befehle gesendet werden können pro Sekunde.
Jetzt mal meine blauäugige Frage, wie intelligent ist so ein Vorgehen?
Kann es zu Fehlern führen weil ich aus dem Thread auf die Sockets zugreife?
(also Test verliefen ohne Fehler)
_________________ GEIZ IST GEIL! - Ihr Sozialamt
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Di 06.03.07 00:45
Moin!
starsurfer hat folgendes geschrieben: | Die Sendethreads werden bei Programmstart erstellt, aber sofort supendiert.
Sollten sich jetzt Befehle in der Warteschlange befinden werden die Threads so lange ausgeführt bis die Warteschlange leer ist.
Danach werden sie wieder suspendiert.
Mit dem "sleep()" im Thread kann ich festlegen wie viel Befehle gesendet werden können pro Sekunde.
Jetzt mal meine blauäugige Frage, wie intelligent ist so ein Vorgehen? |
 Also ich halte es überhaupt nicht für eine gute Idee, den blocking- mit dem non-blocking-Ansatz zu mischen!  Wenn du non-blocking (=ereignisorientiert) rangehen willst, dann solltest du IMHO mit OnRead/OnWrite arbeiten. Dem gegenüber steht der blocking-Ansatz mit Threads.  Die einzige "Sicherheit", dass du bereits weitere Daten senden "darfst" (ohne dass der aktuelle Transfer abbricht) liegt bei dir jetzt im Timing... schlechte Wahl für eine "Sicherheit".
Also nochmal konkret: bei hohen Datenraten ist der Zeitpunkt interessant, an dem du der WSA weitere Daten übergeben kannst: a) wenn das Ereignis OnWrite eintritt (=ereignisorientierter Ansatz) oder b) wenn der letzte send()-Aufruf fertig ist (=blocking-Ansatz mit Threads).
Wie man das ereignisorientiert macht, ist im BinProtoTut ausführlich erklärt (ist etwas trickreich, geb ich zu).
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
starsurfer 
      
Beiträge: 334
Win 95, Win 98, Win XP, Win Vista, Linux
D5 Enterprise ,D2005, D6 Personal, Visual C++ Express 2005, C++ Builder 6 E, Dev-C++
|
Verfasst: Di 06.03.07 10:10
mmmh, na auch wenns Murks ist hab ich was über Thread und Sockets gelernt
Das Tut hab ich schon überflogen.
Dann werd ichs mir jetzt mal intravenös geben 
_________________ GEIZ IST GEIL! - Ihr Sozialamt
|
|
|