Autor |
Beitrag |
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Mi 22.04.15 17:30
Ein freundliches Hallo an alle!
Ich habe ein Objekt erstellt um über Netzwerk Daten austauschen zu können. Für Accept und Listen habe ich Threads erstellt. Sah alles ganz gut aus, obwohl es meine ersten Threads sind. Doch plötzlich zeigte sich ein seltsames Verhalten: Mit jeder Programmänderung ein neues Verhalten.
Da ich den Begriff "Threadsicher" bei der Beschäftigung mit dem Thema mehrfach gelesen habe und nicht sicher bin, ob meine Treads korekt sind, bitte ich darum sie zu begutachten.
Ich habe zum Warten auf Clientverbindungen folgenden Faden erstellt:
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:
| unit TcpSrvAccept;
interface
uses Classes, Winsock, TCP_IP, WinSock2;
type T_TcpSrvAccept = class(TThread) private oSSock : TSocket ; oSock : TSocket ; oOnAccept : T_AcceptEvent ; oInd : Integer ;
protected procedure Execute; override; procedure Synchronize;
public Constructor Create( OnAcc : T_AcceptEvent; var Sock : TSocket ; Ind : Integer ); procedure Terminate;
end;
implementation
Constructor T_TcpSrvAccept.Create( OnAcc : T_AcceptEvent; var Sock : TSocket ; Ind : Integer ); begin oOnAccept := OnAcc ; oSSock := Sock ; oSock := INVALID_SOCKET; oInd := Ind ; inherited create( false ) ; Priority := tpLower ; FreeOnTerminate := true ; end;
procedure T_TcpSrvAccept.Synchronize; begin if Assigned( oOnAccept ) then oOnAccept( oSock, oInd ); end;
procedure T_TcpSrvAccept.Execute; begin oSock := accept( oSSock, Nil, Nil ); Synchronize; end;
procedure T_TcpSrvAccept.Terminate; begin inherited Terminate; end;
end. |
Die Ereignisroutine "oOnAccept" trägt Socket und Buffer in einem Array ein und startet den Faden neu, um weitere Clients versorgen zu können.
Für den Datenaustausch habe ich einen zweiten Tread erstellt:
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:
| unit Clnt_Listen;
interface
uses SysUtils, Classes, winsock, TCP_IP;
type T_ClntListen = class(TThread) private oSock : TSocket ; oOnReceive : T_ReceiveEvent; oBuffer : AnsiString ; oIndex : Integer ;
protected procedure Execute; override; procedure Synchronize( Buffer : AnsiString; Res : Integer );
public procedure Terminate;
Constructor Create( OnRec : T_ReceiveEvent; Sock : TSocket ; var Buffer : AnsiString ; Ind : Integer );
end;
implementation
Constructor T_ClntListen.Create( OnRec : T_ReceiveEvent; Sock : TSocket ; var Buffer : AnsiString ; Ind : Integer ); begin oSock := Sock ; oOnReceive := OnRec ; oBuffer := Buffer; oIndex := Ind ; inherited create( false ) ; Priority := tpLower; FreeOnTerminate := true ; end;
procedure T_ClntListen.Synchronize( Buffer : AnsiString; Res : Integer ); begin if Res > 0 then begin SetLength( Buffer, Res ); if Assigned( oOnReceive ) then oOnReceive( Res, oIndex, Buffer ); end else begin if Assigned( oOnReceive ) then oOnReceive( Res, oIndex, 'Clnt-Fehler: ' + IntToStr( WSAGetLastError ) ); end; end;
procedure T_ClntListen.Execute; var Res : Integer;
begin repeat Res := recv( oSock, oBuffer[1], BuffLen, 0 ); Synchronize( oBuffer, Res ); until Terminated; end;
procedure T_ClntListen.Terminate; begin inherited Terminate; end;
end. |
Dieser Faden wird beendet, wenn die Verbindung getrennt wird. Ich hoffe, jemand kann mir sagen, ob das "Sicher" ist und wenn nicht, was ich ändern muß.
Sollte das nicht reichen, kann ich auch das ganze Test-Projekt hochladen.
Grüße von der sonnigen Nordsee
Peter
|
|
jfheins
Beiträge: 918
Erhaltene Danke: 158
Win 10
VS 2013, VS2015
|
Verfasst: Mi 22.04.15 20:49
Also an den Threrads gibt es jetzt nicht soo viel auszusetzen. Nur die Methode Synchronize, die du deklariert hast, verwirrt ein wenig, da sie eben nicht mit dem Hauptthread synchronisiert.
Also der Code, den du gezeigt hast, müsste gehen. Falls allerdings in den ganzen Events (oOnAccept, oOnReceive) irgendwas gemacht wird, was auf die GUI zugreift, ist das böse. Denn diese Events laufen ja immer noch im erstellten Thread.
Gerade die Formulierung "trägt Socket und Buffer in einem Array ein" lässt die Spekulation zu, dass Daten geschrieben werden. Falls dort keine Synchronisierung (lock?) stattfindet ist das eher nicht threadsicher.
Falls du dich jetzt fragst, wie man denn Sachen (wie die Eventhandler) eigentlich synchonisiert, sodass sie im Hauptthread ablaufen... So ungefähr:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| procedure TClntListen.TriggerOnReceive(); begin if Assigned( oOnReceive ) then oOnReceive( FRes , FIndex, FBuffer ); end;
procedure TClntListen.Execute; begin oSock := accept( oSSock, Nil, Nil ); FRes := 123; FIndex = 456; FBuffer := 'lalala'; Synchronize(TriggerOnReceive); end; |
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Do 23.04.15 10:58
Hallo jfheins,
danke für Deine Antwort. Sieht so aus, als ob sie mich ein Stück weiter bringt. Zu Deiner Spekulation: Der Thread mit der Funktion "accept" wird terminiert bevor "oSock" in das Array eingetragen wird. Ist es dennoch notwendig, diesen Wert im Tread zu kopieren?
Delphi-Quelltext 1: 2: 3: 4:
| if Sock <> INVALID_SOCKET then begin oAcceptTh.Terminate; oSock[ Ind ].Sock := Sock; |
Ich werde die Treads so umbauen, wie ich Deine Antwort verstanden habe und dann berichten.
Grüße von der wolkigen Nordsee
Peter
|
|
Nersgatt
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Do 23.04.15 11:08
Mit dem Aufruf von .Terminate ist der Thread aber noch nicht beendet. Er ist nur aufgefordert worden, sich zu beenden.
Schau mal in den Quelltext von TThread.Terminate rein. Dort siehst Du, dass dort eigentlich nur die Variable Terminated auf True gesetzt wird.
Wenn nun Deine Execute-Procedure im Thread prinzipiell so aussieht:
Delphi-Quelltext 1: 2:
| while not Terminated do MachIrgendwas; |
wirst Du sehen, dass Dein Thread erst dann Terminiert, wenn "MachIrgendwas" mit seiner Arbeit fertig ist, und der Schleifenkopf das nächste mal auf Terminated prüft.
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Do 23.04.15 12:01
Hallo Jens,
auch Dir Dank für Deine Antwort. Es ist ja gerade mein Problem, dass diese Quelltexte bei meinem Delphi nicht vorhanden sind. Was kann ich tun, damit es sauber abläuft??
Ich habe die Threads jetzt umgebaut, aber nach abbrechen der Verbindung zum Server ist ein erneutes Verbinden nicht möglich. Es muß also im Zusammenhang mit dem "accept-Tread" noch etwas falsch laufen.
Accept:
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:
| unit TcpSrvAccept;
interface
uses Classes, Winsock, TCP_IP, WinSock2;
type T_TcpSrvAccept = class(TThread) private oSSock : TSocket ; oSock : TSocket ; oClntSock : TSocket ; oOnAccept : T_AcceptEvent ; oInd : Integer ;
protected procedure Execute; override; procedure TriggerOnReceive;
public Constructor Create( OnAcc : T_AcceptEvent; var Sock : TSocket ; Ind : Integer ); procedure Terminate;
end;
implementation
Constructor T_TcpSrvAccept.Create( OnAcc : T_AcceptEvent; var Sock : TSocket ; Ind : Integer ); begin oOnAccept := OnAcc ; oSSock := Sock ; oSock := INVALID_SOCKET; oClntSock := INVALID_SOCKET; oInd := Ind ; inherited create( false ) ; Priority := tpLower ; FreeOnTerminate := true ; end;
procedure T_TcpSrvAccept.TriggerOnReceive; begin if Assigned( oOnAccept ) then oOnAccept( oClntSock, oInd ); end;
procedure T_TcpSrvAccept.Execute; begin oSock := accept( oSSock, Nil, Nil ); oClntSock := oSock; Synchronize( TriggerOnReceive ); end;
procedure T_TcpSrvAccept.Terminate; begin inherited Terminate; end;
end. |
Listen:
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:
| unit Clnt_Listen;
interface
uses SysUtils, Classes, winsock, TCP_IP;
type T_ClntListen = class(TThread) private oSock : TSocket ; oOnReceive : T_ReceiveEvent; oBuffer : AnsiString ; oMsg : AnsiString ; oIndex : Integer ; oRes : Integer ;
protected procedure Execute; override; procedure TriggerOnReceive;
public procedure Terminate;
Constructor Create( OnRec : T_ReceiveEvent; Sock : TSocket ; var Buffer : AnsiString ; Ind : Integer );
end;
implementation
Constructor T_ClntListen.Create( OnRec : T_ReceiveEvent; Sock : TSocket ; var Buffer : AnsiString ; Ind : Integer ); begin oSock := Sock ; oOnReceive := OnRec ; oBuffer := Buffer; oMsg := '' ; oIndex := Ind ; inherited create( false ) ; Priority := tpLower; FreeOnTerminate := true ; end;
procedure T_ClntListen.TriggerOnReceive; begin if Assigned( oOnReceive ) then oOnReceive( oRes, oIndex, oMsg ); end;
procedure T_ClntListen.Execute; var Res : Integer;
begin repeat SetLength( oBuffer, BuffLen ); Res := recv( oSock, oBuffer[1], BuffLen, 0 ); if Res >= 0 then begin SetLength( oBuffer, Res ); oMsg := oBuffer ; end else oMsg := 'Clnt-Fehler: ' + IntToStr( WSAGetLastError ); oRes := Res; Synchronize( TriggerOnReceive ); until Terminated; end;
procedure T_ClntListen.Terminate; begin inherited Terminate; end;
end |
Die Ereignisroutine "iOnAccept" speichert den Client-Socket in einem Array und startet einen Listen-Thread sowie einen neuen Accept-Tread für weitere Clients. Außerdem wird eine externe Ereignisroutine aufgerufen. Wenn ich das richtig verstanden habe muß der Tread aber beendet sein, wenn ich den Client-Socket speichere, aber das geschieht ja erst mit dem Beenden der Ereignisroutine. Was kann ich also tun, um diese Nuß zu knacken??? Einen weiteren Thread??
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| procedure T_TcpServer.iOnAccept( Sock : TSocket; Ind : Integer ); var I : Integer;
begin if Sock <> INVALID_SOCKET then begin oAcceptTh.Terminate; oSock[ Ind ].Sock := Sock ; |
Grüße von der wieder sonnigen Nordsee
Peter
|
|
baumina
Beiträge: 305
Erhaltene Danke: 61
Win 7
Delphi 10.2 Tokyo Enterprise
|
Verfasst: Do 23.04.15 13:45
Der accept-Thread hat keine Schleife (while not Terminated), deswegen läuft der genau einmal und ist dann fertig.
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Do 23.04.15 16:25
Hallo baumina,
Dank für Deine Antwort! "oAcceptTh.Terminate;" Habe ich auch aus lauter Verzweifelung hineingeschrieben, aber ich kann einen Client nicht mehr verbinden, wenn ein Abbruch der Verbindung stattgefunden hat. Seltsamer Weise ging das mal alles.
Grüße von der noch immer sonnigen Nordsee
Peter
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Fr 24.04.15 11:02
Ein freundliches Hallo an alle,
nach wie vor ist das Verhalten meiner Fäden etwas seltsam. Ich habe den Listen-Thread umgebaut, damit ich ihn leicht terminieren kann. mit "Res := recv( oSock, oBuffer[1], BuffLen, MSG_PEEK );" sollte die Funktion Prüfen, ob etwas empfangen wurde und sofort mit der Anzahl Zeichen zurückkehren. Erst wenn die Anzahl der Zeichen > 0 ist soll der Puffer gelesen werden. Damit soll die Schleife ständig durchlaufen werden und damit auch beendet werden können. Doch Banane!
"Res := recv( oSock, oBuffer[1], BuffLen, MSG_PEEK );" verhält sich wie "Res := recv( oSock, oBuffer[1], BuffLen, 0 );"!
Es muß also noch etwas Faul sein! Hier der aktuelle Stand: (Der Rest ist wie oben)
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:
| procedure T_ClntListen.Execute; var Res : Integer;
begin repeat SetLength( oBuffer, BuffLen ); Res := recv( oSock, oBuffer[1], BuffLen, MSG_PEEK ); if Res = 0 then Application.ProcessMessages else begin Res := recv( oSock, oBuffer[1], BuffLen, 0 ); if Res >= 0 then begin SetLength( oBuffer, Res ); oMsg := oBuffer ; end else oMsg := 'Clnt-Fehler: ' + IntToStr( WSAGetLastError ); oRes := Res; Synchronize( TriggerOnReceive ); end; until Terminated; end; |
So hatte ich mir das gedacht:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| procedure T_TcpClient.Disconnect; begin oListen.Terminate; oListen.WaitFor ; oCloseSocket( nil, oSock, true ); oConn := false; end; |
Grüße von der sonnigen Nordsee mit leichten Schleierwolken
Peter
|
|
Sinspin
Beiträge: 1327
Erhaltene Danke: 117
Win 10
RIO, CE, Lazarus
|
Verfasst: Fr 24.04.15 13:14
Hallo Peter,
warum überschreibst Du Terminate? Du veränderst dessen Verhalten ja nicht in der Ableitung. Zumal die Ableitung nicht vollständigt ist. In der Deklaration fehlt ein override.
Thread Prioriät sollte man nie verändern. Wen man nicht will das ein Thread permanent CPU verbraucht kann man ihn auf ein Ereignis warten lassen oder immer wieder schlafen schicken via Sleep.
Niemals Application.ProcessMessages in einem Thread verwenden! Das gehört da einfach nicht hin.
Threads Debuggen ist nicht ganz lustig. Aber Du könntest via Windows.OutputDebugString Informationen nach außen liefern. Die werden Dir in Delphi unter Ansicht/Debug-Fenster/Ereignisprotokoll angezeigt. So kannst Du verfolgen was wann passiert. Hoffe das es das in D4 schon gibt.
Wenn irgendwie möglich solltest Du versuchen eine neuere Delphi version aufzutreiben
Grüße von der arabischen Halbinsel.
_________________ Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Warum sollte unser aller Mutter, die Natur, nicht die gleichen Rechte haben?
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Fr 24.04.15 14:25
Hallo Stefan,
Dank Dir für Deine Antwort. Einen Fehler habe ich inzwischen gefunden, doch der hatte nichts mit den Thread zu tun. Sleep hatte ich probiert, "Application.ProcessMessages" war eine Verzweiflungstat. Inzwischen habe ich wieder einiges zurückgebaut. "Ansicht/Debug-Fenster/Ereignisprotokoll" gibt es bei meinem Delphi leider nicht. Aber ich kenne jetzt ein Problem.
"recv" gibt die Kontrolle erst zurück, wenn etwas empfangen wurde, auch wenn es ein Abbruch war. Ich hatte erst "oListen.Terminate;" dann "oCloseSocket( nil, oSock, true );" aufgerufen. Umgekehrt funktioniert es dann.
Nun muß ich noch einen Fehler auf der Serverseite finden und kann dann hoffentlich von Erfolg berichten.
Grüße auf die arabische Halbinsel von der noch sonnigen Nordsee
Peter
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Sa 25.04.15 12:29
Hallo Stefan,
Ich vergaß: "Terminate" stammt noch aus dem ersten Ansatz, bei dem ich dachte hier müsse eventuell noch etwas getan werden. Solche Dinge bereinige ich in der Regel, wenn es erst einmal grob läuft.
Es gibt aber noch ein Problem: Wenn der Client die Verbindung abbricht wird der Server-Socket ungültig. Ein wartender Accept kehrt mit -1 zurück. Ein neuer Listen am Serversocket erhält ebenfals -1 und damit ist kein Verbinden mehr möglich. Der Client ruft "closesocket( Sock );" auf, dann "WSACleanup;", danach "oListen.Terminate;". Aber auch ein Aufruf von "oListen.Terminate;" vor "WSACleanup;" ändert nichts. Irgend etwas ist hier noch faul.
Grüße von der nieselnden Nordsee
Peter
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Mo 27.04.15 11:11
Ein freundliches Hallo an alle,
nach diversen Versuchen verhält sich das ganze ziemlich stabil, aber nicht so wie es soll. Der Datenaustausch über den Listen-Thread funktioniert sicher. Wenn aber der Client die Verbindung abbricht, wird auch der Serversocket abgeschossen. Die Frage ist nun: liegt es am Thread oder am Socket? Wird der Serversocket richtig ubergeben??
Hier der aktuelle "Execute" des Listen-Faden:
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:
| procedure T_ClntListen.Execute; var Res : Integer; Buff : AnsiString;
begin repeat SetLength( Buff, BuffLen ); Res := recv( oSock, Buff[1], BuffLen, 0 ); oErr := WSAGetLastError; oRes := Res; if Res > 0 then begin SetLength( Buff, Res ); oMsg := Buff; end else begin oMsg := 'Clnt-Fehler: ' + IntToStr( oErr ); Terminate; Res := shutdown( oSock, SD_BOTH ); if Res = 0 then begin oMsg := oMsg + ' ' + IntToStr( Res ); Res := closesocket( oSock ); if Res = 0 then begin oMsg := oMsg + ' ' + IntToStr( Res ); WSACleanup; end; end; end; Synchronize( TriggerOnReceive ); SetLength( Buff, BuffLen ) ; until Terminated; end; |
Wenn der Client die Verbindung abbricht, kehrt auch "accept" zurück und im folgenden schlagen alle Verbindungsversuche des Client fehl. Der "accept - Thread" kann nicht aufgebaut werden weil zuvor Listen fehlschlägt, aber auch ohne Listen geht es dann nicht. Wird der "Serversocket" neu aufgebaut, so werden alle wartende Verbindungsversuche akzeptiert! (Auch mehrere Versuche eines Client vor dem Neuaufbau des Serversocket.)
Warscheinlich ist der Fehler so einfach, dass ich ihn nicht finde.
Hier der "accept - tread":
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:
| unit TcpSrvAccept;
interface
uses Classes, Winsock, TCP_IP, WinSock2;
type T_TcpSrvAccept = class(TThread) private oSSock : TSocket ; oClntSock : TSocket ; oOnAccept : T_AcceptEvent ; oInd : Integer ;
protected procedure Execute; override; procedure TriggerOnReceive;
public Constructor Create( OnAcc : T_AcceptEvent; var Sock : TSocket ; Ind : Integer );
end;
implementation
Constructor T_TcpSrvAccept.Create( OnAcc : T_AcceptEvent; var Sock : TSocket ; Ind : Integer ); begin oOnAccept := OnAcc ; oSSock := Sock ; oClntSock := INVALID_SOCKET; oInd := Ind ; inherited create( false ) ; Priority := tpLower ; FreeOnTerminate := true ; end;
procedure T_TcpSrvAccept.TriggerOnReceive; begin if Assigned( oOnAccept ) then oOnAccept( oClntSock, oInd ); end;
procedure T_TcpSrvAccept.Execute; begin oClntSock := accept( oSSock, Nil, Nil ); Synchronize( TriggerOnReceive ); end;
end. |
Den Server - Socket habe ich als "var" übergeben, weiß aber nicht, ob das notwendig ist. Das Verhalten ändert sich nicht.
Der Aufbau des Serversocket:
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: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179:
| procedure T_TcpServer.ConnectNext( I : Integer ); var Res : Integer;
begin if oDebug then oDebugInf.Lines.Add( 'listen Client: ' + IntToStr( I ) ); Res := listen( oSSock, 10 ); if oDebug then oDebugInf.Lines.Add( 'listen: ' + IntToStr( Res ) + CrLf + IntToStr( WSAGetLastError ) ); if Res = 0 then begin if oDebug then oDebugInf.Lines.Add( 'accept: ' + IntToStr( I ) ); oWait := true; oAcceptTh := T_TcpSrvAccept.Create( iOnAccept, oSSock, I ); end; end;
function T_TcpServer.oStartup : Integer; var VerR : word; WSADATA : TWSAData; Res : Integer;
begin if oDebug then oDebugInf.Lines.Add( 'WSAStartup' ); oSSock := INVALID_SOCKET; VerR := 2; Res := WSAStartup( VerR, WSADATA ); if oDebug then oDebugInf.Lines.Add( 'WSAStartup: ' + IntToStr( Res ) ); if Res <> 0 then begin if oDebug then oDebugInf.Lines.Add( 'Fehler bei WSAStartup: ' + IntToStr( Res ) ); SetError( 'Fehler beim Initialisieren der Netzwerkverbindung: ' + GetWSAError( Res ) ); end; Result := Res; end;
function T_TcpServer.ogetaddrinfo( var ARes : PAddrInfo ) : Integer; var hints : PAddrInfo; Res : Integer;
begin if oDebug then oDebugInf.Lines.Add( 'getaddrinfo' ); hints := AllocMem( SizeOf( TAddrInfo ) ); hints.ai_family := AF_INET; hints.ai_socktype := SOCK_STREAM; hints.ai_protocol := IPPROTO_TCP; hints.ai_flags := AI_PASSIVE; Res := getaddrinfo( nil, PChar( oPort ), hints, ARes ); if oDebug then oDebugInf.Lines.Add( 'getaddrinfo: ' + IntToStr( Res ) ); if Res <> 0 then begin if oDebug then oDebugInf.Lines.Add( 'Fehler bei getaddrinfo: ' + IntToStr( Res ) ); SetError( 'Fehler bei der auflösung des Servernamens oder Ports: ' + CrLf + GetWSAError( Res ) ); oCloseSocket( nil, INVALID_SOCKET, true ); end; FreeMem( hints ); Result := Res; end;
function T_TcpServer.oGetsocket( ARes : PAddrInfo ): Integer; begin if oDebug then oDebugInf.Lines.Add( 'socket' ); oSSock := socket( ARes.ai_family, ARes.ai_socktype, ARes.ai_protocol ); if oDebug then oDebugInf.Lines.Add( 'socket: ' + IntToStr( oSSock ) ); if oSSock = INVALID_SOCKET then begin if oDebug then oDebugInf.Lines.Add( 'Fehler bei socket: ' + IntToStr( WSAGetLastError ) ); SetError( 'Fehler beim Einrichten des Ports: ' + CrLf + GetWSAError( oSSock ) ); oCloseSocket( ARes, INVALID_SOCKET, true ); Result := -1; end else Result := 0; end;
function T_TcpServer.oSetPortOpt( ARes : PAddrInfo ): Integer; var Res : Integer;
begin if oDebug then oDebugInf.Lines.Add( 'setsockopt' ); Res := setsockopt( oSSock, SOL_SOCKET, SO_REUSEADDR, 'true', 4 ); if oDebug then oDebugInf.Lines.Add( 'setsockopt: ' + IntToStr( Res ) ); if Res <> 0 then begin if oDebug then oDebugInf.Lines.Add( 'Fehler bei setsockopt: ' + IntToStr( Res ) ); SetError( 'Fehler beim Setzen der Port - Optionen: ' + CrLf + GetWSAError( Res ) ); oCloseSocket( ARes, oSSock, true ); end; Result := Res; end;
function T_TcpServer.obind( ARes : PAddrInfo ): Integer; var Res : Integer;
begin if oDebug then oDebugInf.Lines.Add( 'bind' ); Res := bind( oSSock, ARes.ai_addr^, ARes.ai_addrlen ); if oDebug then oDebugInf.Lines.Add( 'bind: ' + IntToStr( Res ) ); if Res = SOCKET_ERROR then begin if oDebug then oDebugInf.Lines.Add( 'Fehler beim binden: ' + IntToStr( Res ) ); SetError( 'Fehler beim Verbinden mit dem Port: ' + CrLf + GetWSAError( Res ) ); oCloseSocket( ARes, oSSock, true ); end; Result := Res; end;
function T_TcpServer.olistenSSock( ARes : PAddrInfo ): Integer; var Res : Integer;
begin freeaddrinfo( ARes ); if oDebug then oDebugInf.Lines.Add( 'listen' ); Res := listen( oSSock, 10 ); if oDebug then oDebugInf.Lines.Add( 'listen: ' + IntToStr( Res ) ); if Res <> 0 then begin if oDebug then oDebugInf.Lines.Add( 'Fehler beim listen: ' + IntToStr( Res ) ); SetError( 'Fehler beim listen am Server-Socket: ' + CrLf + GetWSAError( Res ) ); oCloseSocket( nil, oSSock, true ); end; Result := Res; end;
procedure T_TcpServer.oaccept( I : Integer ); begin if oDebug then oDebugInf.Lines.Add( 'accept' ); oWait := true; oAcceptTh := T_TcpSrvAccept.Create( iOnAccept, oSSock, I ); end;
procedure T_TcpServer.PrepConnect( I : Integer ); var ARes : PAddrInfo; Res : Integer;
begin Res := oStartup; if Res = 0 then Res := ogetaddrinfo( ARes ); if Res = 0 then Res := oGetsocket ( ARes ); if Res = 0 then Res := oSetPortOpt ( ARes ); if Res = 0 then Res := obind ( ARes ); if Res = 0 then Res := olistenSSock( ARes ); if Res = 0 then oaccept( I ); end;
|
"ConnectNext" startet einen neuen "accept-thread", wenn eine Verbindung aufgebaut wurde. 10 Vebindungen werden angenommen. wenn eine Verbindung abgebrochen wird, kann eine Neue aufgebaut werden. (So soll es sein.) Bisher werden alle Verbindungen abgebrochen, wenn ein Client sich "Abmeldet".
Grüße von der heute wieder sonnigen Nordsee
Peter
|
|
Sinspin
Beiträge: 1327
Erhaltene Danke: 117
Win 10
RIO, CE, Lazarus
|
Verfasst: Mo 27.04.15 18:13
Hallo Peter,
kannst Du bitte mal dein gesamtes Testprojekt anhängen? Mit den ausschnitten tue ich mich leider recht schwehr durchzusteigen woher die Probleme kommen könnten.
_________________ Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Warum sollte unser aller Mutter, die Natur, nicht die gleichen Rechte haben?
Für diesen Beitrag haben gedankt: Peter18
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Di 28.04.15 10:23
Hallo Stefan,
Dank Dir, Sende ich Dir zu.
Grüße von der sonnigen Nordsee
Peter
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Mo 04.05.15 15:12
Ein freundliches Hallo an alle,
kleine Ursache, große Wirkung. Die Probleme, die ich zum Schluß hatte, hatten nichts mit den Threads oder mit Socketeinstellungen zu tun. Wenn man bei größeren Änderungen zwischen durch vom Telefon belästigt wird, kann man schon mal etwas als erledigt abhacken, was aber doch noch nicht fertig ist.
So war es auch hier. Der Client sollte eine Verbindung aufbauen, wenn Server und Port eingetragen sind. Durch den Anruf fehlte die Prüfung, ob bereits eine Verbindung besteht. Dadurch hat der Client 2 Verbindungen aufgebaut, was zu "lustigen" Nebeneffekten führte. Die haben mich in die Irre geführt und ich habe an der falschen Stelle gesucht.
Grüße von der sonnigen Nordsee
Peter
|
|
|