2014-07-07 1 views
2

J'ai un serveur async tcp qui peut accepter plusieurs clients en même temps. Les scénarios dans lesquels le client demande des données au serveur sont bien servis. Maintenant, j'essaie d'implémenter la situation dans laquelle le serveur doit trouver un client particulier et lui envoyer des données, c'est-à-dire que le client est connecté mais n'a pas demandé de données mais le serveur veut lui envoyer des données. Comment puis-je trouver le thread qui est déjà en cours d'exécution entre le serveur et le client et y placer des données?Le serveur TCP peut-il démarrer la communication avec le client

Voici le code du serveur:

public async void RunServerAsync() 
{ 
    tcpListener.Start();   

     while (true) 
     { 
      try 
      { 
       var client = await tcpListener.AcceptTcpClientAsync();   
       Accept(client); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message);      
      }     
     }       
} 

private async void Accept(TcpClient client) 
{ 
    //get client information 
    String clientEndPoint = client.Client.RemoteEndPoint.ToString();    
    Console.WriteLine("Client connected at " + clientEndPoint); 

    await Task.Yield(); 

    try 
    {    
     using (client) 
     using (NetworkStream stream = client.GetStream()) 
     { 
      byte[] dataReceived = new byte[100];     
      while (true) //read input stream      
      {      
        try 
       { 
        int x = await stream.ReadAsync(dataReceived, 0, dataReceived.Length); 

        if (x != 0) 
        {        
         //pass on data for processing  

         byte[] dataToSend = await ProcessData(dataReceived); 

         //send response,if any, to the client 
         if (dataToSend != null) 
         { 
          await stream.WriteAsync(dataToSend, 0, dataToSend.Length); 
          ConsoleMessages.DisplayDataSent(dataReceived, dataToSend); 
         } 
        } 
       } 
       catch (ObjectDisposedException) 
       { 
        stream.Close(); 
       } 
      }     
     } 
    } //end try   
    catch (Exception ex) 
    { 
     Console.WriteLine(ex.Message); 

    } 
}//end Accept(TcpClient client) 
+0

Je ne pense pas que cette méthode, je veux dire le client tcp fonctionne très bien, je vous suggère d'utiliser la méthode async, avec cette méthode, vous pouvez gérer chaque connexion bu un fil, avec cette méthode, vous pouvez envoyer votre IP de votre client au serveur comme ID, et envoyer des informations si vous voulez –

Répondre

2

Vous devez garder une trace de vos clients. Par exemple:

ConcurrentDictionary<Guid, TcpClient> _clients = new ConcurrentDictionary<Guid, TcpClient>(); 

public async void RunServerAsync() 
{ 
    tcpListener.Start(); 

    while (true) 
    { 
     try 
     { 
      var client = await tcpListener.AcceptTcpClientAsync(); 
      var clientId = Guid.NewGuid(); 
      Accept(clientId, client); 
      _clients.TryAdd(clientId, client); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
     } 
    } 
} 

public Task SendAsync(Guid clientId, Byte[] buffer, Int32 offset, Int32 count) 
{ 
    TcpClient client; 
    if (_clients.TryGetValue(clientId, out client)) 
     return client.GetStream().WriteAsync(buffer, offset, count); 

    // client disconnected, throw exception or just ignore 
    return Task.FromResult<Object>(null); 
} 

public Boolean TryGetClient(Guid clientId, out TcpClient client) 
{ 
    return _clients.TryGetValue(clientId, out client); 
} 

private async void Accept(Guid clientId, TcpClient client) 
{ 
    //get client information 
    String clientEndPoint = client.Client.RemoteEndPoint.ToString(); 
    Console.WriteLine("Client connected at " + clientEndPoint); 

    await Task.Yield(); 

    try 
    { 
     using (client) 
     using (NetworkStream stream = client.GetStream()) 
     { 
      byte[] dataReceived = new byte[100]; 
      while (true) //read input stream      
      { 
       try 
       { 
        int x = await stream.ReadAsync(dataReceived, 0, dataReceived.Length); 

        if (x != 0) 
        { 
         //pass on data for processing  

         byte[] dataToSend = await ProcessData(dataReceived); 

         //send response,if any, to the client 
         if (dataToSend != null) 
         { 
          await stream.WriteAsync(dataToSend, 0, dataToSend.Length); 
          ConsoleMessages.DisplayDataSent(dataReceived, dataToSend); 
         } 
        } 
       } 
       catch (ObjectDisposedException) 
       { 
        stream.Close(); 
       } 
      } 
     } 
    } //end try   
    catch (Exception ex) 
    { 
     Console.WriteLine(ex.Message); 

    } 
    finally 
    { 
     // deregister client 
     _clients.TryRemove(clientId, out client); 
    } 
}//end Accept(TcpClient client) 
+1

C'est certainement la bonne approche, essentiellement une version complète de ce que ma réponse parlait. Il manque un moyen pour les utilisateurs de la classe d'obtenir le GUID créé. Compte tenu de l'exhaustivité, semble être un oubli important. Ajoutez-le et vous obtiendrez mon +1! – BradleyDotNET

+0

Merci pour la solution. J'ai cependant deux questions: 1. Si je voulais enregistrer les informations des clients connectés dans la base de données, comment puis-je sauvegarder les informations de tcpClient? 2. Comment puis-je obtenir clientId pour utiliser la méthode SendAsync? – AM0

+0

1 - que voulez-vous économiser? Adresse IP? Vous ne pouvez pas restaurer ces connexions à partir de données stockées. 2 - cela dépend de votre application et de ses cas d'utilisation. – vtortola

1

TCP est bidirectionnel, de sorte que chaque côté peut envoyer des données à chaque fois qu'ils veulent.

Ce que vous devez faire est de stocker une référence à la TcpClient sur Accepter, quelque chose comme:

//Class Level 
List<TcpClient> connectedClients = List<TcpClient>(); 

private async void Accept(TcpClient client) 
{ 
    connectedClients.Add(client); 
    ... 
} 

public SendMessage(int index, String message) 
{ 
    //pseudo-code 
    connectedClients[index].Send(message); 
} 

Normalement, je susciterai un événement à la connexion si celui qui utilise la classe peut obtenir l'index du nouveau client. Le code de message d'envoi est pseudo-code, il existe de bons exemples sur MSDN de la façon d'effectuer réellement l'envoi.

Questions connexes