Autor Beitrag
ZO_orki
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 42



BeitragVerfasst: Do 01.06.06 22:21 
Hallo,

ich habe folgendes Problem: Ich habe einen Server und mehrere Clients (TServerSocket & TClientSocket). Wenn sich ein Client einloggt, funktioniert alles prima, ich kann Text hin und her jagen, etc. Das Problem: Wenn ein Client die Verbindung schließt und neu connecten will, bekomm ich die Fehlermeldung, dass ein Socket nur einmal verwendet werden darf. Logisch. Die Frage ist wie kann ich das umgehen? Wenn ich sowas probiere wie Socket.Close oder .Disconnect, dann spukt er mir immer Zugriffsverletztungsfehler aus.

Edit: Diese Probleme treten auf, wenn ich die Funktionen direkt auf das Socket, dass im OnClientDisconnect übergeben wird anwende. Wenn ich dagegen Socket.Connections[verbindung].Disconnect verwende, spukt er mir zwar keinen fehler aus, aber das neu connecten geht immer noch nicht. Das Socket mit .Free freigeben funktioniert übrigens auch nicht.

Hoffe ihr könnt mir weiterhelfen

MfG ZO_orki
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Do 01.06.06 23:37 
Moin!

Was tust du denn da nur... :gruebel: speziell hier:
user profile iconZO_orki hat folgendes geschrieben:
bekomm ich die Fehlermeldung, dass ein Socket nur einmal verwendet werden darf.

Wer oder welches Objekt erzeugt diese Fehlermeldung?

user profile iconZO_orki hat folgendes geschrieben:
Wenn ich sowas probiere wie Socket.Close oder .Disconnect, dann spukt er mir immer Zugriffsverletztungsfehler aus.
Edit: Diese Probleme treten auf, wenn ich die Funktionen direkt auf das Socket, dass im OnClientDisconnect übergeben wird anwende.

Das brauchst und darfst du auch gar nicht. Wenn eine Seite die Verbindung trennt (ob Client oder Server ist egal), wird das entsprechende Socket-Objekt auf der anderen Seite der Verbindung automatisch mit entsorgt.

Ehrlich gesagt kann ich mir das nur als Konzept-Fehler in der Benutzung der Sockets erklären. Zeig mal den Code, so kommen wir da nicht weiter, weil du IMHO einen Fehler machst, den du gar nicht bemerkst. ;)

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
ZO_orki Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 42



BeitragVerfasst: Fr 02.06.06 23:08 
Dann geb ich mal meinen Code zum besten:

Der Server erstellt das Socket:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
 Server := TServerSocket.Create(Form1);
 Server.Port := 8954;

 Server.Open;
 Server.OnClientRead := Form1.TServerSocketRead; // Das ist nur, weil bei mir die Sockets
 Server.OnClientConnect := Form1.OnClientConnect; // nicht in der Komponentenpallette und
 Server.OnClientDisconnect := Form1.OnClientDisconnect; // daher nicht im Inspektor sind.

 users := TStringList.Create; // In den beiden StringLists werden Ip des Clients und ein
 users.Sorted := False; // Name den der Client dem Server übermittel gespeichert,
 IPs := TStringList.Create;// natürlich mit dem selben Index
 IPs.Sorted := False;


Der Client erstellt das Socket:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
      Client := TClientSocket.Create(Form1);
      Client.Port := 8954;
      Client.Host := wert; // Die IP wird automatisch "geholt" -> funktioniert bestens

      Client.Open;
      Client.OnRead := Form1.TClientSocketRead;



Wenn der Client connected:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TForm1.OnClientConnect(Sender: TObject; Socket: TCustomWinSocket);
VAR verbindung: Integer;
BEGIN
   IPs.Add(Socket.RemoteAddress); // Zunächst wird die Ip gespeichert, später wird dann
   users.Add(Socket.RemoteAddress); // der Name übermittelt
   verbindung := users.IndexOf(Socket.RemoteAddress); // Das ist die Nummer des Sockets

   Server.Socket.Connections[verbindung].SendText('/logy' + #13); // Der Befehl an den
       // Client seinen Namen zu übermitteln.
END;


Bis hierhin funktioniert alles prima.
Wenn der client offline geht:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure TForm1.OnClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
VAR verbindung: Integer;
BEGIN
//IF(IPs.IndexOf(Socket.RemoteAddress) = -1) THEN Exit;
// (Hatte ich mal zwischenzeitlich drinne)

verbindung := IPs.IndexOf(Socket.RemoteAddress); // Wieder die Nummer der Verbindung

IPs.Delete(verbindung); // Das löschen der Werte in den beiden Stringlists
users.Delete(verbindung);


// Und jetzt das, was Probleme bereitet:
Server.Socket.Connections[verbindung].Disconnect(0);
Server.Socket.Connections[verbindung].Free;

END;

Theoretisch könnte den Schluss auch draußen lassen (Nach dem Post über mir jedenfalls),
jedoch bekomme ich, wenn der Client nach dem Disconnect erneut versucht zu verbinden,
folgende Fehlermeldung (egal obs drinnen ist oder ob nicht):
"Windows-Socket-Fehler: Normalerweise darf jede Socketadresse (Protokoll, Netzwerk oder Anschluss) nur jeweils einmal verwendet werden (10048), auf API 'bind'"
Das spukt der Client aus. Der Server zeigt folgende Meldung an:
"Zugriffsverletzung bei Adresse 004261DE in Modul 'project1.exe'. Lesen von Adresse 1082E8CC"

Beim Compilieren bekomme ich keine Fehlermeldungen, da ist alles in Ordnung. Hoffe ihr könnt was damit anfangen und mir weiterhelfen.

Edit: Wenn ich die Fehlermeldung weckklicke und nochmal versuche zu connecten gehts irgendwie...
MfG ZO_orki
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Sa 03.06.06 23:42 
Moin!

Dachte ich´s mir doch, Konzeptfehler bei den Sockets. ;)

Erstmal das hier:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm1.OnClientConnect(Sender: TObject; Socket: TCustomWinSocket);
VAR verbindung: Integer;
BEGIN
   IPs.Add(Socket.RemoteAddress); // Zunächst wird die Ip gespeichert, später wird dann
   users.Add(Socket.RemoteAddress); // der Name übermittelt
   verbindung := users.IndexOf(Socket.RemoteAddress); // Das ist die Nummer des Sockets
   Server.Socket.Connections[verbindung].SendText('/logy' + #13); // Der Befehl an den
       // Client seinen Namen zu übermitteln.
END;

Das ist leider potentiell kaputter Code; du kannst dich nicht auf die Reihenfolge im Socketarray des Servers derart verlassen, dass eine externe Referenz als Index dient! Besser: Pack das Socket-Objekt in die .Objects[] der User-StringList und nimm .IndexOfObject(), um den Client in deiner Liste zu identifizieren; Nebeneffekt: dur brauchst das .Connections[]-Array im Serversocket nicht und kannst direkt den Wert aus .Objects[] verwenden. Habe ich hier in meinem Code so gemacht, vielleicht ist das klarer im Beispiel. ;)

In diesem Code bist du etwas zu eifrig, du brauchst hier nix weiter zu tun, als deine eigenen Daten wegzuräumen, den Rest macht der ServerSocket schon ganz alleine: ;)
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TForm1.OnClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
VAR verbindung: Integer;
BEGIN

// !! wenn wir mal davon absehen, dass hier praktisch das gleiche gilt, wie oben... !!

verbindung := IPs.IndexOf(Socket.RemoteAddress); // Wieder die Nummer der Verbindung
IPs.Delete(verbindung); // Das löschen der Werte in den beiden Stringlists
users.Delete(verbindung);

// Und jetzt das, was Probleme bereitet:
// --> genau, und das dann einfach weglassen! ;)
END;


cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.