Autor Beitrag
RBiniasch
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Mo 14.07.08 16:31 
Hallo,

für die interne Kommunikation einer verteilten Anwendung verwende ich die Indy-Komponenten. Der gesamte Nachrichtenaustausch läuft über einen kleinen Kommunikationsserver, an welchem sich alle Dienste anmelden und über den das Konfigurationsprogramm Zugriff auf diese Dienste erhält.

Damit das klappt fordert der Kommunikationsserver bei jedem Connect von dem Client erstmal ein LogOn an. In dieser Anforderung erhält der Client auch seine Session-ID.

So weit, so gut. Leider gibt es bei diesem Schritt immer wieder Aussetzer die sich derart äussern, dass der Indy-Server behauptet, er habe die Anforderung zum Client übertragen, dort aber nichts ankommt.

Die Funktionen sehen wie folgt aus.

OnConnect des Servers.
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:
procedure TtoIFCommServer.OnServerConnect(toThread: TIdPeerThread);
var
  recRequest     : TrecMsgHeader;
  recReply       : TrecMsgLogOn;

  pNewConnection : PrecIFConnection;

  cardMinWait    : Cardinal;

  siCounter      : SmallInt;
begin
  LogEvent(Format(g_astrIFComBase[4], [toThread.Connection.LocalName]),
            IF_CONST_LOGCONNECT,
            etInfo,
           IF_CONST_LOGLEVEL_MIN);

  GetMem(pNewConnection, SizeOf(TrecIFConnection));

  pNewConnection.strDNS       := toThread.Connection.LocalName;
  pNewConnection.dtConnStart  := Now;
  pNewConnection.dtLastAction := pNewConnection.dtConnStart;
  pNewConnection.pConnection  := toThread;

  cardMinWait                 := GetTickCount + IF_CONST_WAITAFTERCONNECT;

  repeat
    pNewConnection.iSessionID  := Random(IF_CONST_SESSIONIDRANGE);
  until F_tolConThreads.SessionIDIsUnique(pNewConnection.iSessionID);

  toThread.Data                := TObject(pNewConnection);

  recRequest                  := F_toComBase.CreateMsgHeader(IF_CONST_TCPIPHOST,
                                                             EmptyStr,
                                                             EmptyStr,
                                                             svctCommSvr,
                                                             mtRequestLogOn,
                                                             pNewConnection.iSessionID);
  ZeroMemory(@recReply, SizeOf(recReply));

  while GetTickCount < cardMinWait do
    g_procProcessMessages;

  if F_toComBase.ExchangeMsgs(toThread.Connection,
                              @recRequest,
                              @recReply,
                              SizeOf(recRequest),
                              SizeOf(recReply)) = frOK then
  begin
    pNewConnection.strName        := recReply.recHeader.strClientname;
    pNewConnection.strTargetname  := recReply.recHeader.strTargetname;
    pNewConnection.secClientType  := recReply.recHeader.secClientType;
    pNewConnection.bLoggedOn      := TRUE;
  end
  else
  begin
    pNewConnection.strName        := g_astrIFComBase[31];
    pNewConnection.strTargetname  := g_astrIFComBase[31];
    pNewConnection.secClientType  := svctUnknown;
    pNewConnection.bLoggedOn      := FALSE;
  end;
end;


ExchangeMsgs aus dem OnConnect.
ausblenden 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:
function TtoIFComBase.ExchangeMsgs(toConnection: TIdTCPConnection; pSend, pReply: Pointer; iSendLength, iReplyLength: Integer; bExpectMsgHeader: Boolean): TsecFuncResult;
begin
  Result  := frError;

  if Assigned(toConnection) then
  begin
    if Assigned(pSend) then
    begin
      if Assigned(pReply) then
      begin
        try
          if SendData(toConnection, pSend, iSendLength)then
          begin
            Sleep(IF_CONST_MINWAITFORREPLY);
            Result  := ReadData(toConnection, pReply, iReplyLength, bExpectMsgHeader);
          end;
        except
          Result  := frError;
        end;
      end;
    end;
  end;
end;


Mit der Funktion SendData beginnt das Problem.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
function TtoIFComBase.SendData(toConnection: TIdTCPConnection; pData: Pointer; cardDataLength: Cardinal): Boolean;
begin
  Result  := TRUE;
  try
    if (Assigned(toConnection)) and
       (toConnection.Connected) then
      toConnection.WriteBuffer(pData^, cardDataLength, TRUE)
    else
      Result  := FALSE;
  except
    Result  := FALSE;
  end;
end;


Im WriteBuffer wird keine Exception ausgelöst. Die Funktion läuft einfach durch und alles scheint gut ...

Auf Client-Seite sieht das OnExecute so aus :
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
procedure TService_IFSendMsg.ContactExecute(AThread: TIdPeerThread);
var
  recMsgHeader  : TrecMsgHeader;
begin
  ShowMessage('Execute');  //Debug - bloß nicht vergessen ...

  if AThread.Connection.Socket.Binding <> nil then
  begin
    F_toIFCom.ReadData(AThread.Connection,
                       @recMsgHeader,
                       SizeOf(TrecMsgHeader));

    LogEvent(F_toIFCom.GetMsgInfo(recMsgHeader),
             IF_CONST_LOGEXECUTE,
             etInfo,
             IF_CONST_LOGLEVEL_MED);
.
.
.


Grundsätzlich scheint das zu funktionieren. Ich habe schon ein LogOn bekommen. Aber in über 90% der Fälle kommt leider rein gar nichts an. Im Log ist nichts zu sehen und auch das ShowMessage springt nicht an (rein prophylaktisch, ja, der Dienst darf mit dem Desktop interagieren und er tut es auch. Ich habe ihm aus anderer Quelle Nachrichten geschickt und die Messagebox war zu sehen).

Falls irgendwer eine Idee hat, wo das Problem liegen könnte wäre das toll.

Schon mal danke fürs Lesen und bis denn,
Ralf Biniasch
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 14.07.08 16:37 
Moin und :welcome: im Forum!

Ich hätte da noch ein paar Rückfragen:
user profile iconRBiniasch hat folgendes geschrieben:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
procedure TtoIFCommServer.OnServerConnect(toThread: TIdPeerThread);
//...
  while GetTickCount < cardMinWait do
    g_procProcessMessages;
Was hat dieser Code für einen Sinn und was tut g_procProcessMessages?

user profile iconRBiniasch hat folgendes geschrieben:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure TService_IFSendMsg.ContactExecute(AThread: TIdPeerThread);
//...
    LogEvent(F_toIFCom.GetMsgInfo(recMsgHeader),
             IF_CONST_LOGEXECUTE,
             etInfo,
             IF_CONST_LOGLEVEL_MED);
Was passiert hier genau (thread-save und keine VCL-Zugriffe?) :nixweiss:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
RBiniasch Threadstarter
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Mo 14.07.08 16:58 
Hallo und danke für die rasche Reaktion.

Die Prozedur g_procProcessMessages sorgt nur dafür, dass während der Wartezeit weiterhin Windowsmessages abgearbeitet werden.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure g_procProcessMessages;
var
  msgActWinMessage  : TMsg;
begin
  while PeekMessage(msgActWinMessage, 000, PM_REMOVE) do
  begin
    TranslateMessage(msgActWinMessage);
    DispatchMessage(msgActWinMessage);
  end;
end;


LogEvent schreibt das Ereignislog in eine Datenbank.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure TService_IFSendMsg.LogEvent(strMessage, strKey: String; secEventType: TsecEventTypes; byLogLevel: Byte);
var
  csLock  : TCriticalSection;
begin
  csLock  := TCriticalSection.Create;
  try
    csLock.Acquire;

    F_toSvcBase.LogEvent(strMessage,
                         strKey,
                         secEventType,
                         byLogLevel);
  finally
    csLock.Release;
    FreeAndNil(csLock);
  end;
end;


Das dürfte beides keinen Einfluss auf die TCP/IP-Kommunikation haben. Insbesondere da die Verarbeitung das LogEvent beim Client nicht erreicht.

Bis denn,
Ralf Biniasch
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 14.07.08 19:29 
Moin!

user profile iconRBiniasch hat folgendes geschrieben:
Die Prozedur g_procProcessMessages sorgt nur dafür, dass während der Wartezeit weiterhin Windowsmessages abgearbeitet werden.
Aber das ist doch evtl. nicht im GUI-Thread der Anwendung, oder? :? Du scheinst nicht grad erst mit Programmieren und TCP/IP angefangen zu haben, deshalb würde es mich wundern, wenn du nicht wüsstest, dass die VCL nicht threadsave ist und deshalb sowas potentiell ein Problem darstellt. :nixweiss: Schonmal diesen Teil einfach entfernt? Indy arbeitet doch mit blocking-socket-calls und threads, da sollte es wegen nicht vorhandener Daten doch eigentlich keine Timingprobleme geben... :gruebel:

user profile iconRBiniasch hat folgendes geschrieben:
Das dürfte beides keinen Einfluss auf die TCP/IP-Kommunikation haben. Insbesondere da die Verarbeitung das LogEvent beim Client nicht erreicht.
Ja, das sehe ich auch so. ;) War nur für die Sicherheit.

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
RBiniasch Threadstarter
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Di 15.07.08 09:57 
Hallo nochmal,

ja, du hast natürlich völlig recht :oops: .
Da hat der Autopilot zugeschlagen. Die Funktion kloppe ich standardmäßig in den Code wenn ich vermute, dass es Timingprobleme geben könnte ...

Gestern Abend kam mir dann auch der Gedanke, dass das Probleme bereiten könnte und ich habe die Warteschleife heraus genommen. Das macht aber leider keinen Unterschied. Nach wie vor kommt die Nachricht nicht beim Client an.

Mit welchem Programm schaue ich denn am besten, ob überhaupt eine Nachricht rausgeht? Ich habe den Wireshark probiert, weiß aber nicht wie ich den korrekt konfiguriere.

Bis denn,
Ralf Biniasch
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 15.07.08 10:19 
Moin!

user profile iconRBiniasch hat folgendes geschrieben:
ich habe die Warteschleife heraus genommen. Das macht aber leider keinen Unterschied. Nach wie vor kommt die Nachricht nicht beim Client an.
Schade. :?

user profile iconRBiniasch hat folgendes geschrieben:
Mit welchem Programm schaue ich denn am besten, ob überhaupt eine Nachricht rausgeht? Ich habe den Wireshark probiert,
Das hätte ich dann jetzt auch vorgeschlagen. :)

user profile iconRBiniasch hat folgendes geschrieben:
weiß aber nicht wie ich den korrekt konfiguriere.
Puh, sowas macht hier immer mein Kollege, ich schaue mir dann nur idR die Logs an... :? sorry, kann ich dir nicht bei helfen... :nixweiss: jaja, ich sollte mich langsam mal mit dem Kabelhai auseinandersetzen... *seufz* sagt mein Kollege auch immer... *g* Wenn ich das mal selbst machen musste, habe ich das mit dem cmd-Tool WinDump gemacht (basiert auch auf WinPCap).

Du könntest aber einfach mal in ein Logfile auf dem Serverdateisystem festhalten, an welchen Codestellen er so beim Connect vorbeirauscht, dann kann man zumindest da mal den Verlauf nachvollziehen. Leider ist der Debugger IMHO bei timingsensitiven Problemen bei der Netzwerkkommunikation nur sehr beschränkt brauchbar, deshalb verfolge ich sowas normalerweise lieber mit Logs. :idea:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
RBiniasch Threadstarter
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Di 15.07.08 10:37 
Hi,

ich habe auch einen Kollegen zu Hilfe gerufen, der den ollen Hai ein wenig besser kennt :) .
Sowas ist immer ein guter Plan ...

Falls das neue Erkenntnisse bringt sage ich nochmal bescheid.

Bis denn,
Ralf Biniasch

---Moderiert von user profile iconNarses: Beiträge zusammengefasst---

Hallo nochmal,

so, nun ist es offiziell. Die LogOn-Anforderung geht raus, kommt auch beim Client an (zumindest sendet er ein ACK), löst aber nicht das Execute-Ereignis aus.

Mit den Innereien der Indys habe ich mich noch so gar nicht auseinander gesetzt. Und der Debugger weigert sich leider dort hinein zu schauen.

Mal schauen was ich da finde. Wie immer ist jeder Tipp willkommen ... :D .

Bis denn,
Ralf Biniasch

---Moderiert von user profile iconNarses: Beiträge zusammengefasst---

Hallo,

ok, wie immer saß das Problem 60cm vor dem Monitor :oops: .

Ich hatte die Kommunikationsstruktur umgestellt und dabei die Zuweisung der OnExecute-Funktion verhunzt. Die war gar nicht mehr in den ganzen Ablauf eingebunden und konnte dementsprechend auch nicht anspringen ...

Danke für die guten Ideen und sorry für die verschwendete Zeit,
Ralf Biniasch