【问题标题】:Can TCP server start communication with clientTCP服务器可以开始与客户端通信吗
【发布时间】:2014-08-28 05:18:41
【问题描述】:

我有可以同时接受多个客户端的异步 tcp 服务器。客户端从服务器请求数据的场景得到了很好的服务。现在我正在尝试实现服务器必须找到特定客户端并向其发送一些数据的情况,即客户端已连接但尚未请求数据但服务器想要向其发送一些数据。如何找到已经在服务器和客户端之间运行的线程并在上面放置数据?

这是服务器代码:

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)

【问题讨论】:

  • 我不认为这种方法我的意思是tcp客户端工作正常,我建议你使用异步方法,使用这种方法你可以处理一个线程的每个连接,使用这种方法你可以发送你的ip将您的客户端作为 id 发送到服务器,并根据需要向其发送信息

标签: c# async-await tcpclient tcplistener


【解决方案1】:

TCP 是双向的,因此任何一方都可以随时发送数据。

您需要做的是在接受时存储对 TcpClient 的引用,例如:

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

通常我会在连接时引发一个事件,以便使用该类的任何人都可以获得新客户的索引。发送消息代码是伪代码,MSDN上有很好的例子说明如何实际执行发送。

【讨论】:

    【解决方案2】:

    您需要跟踪您的客户。例如:

    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)
    

    【讨论】:

    • 这绝对是正确的方法,基本上是我的回答所说的完整版本。但是,该类的用户缺少一种获取创建的 GUID 的方法。鉴于完整性,这似乎是一个重要的疏忽。添加它,你会得到我的 +1!
    • 感谢您的解决方案。不过我有两个问题: 1. 如果我想将连接的客户端信息保存在数据库中,那么如何保存 tcpClient 信息? 2. 如何让clientId使用SendAsync方法?
    • 1 - 你想保存什么? IP地址?您无法从存储的数据中恢复这些连接。 2 - 这取决于您的应用程序及其用例。
    猜你喜欢
    • 2017-07-16
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-19
    • 1970-01-01
    • 2019-03-07
    • 2013-12-14
    相关资源
    最近更新 更多