Entwickler-Ecke
Internet / Netzwerk - TServerSocket-Threads für Clienten erstellen/benutzen
starsurfer - Mo 05.03.07 01:20
Titel: TServerSocket-Threads für Clienten erstellen/benutzen
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 :cry:
Mit freundlichen Grüßen
Starsurfer
Narses - 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 [
http://www.delphi-library.de/topic_66706.html], 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
starsurfer - 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
Narses - 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... :oops: 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... :nut: ;)
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. :D
cu
Narses
starsurfer - 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 :zustimm: )
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
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: 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
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: 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)
Narses - 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! :shock: Wenn du non-blocking (=ereignisorientiert) rangehen willst, dann solltest du IMHO mit OnRead/OnWrite arbeiten. Dem gegenüber steht der blocking-Ansatz mit Threads. :mahn: 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 [
http://www.delphi-library.de/topic_66706.html] ausführlich erklärt (ist etwas trickreich, geb ich zu). ;)
cu
Narses
starsurfer - Di 06.03.07 10:10
mmmh, na auch wenns Murks ist hab ich was über Thread und Sockets gelernt :wink:
Das Tut hab ich schon überflogen.
Dann werd ichs mir jetzt mal intravenös geben :les:
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!