2017-04-21 1 views
0

Je souhaite utiliser plusieurs liaisons sur un composant socket TIdTCPServer pour envoyer des données à plusieurs clients différents. Je peux créer avec succès les liaisons et attribuer un numéro de port différent à chacune. Voici mon code pour créer les liaisons:Comment utiliser le composant Indy pour envoyer à partir d'un socket IDTCPServer avec plusieurs liaisons

IdTCPServer1.Active := False; 
IdTCPServer1.Bindings.Clear; 
{create the new sockets} 
for i := 0 to NumberOfItemsSpin.Value-1 do 
begin 
    IdTCPServer1.Bindings.Add; 
    IdTCPServer1.Bindings.Items[i].Port := StartingPortSpin.Value+i; 
end; 
SocketsCreatedCount := IdTCPServer1.Bindings.Count; 
Timer1.Enabled  := SocketsCreatedCount > 0; 
IdTCPServer1.Active := True; 

Les clients connaissent l'adresse IP et le port pour se connecter à et sont en mesure d'établir une connexion réussie. Cependant, je dois asynchroneenvoyer des données à l'un des clients connectés en fonction de certains événements.

Je sais que je peux détecter un événement OnExecute, obtenir le Context et répondre, mais mon socket serveur doit fonctionner de manière asynchrone. Il ne sera pas interrogé par le client.

Je connais l'adresse IP du client et le numéro de port de l'information de connexion. Cependant, je ne peux pas trouver un moyen d'envoyer des données à un client spécifique. Comment puis-je utiliser le composant pour mapper correctement la liaison et envoyer des données au bon client connecté?

Répondre

3

Vous semblez avoir un peu de malentendu sur la façon dont fonctionne réellement TIdTCPServer. La collection Bindings n'a rien à voir avec les clients individuels. Les éléments Binding sont simplement les paires IP/Port que le serveur écoute pour les connexions. Il n'y a pas de correspondance entre un Binding et un client. Lorsqu'un client se connecte, un objet TIdContext est créé pour lui et stocké dans la liste Contexts du serveur, puis les événements OnConnect/OnDisconnect/OnExecute sont déclenchés pour ce TIdContext, indépendamment des autres clients. TIdTCPserver est multi-thread, chaque client est exécuté dans son propre thread dédié, et les événements pour un client donné sont déclenchés dans le thread de ce client, de sorte que les événements pour plusieurs clients peuvent être exécutés en parallèle. L'événement OnExecute ne dépend PAS d'un client interrogeant le serveur. L'événement est simplement appelé dans une boucle continue pour la durée de vie de la connexion du client. Le gestionnaire d'événements décide quoi faire avec le client à chaque itération de boucle. Cependant, vous devez assigner un gestionnaire, sinon une exception sera déclenchée lorsque le serveur est activé (pour éviter cela, vous pouvez dériver une nouvelle classe de TIdTCPServer et remplacer sa méthode CheckOkToBeActive() pour ne pas déclencher une exception).

Pour envoyer des données à un client en particulier, il suffit de localiser TIdContext objet dans Contexts liste du serveur de ce client, et vous aurez accès à son objet TIdTCPConnection associé pour échanger des données avec ce client.

Cependant, effectuer l'envoi à l'intérieur de la même boucle qui recherche la liste Contexts est inheritantly dangereux, je vous conseille donc à la place que vous créez une file d'attente de thread-safe par client (par exemple une instance TIdThreadSafe(String|Object)List) pour tenir votre sortant données, puis lorsque vous localisez le client, vous pouvez placer les données dans la file d'attente associée de ce client. Ensuite, vous pouvez demander au gestionnaire d'événements OnExecute d'envoyer le contenu de la file d'attente de son client chaque fois qu'il n'est pas vide. De cette façon, vous évitez que les clients ne se bloquent les uns les autres, la gestion des erreurs est localisée sur chaque client et vous pouvez envoyer des données à plusieurs clients en parallèle.

Pour associer une file d'attente avec chaque client, vous pouvez:

  • utiliser la propriété du TIdContext.Data public.

  • dérivez une nouvelle classe de TIdServerContext, ajoutez vos propres champs personnalisés selon vos besoins, puis assignez cette classe à la propriété ContextClass du serveur avant d'activer le serveur. Vous pouvez ensuite dactylographier n'importe quel pointeur d'objet TIdContext pour accéder à vos champs personnalisés.

Pour un exemple de code, voir this answer j'ai posté à une question précédente:

How do I send a command to a single client instead of all of them?