Autor Beitrag
Piranha
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 31



BeitragVerfasst: Mi 06.08.08 23:43 
Ich programmiere grade an einem Programm welches mit TServersocket eine Verbindung aufbaut. Testbedingt läuft die Anwendung nicht 100% stabil, was dazu führt, das diese manchmal abstürzt und so der Socket nicht mehr geschlossen werden kann (FormClose Ereignis tritt ja nicht mehr ein).

Jetzt kann ich die Anwendung auch nicht mehr auf dem Port testen, da der Socket ja belegt ist.

Meine bisherige Notlösung war, das ich mich bei Windows abgemeldet und wieder angemeldet habe.
Da dies bei meinem System allerdings äußerst kontraproduktiv ist (Platten werden nachträglich gemounted) suche ich nach einer anderen Lösung:

1. Wie kriege ich den Port/Socket wieder frei (egal wie)? (Ich nehme in kauf, dafür ein kleines Programm schreiben zu müssen)

2. Wie kann ich es schaffen, dass auch wenn eine Anwedung den Bach runter geht, dass der Socket noch umbedingt geschlossen wird?

(Antworten wie "Anwendung fehlerfrei schreiben" werde ich ignorieren, da das in der Entwicklungphase mehr oder weniger unmöglich ist.)


Zuletzt bearbeitet von Piranha am Mi 06.08.08 23:44, insgesamt 1-mal bearbeitet
GTA-Place
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
EE-Regisseur
Beiträge: 5248
Erhaltene Danke: 2

WIN XP, IE 7, FF 2.0
Delphi 7, Lazarus
BeitragVerfasst: Mi 06.08.08 23:44 
OnFormDestroy hilft auch nicht?

_________________
"Wer Ego-Shooter Killerspiele nennt, muss konsequenterweise jeden Horrorstreifen als Killerfilm bezeichnen." (Zeit.de)
Piranha Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 31



BeitragVerfasst: Mi 06.08.08 23:49 
Oh man das geht hier aber schnell :D

Erstmal danke für die zügige Antwort.
Ich hab diese Methode noch nicht ausprobiert.
Gut möglich das es so funktioniert, ich werde es Testen...

doch im Moment ist mein betroffener Port immernoch gesperrt, weswegen
eine Lösung zu Frage 1. auchnoch sehr wichtig ist.

Danke schonmal....
alias5000
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2145

WinXP Prof SP2, Ubuntu 9.04
C/C++(Code::Blocks, VS.NET),A51(Keil),Object Pascal(D2005PE, Turbo Delphi Explorer) C# (VS 2008 Express)
BeitragVerfasst: Mi 06.08.08 23:53 
Interessante Frage. OnFormDestroy sollte da nicht helfen, wenn es richtig fest abschmiert. Kannst du die Fehler irgendwie so abfangen, dass die Anwendung noch eine Fehlerbehandlung machen kann (try...finally/try...except, TApplicationEvents.OnEcxeption,...)

Gruß
alias5000

_________________
Programmers never die, they just GOSUB without RETURN
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mi 06.08.08 23:57 
Moin!

user profile iconPiranha hat folgendes geschrieben:
Ich programmiere grade an einem Programm welches mit TServersocket eine Verbindung aufbaut.
Mit TServerSocket kann man gar keine Verbindungen aufbauen... :gruebel: sondern nur annehmen. :nixweiss:

user profile iconPiranha hat folgendes geschrieben:
diese manchmal abstürzt und so der Socket nicht mehr geschlossen werden kann (FormClose Ereignis tritt ja nicht mehr ein).
Wenn der Prozess, der das Handle auf den Socket hält, terminiert, dann gibt die WSA den Socket automatisch wieder frei. :arrow: Deine Anwendung läuft noch oder du versuchst mehrfach den Socket zu öffnen. Mal im Taskmanager geschaut, ob der Prozess wirklich weg ist? Das würde nämlich erklären, warum eine Abmeldung da hilft... ;) Oder beim TServerSocket in der IDE die Eigenschaft .Active auf TRUE gesetzt?

Beendest du die Anwendung im Fehlerfall möglicherweise per Strg+F2? Dann könnte es nötig sein, die IDE zu beenden und neu zu starten. :idea:

Wie immer bei sowas: zeig mal Code, der das Verhalten reproduzierbar vorführt, dann kann man auch nach Lösungen (oder Alternativen) suchen; so ist das doch nur im-Trüben-fischen. :|

cu
Narses

//EDIT: Grad nochmal getestet, Server.exe aus dem TermCharTut gestartet und per Taskmanager/Prozesse gekillt, der Socket wird korrekt geschlossen (netstat -a an der Kommandozeile zum Prüfen nehmen)

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



BeitragVerfasst: Do 07.08.08 00:26 
Hallo Narses

Okay, beim ersten Satz hab ich wohl nicht richtig drüber nachgedacht. Ist wohl das "geh mal Computer spielen" Phänomen. Bei beidem weis man was gemeint ist, aber sonst grundsätzlich falsch :D

Btt:

Die Anwendung läuft definitiv nicht mehr. Als ich das erstemal den Fehler hatte hab ich solche grundlegenden Dinge erstmal selber nachgeschaut. Das lässt sich alleine deswegen ausschließen das das Programm normalerweise zickenterror macht, wenn es 2 Mal gestart wird.

Den teil mit dem Active hab ich nicht so ganz verstanden, aber ja, ich verwende Active um den Socket an und Abzuschalten (ist das vielleicht der Fehler?).
Ich programmiere mit Delphi erst seit 2 Wochen, deswegen nach möglichkeit Schlagwörter nennen, das ich Entsprechendes selber recherchieren kann und nicht 10 mal dumm und dämlich nachfragen muss.

Ich weis nicht inwiefern der Code weiterhilft, da letztendlich eine Drittsoftware
den Crash verursacht.

Es handelt sich bei dem meinem Programm um einen Netzwerk-Compiler für die Source-Engine (Spiele Engine) bei der ich lediglich eine Oberfläche erstelle (die Compiler sind Commandozeile) um das Compilen zu erleichtern und einige Bugs der Compiler zu umgehen.

Um es auf den Punkt zu bringen: Die Software sammelt vom User informationen startet dann, mit diesen Informationen, den Compiler welcher über DosCommand gestartet wird, und alle Ausgaben landen in einem Memo.

Ich denke mal die Entscheidende Stelle ist die hier

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:
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:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
procedure TForm1.bRunClick(Sender: TObject);
var
_ToolPath:    string;
_GamePath:    string;
step:         string;
i:            integer;
en:           string;

_mpi:         string;
_mpig:        string;
_mpiw:        string;
_mpis:        string;
_mpil:        string;
_mpilw:       string;
_mpin1:       string;
_mpils:       string;
_mpin2:       string;
_mpiport:     string;
begin
  // Command zusammenbauen


  //Nur wenn Games vorhanden
  if coGames.Items.Count >= 1 then
  begin

    //Erstmal speichern
      WriteCFG;

    // Engine wählen
    if rEngineEP1.Checked then
    begin
      _ToolPath := EP1_ToolPath;
    end;
    if rEngineEP2.Checked then
    begin
      _ToolPath := OB_ToolPath;
    end;

    // Ausgewähltes Game aus der Liste holen
    step  := GameList[coGames.ItemIndex];
    i     := Pos('#', step);
    Delete(step, 1, i);
    _GamePath := step;

    //Checkboxen interpretieren...
    if cMPISingle.Checked then
    begin
      _mpi    := '';
      _mpig   := '';
      _mpiw   := '';
      _mpis   := '';
      _mpil   := '';
      _mpilw  := '';
      _mpin1  := '';
      _mpils  := '';
      _mpin2  := '';
      _mpiport:= '';
    end
    else 
    begin

      _mpi := '-mpi ';

      if cMPIGraphics.Checked then
        _mpig := '-mpi_Graphics ' else _mpig := '';
      if cMPIWait.Checked then
        _mpiw := '-mpi_TimingWait ' else _mpiw := '';
      if cMPIStats.Checked then
        _mpis := '-mpi_ShowDistributeWorkStats ' else _mpis := '';
      if cLazyMaster.Checked then
        _mpil := '-mpi_NoMasterWorkerThreads ' else _mpil := '';
      if cMPIWorker.Checked then
      begin
        _mpilw :=    '-mpi_WorkerCount ';
        _mpin1 :=   fMaxWorkers.Text + ' ';
      end
      else
      begin
        _mpilw    := '';
        _mpin1  := '';
      end;
      if cSpeedLimit.Checked then
      begin
        _mpils :=    '-mpi_FileTransmitRate ';
        _mpin2 :=   fMaxSpeed.Text + ' ';
      end
      else
      begin
        _mpils    := '';
        _mpin2  := '';
      end;

      _mpiport:= '-mpi_Port ' + fMPIPort.Text;

    end;


    mCom.Clear;
    mLog.Lines.Add(TimeToStr(now) + ' : compiling started...');
    Cmd.CommandLine := _ToolPath + '\vbsp.exe -game "' + _GamePath + '" "' + fVMFPath.Text + '\' + liVMF.Items[liVMF.ItemIndex] + '"';
    Cmd.Execute;
    repeat
    Application.ProcessMessages;
    //Sleep(10);
    until Cmd.IsRunning=false;

    //Wenn MPI an, dann Workern sagen das es los geht.
    if NOT cMPISingle.Checked then
    begin
      //Welche Engine?
      if rEngineEP1.Checked then en := 'EP1';
      if rEngineEP2.Checked then en := 'EP2';

      i := 0;
      while i <> Server.Socket.ActiveConnections do
      begin
        Server.Socket.Connections[i].SendText('WORK#VIS#' + en +#13#10);
        Inc(i);
      end;
    end;



    Cmd.CommandLine := '"' + _ToolPath + '\vvis.exe" ' + _mpi + _mpig + _mpiw + _mpis + _mpil + _mpilw + _mpin1 + _mpils + _mpin2 + _mpiport + ' -game "' + _GamePath + '" "' + fVMFPath.Text + '\' + liVMF.Items[liVMF.ItemIndex] + '"';
    Cmd.Execute;
    repeat
    Application.ProcessMessages;
    //Sleep(10);
    until Cmd.IsRunning=false;

    //Wenn MPI an, dann Workern sagen das es los geht.
    if NOT cMPISingle.Checked then
    begin
      i := 0;
      while i <> Server.Socket.ActiveConnections do
      begin
        Server.Socket.Connections[i].SendText('WORK#RAD#' + en +#13#10);
        Inc(i);
      end;
    end;

    Cmd.CommandLine := '"' + _ToolPath + '\vrad.exe" ' + _mpi + _mpig + _mpiw + _mpis + _mpil + _mpilw + _mpin1 + _mpils + _mpin2 + _mpiport + ' -game "' + _GamePath + '" "' + fVMFPath.Text + '\' + liVMF.Items[liVMF.ItemIndex] + '"';
    Cmd.Execute;
    repeat
    Application.ProcessMessages;
    //Sleep(10);
    until Cmd.IsRunning=false;

  end;
end;

procedure TForm1.ServerClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
rc:       string;
Liste:    TStringList;

o, i , j:        integer;
step:     string;
begin
  Liste := TStringList.Create;
  try
  rc := Socket.ReceiveText;

  // Ankommende Daten in Liste schreiben.
  Liste.Text := rc;


  //Befehle interpretieren und abarbeiten.

  o := Liste.Count;
  while o >= 1 do  // Wird solange gemacht wie noch Befehle in der
  begin            // Queue sind...
    // --- WORKER ADD ---
   i := Pos('ADDME#',Liste[0]);
    if i <> 0 then
    begin
      Inc(id);
      step := Liste[0];
      Delete(step, 16);

      liWorkers.AddItem(step + 'Worker ID:' + IntToStr(id) , Socket);
      Socket.Sendtext('ADDED#' + #10);
    end;

   i := Pos('QUIT#',Liste[0]);
    if i <> 0 then
    begin
      step := Liste[0];
      i := 0;
      while i <> Server.Socket.ActiveConnections -1 do
      begin
        if Socket = Server.Socket.Connections[i] then
          j := i;
        
        Inc(i);
      end;

      liWorkers.Items.Delete(j);
      Socket.Close;



    end;

      Liste.Delete(0);
      Dec(o);
  end;



  finally
    Liste.Free;
  end;
end;


Ich weiss nicht genau welche Stellen relevant sind. Allerdings geht die Anwendung flöten wenn vvis.exe oder vrad.exe zicken machen. (Was bei den Dingern normal ist).


Zuletzt bearbeitet von Piranha am Do 07.08.08 00:28, insgesamt 1-mal bearbeitet
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 07.08.08 00:54 
Moin!

user profile iconPiranha hat folgendes geschrieben:
Die Anwendung läuft definitiv nicht mehr.
Wenn der Fall eingetreten ist, was sagt netstat -a (bitte mal die Ausgabe hier reinkopieren). Noch besser wäre fport.exe von sysinternals (mal danach googlen). Hast du XP? Dann bitte auch die Ausgabe von Tasklist.exe dazu tun (z.B. als Anhang). Der ProcessExplorer von denen ist auch ganz nützlich, um sowas auf die Spur zu kommen. :idea:

user profile iconPiranha hat folgendes geschrieben:
Das lässt sich alleine deswegen ausschließen
Der Socket ist nach deiner Aussage belegt, also muss es noch ein Handle darauf geben - das würde ich mal eher nicht ausschließen. ;)

user profile iconPiranha hat folgendes geschrieben:
Den teil mit dem Active hab ich nicht so ganz verstanden, aber ja, ich verwende Active um den Socket an und Abzuschalten (ist das vielleicht der Fehler?).
Nein, das ist kein Fehler, das ist (grundsätzlich erstmal) OK so. Die Frage ist: hast du die Eigenschaft .Active des TServerSockets in der IDE auf TRUE stehen?

user profile iconPiranha hat folgendes geschrieben:
Ich programmiere mit Delphi erst seit 2 Wochen, deswegen nach möglichkeit Schlagwörter nennen, das ich Entsprechendes selber recherchieren kann und nicht 10 mal dumm und dämlich nachfragen muss.
Hm, da du mit TServerSocket und einem selbstgebastelten Protokoll arbeitest: hast du dir das hier mal angesehen? Vereinfacht die Netzwerkkommunikation mit den Sockets deutlich... ;)

user profile iconPiranha hat folgendes geschrieben:
Ich weis nicht inwiefern der Code weiterhilft, da letztendlich eine Drittsoftware den Crash verursacht.
Tja, da wird es auf jeden Fall einen Zusammenhang geben. :?

user profile iconPiranha hat folgendes geschrieben:
Die Software sammelt vom User informationen startet dann, mit diesen Informationen, den Compiler welcher über DosCommand gestartet wird, und alle Ausgaben landen in einem Memo.
Das scheint mir noch ein möglicher Ansatz für weitere Forschung (AFAIK wird da das CreateProcess in einem Thread gekapselt, damit die GUI nicht blockiert - was auch gut so ist, sonst hast du fett Probleme mit den Socket-Kompos, weil die Ereignisse nicht zeitnah abgearbeitet werden - allerdings ist diese Warte-Geschichte in deinem Ansatz auch nicht soo toll über die APM-Schleife gelöst, naja). Wenn diese Kommandozeilenanwendung noch als Zombie an dem Thread hängt, kann das schonmal komische Effekte geben. Hier kann aber wohl nur ein WinAPI-Experte (wie z.B. user profile iconLuckie) mit qualifizierten Aussagen weiterhelfen; da hab ich auch nicht genug Erfahrung mit. :(

user profile iconPiranha hat folgendes geschrieben:
Ich denke mal die Entscheidende Stelle ist die hier
Da leider weitere Software in Spiel ist, nutzt der Coder nur wenig, so kann ich´s ja nicht nachstellen. :nixweiss:

cu
Narses

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



BeitragVerfasst: Do 07.08.08 01:33 
Okay ich denke ich hab es jetzt.

Unter bestimmten umständen wartet die Software auf antworten von den Workern bis ein Tastendruck entsteht (wartet auf Port 23311). Wenn nun ein neuer Compile gestartet wird, kann dieser nicht durchgeführt werden da er im Hintergrund immernoch auf selbigem Port wartet. Schließst man das Programm jetzt, muss wohl etwas dermaßen verkorksen, das selbst der Server von mir der auf Port 15250 horcht (vvis, bzw. vrad wie gesagt auf 23311) auch nicht mehr geschlossen wird.

Schieße ich nachträglich den Prozess ab (vvis/vrad), funktionieren wieder mein Socket und der Socket von der Drittsoftware :)

Das heisst, ich muss mich jetzt nurnoch informieren wie man effizient Prozesse abschießt.

Danke an alle :)

Wenn jemand trotzdem eine Lösung hat wie man Sockets mit einem Drittprogramm wieder "frei" machen kann, kann er das gerne posten.