简体   繁体   中英

Can TCP server start communication with client

I have async tcp server that can accept multiple clients at the same time. The scenarios where client requests data from server are served well. Now I am trying to implement the situation in which server has to find a particular client and send some data to it ie the client is connected but has not requested data but server wants to send some data to it. How can I find the thread that is already running between the server and client and place data on it?

Here is server code:

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)

You need to keep track of your clients. For example:

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)

TCP is bi-directional, so either side can send data whenever they want.

What you need to do is store a reference to the TcpClient on Accept, something like:

//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);
}

Normally I will raise an event on connect so whoever is using the class can get the new client's index. The send message code is pseudo-code, there are good examples on MSDN of how to actually perform the send.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM