Bonjour,

Je ne maîtrise pas, loin s'en faut Indy. Je souhaite utiliser un serveur TCP pour une communication entre plusieurs process, 2 process clients, 1 process serveur, sur une même machine.

J'ai donc un serveur (TIdTcpServer) et 2 clients (TIdTcpClient).

L'idée est de créer un process serveur qui écoute sur 3 ports.
Le port 1 est utilisé pour la connexion initiale quand un client se connecte, port sur lequel le serveur lui renvoie un n° de port disponible.
Alors le client se déconnecte du port 1 et se reconnecte au port libre fourni précédemment.
Le but est aussi que le serveur puisse réagir quand un client se déconnecte, donc la connexion est permanente sur les ports 2 et 3 et du code est exécuté sur le serveur dans le "OnDisconnect" du serveur.

Pour schématiser on a SRV, CLT1, CLT2. On ne peut pas prédire si CLT1 démarre avant CLT2 ou inversement, et on veut pouvoir prévoir d'autre process clients si nécessaire à l'avenir.
SRV écoute sur les ports 1, 2 , 3
CLT1 démarre, se connecte au port 1, SRV lui renvoie le port libre 2, CLT1 se déconnecte du port 1 et se reconnecte au port 2 de façon permanente.
CLT2 démarre, se connecte au port 1, SRV lui renvoie le port libre 3, CLT2 se déconnecte du port 1 et se reconnecte au port 3 de façon permanente.

Est-ce une bonne approche ? Ou existe-t-il déjà un mécanisme similaire dans Indy ?

J'ai commencé à expérimenté. La communication entre SRV et CLT1 est ok.
En revanche, bien que le port 1 soit déconnecté, la connexion du client suivant CLT2 sur le port 1 semble poser problème.
La connexion n'est pas refusée, mais elle semble échouer après le timeout fixé. Que ce timeout soit de 500ms ou de 10 secondes ne change rien.

Code de connexion d'un client :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
 
function TMes.Conn() : Boolean;
const sname = 'TMes.Conn';
var NewPort : Word;
     SocketResponse : TSocketMsg;
Begin
        result := false;
 
        clt.Connect; // Connexion au port 1
        if IsCltConnected(Clt) then begin
 
          clt.IOHandler.WriteLn(SocketMsg(tcpGetPort, '', true).Str); // Demande de port libre
          SocketResponse := SocketMsg(clt.IOHandler.ReadLn);      // Lecture du port fourni
 
          if clt.IOHandler.ReadLnTimedout then begin
            trace(logOnDebug, sname,'TIMEOUT CAS 1'); // Timeout : C'est un des deux traces que je trouve dans mon log quand CLT2 tente de se connecter...
          end;
 
          if SocketResponse.ok then begin
            NewPort := StrToInt(SocketResponse.What);
            clt.IOHandler.InputBuffer.clear;
            clt.Disconnect; // Déconnexion port 1
 
            clt.Port := NewPort;
            clt.Connect; // Reconnexion au port fourni
 
            if IsCltConnected(Clt) then begin
              Clt.IOHandler.WriteLn(SocketMsg(tcpGetProcess, '', true).Str);
              try
                SocketResponse := SocketMsg(clt.IOHandler.ReadLn);
 
                if clt.IOHandler.ReadLnTimedout then begin
                  trace(logOnDebug, sname,'TIMEOUT CAS 2'); // ..Ou c'est parfois celle-ci
                end;
 
                if SocketResponse.ok then begin                    
                    result := true;
                end
                else begin
                    clt.IOHandler.InputBuffer.clear; 
                    clt.Disconnect;  
                end;
             except
                on e: Exception do begin
                  clt.IOHandler.InputBuffer.clear; // Sinon pas de déconnexion !
                  clt.Disconnect(false);
                end;
              end;
            end
            else begin
              trace(logOnDebug, sname,'Connexion impossible');
            end;
          end
          else begin
            trace(logOnDebug, sname,'Réponse invalide, déconnexion');
            clt.IOHandler.InputBuffer.clear; // Sinon pas de déconnexion !
            clt.Disconnect;
          end;
        end;
end;
 
function TMes.IsCltConnected (var Clt : TIdTcpClient) : boolean;
const sname = 'TMes.IsCltConnected';
begin
    result := false;
    try
      result := clt.Connected;
    except
      on e : exception do begin
        try
          // Dans ce cas on passe False => on ne notifie pas la peer de la déconnexion puisque la peer est morte.
          clt.Disconnect(False);
        except
          on e : exception do begin
            trace(logOnRelease, sname,'==========================> EXCEPTION : '+ e.Message);
          end;
        end;
        if assigned(clt.IOHandler) then clt.IOHandler.InputBuffer.Clear;
      end;
    end;
end;
Code côté serveur:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
 
procedure TMes.TcpSrvExecute(AContext: TIdContext);
const sname = 'TMes.TcpSrvExecute';
Var i : integer;
    Msg, Response : TSocketMsg;
begin
 
 
    Msg := SocketMsg(AContext.Connection.IOHandler.ReadLn);
 
    // Initialisation réponse : Qu'on enverra que si Msg.Wait
    Response := SocketMsg(tcpKo, '', false);
 
    trace(logOnRelease, sname,'Réception de ' + Msg.Str + ' Who=' + Msg.Who + ' Why=' + Msg.Why + ' What='+Msg.What + ' Wait='+StrLog(Msg.Wait));
 
 
    // Demande de port
    if Msg.Why = tcpGetPort then begin
      trace(logOnDebug, sname,'Demande de port');
      for i := 2 to tcpNbPort do begin // Port 1 = broker
        if not PortInUse[i].Used then begin
          PortInUse[i].Used := true;
          Response := SocketMsg(tcpOk, IntToStr(PortInUse[i].Port), false);
          break;
        end
        else begin
          trace(logOnDebug, sname,'Port ' + IntToStr(PortInUse[i].Port) + ' occupé');
        end;
      end;
    end
 
    // Demande de nom de process pour contrôle
    else if Msg.Why = tcpGetProcess then begin
      trace(logOnDebug, sname,'Demande de Process');
      Response := SocketMsg(tcpOk,GetWinProc(WHO_AM_I), false);
    end
 
    else begin
       trace(logOnDebug, sname,'TcpSrvExecute - "Why" inconnu ' + Msg.Why + ' What=' + Msg.What);
    end;
 
    // Renvoi ack et réponse
    if Msg.Wait then begin
      trace(logOnDebug, sname,'Renvoi de ' + Response.Str +
                           ' Who=' + Response.Who +
                           ' Why=' + Response.Why +
                           ' What='+ Response.What +
                           ' Wait=' + StrLog(Response.Wait));
      AContext.Connection.IOHandler.WriteLn(Response.Str);
    end;
end;

(Remarque : TMes = class(TThread))

Y-a-t-il un problème de déconnexion ? Ou est-ce un problème lié au fait que le CLT1 restant connecté au port 2, le SRV ne peut pas prendre en compte les demandes du CLT 2 ?

Merci pour vos retours.