异步发送到多个客户端并正确使用FromAsync?

时间:2012-02-29 13:41:37

标签: c# asynchronous

我在服务器上有500个连接的客户端。服务器每隔N秒向所有客户端发送数据。

当服务器向客户端发送数据时,结果为:

-----------Result-----------
Client1 received : 102 datas
Client2 received : 109 datas
Client3 received : 105 datas
Client4 received : 108 datas
Client5 received : 108 datas
ClientN received : 107 datas

但最后应该是110个数据。

我使用以下代码将数据发送到客户端

public static class AsyncSend
{
    public static Task<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
    {
        return new Task<int>(() =>
        {
            return Task.Factory.FromAsync<int>(
                           socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
                     socket.EndSend).Result;
        });
    }
}
class ServerTest
{
  private Thread sendThread;
  private Dictionary<String, Client> connectedClients;

  public ServerTest()
  {
    connectedClients = new Dictionary<String, Client>();
  }

  public void receive()
  {
    //here server receives data from another server
    //When server receives new data then fires the newDataReceived(String data) method.

    newDataReceived(data);
  }

  public void newDataReceived(String data)
  {
    sendThread = new Thread(new ParameterizedThreadStart(sendToClients));
    sendThread.Start(data);
  }

  private void sendToClients(object dataObj)
  {
    try
    {
      int totalConnectedClients = connectedClients.Count;

      if (totalConnectedClients > 0)
      {
        lock (connectedClients)
        {
          byte[] buffer = Encoding.UTF8.GetBytes((string)dataObj);
          byte[] cBuffer = new SCompressor().Compress(buffer);

          foreach (Client client in connectedClients.Values)
          {
            Task<int> task = AsyncSend.SendAsync(client.Socket, cBuffer, 0, cBuffer.Length);
            task.Start();
          }
        }
      }
    }
    catch (Exception ex)
    {
      //ERROR.
    }
  }

是否有其他方式或如何正确有效地向多个客户端发送数据?

如果可以,请告诉我示例。

感谢您的建议。

4 个答案:

答案 0 :(得分:3)

我有一些要点:

您可以通过删除每次发送的多余任务来提高效率:

public static Task<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
{
        return Task.Factory.FromAsync<int>(
                       socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
                 socket.EndSend);
//Don't explicitly start the return value of this method. The task will already be started.
}

就我所知,异步发送的使用是正确的(除了不需要的任务)。

您使用的是UDP吗?如果是,那将解释为什么缺少某些数据。

是否有错误被捕获?如果是,则循环将中止,并非所有客户端都将获取其数据。

你有竞争条件:

int totalConnectedClients = connectedClients.Count;

这应该发生在锁内。您无法安全地读取Count,而其他线程可能会更新此词典。

您是如何收到数据的?也许这个bug在接收端。请发一些代码。

编辑:

如果两个并发接收正在运行怎么办?这会导致错误:客户端可以先接收最新数据,然后接收旧数据。我建议您每次收到数据时都不要启动新线程。相反,让一个线程永久运行,它使用GetConsumerEnumerable方法从BlockingCollection中提取新数据。你的newDataReceived方法只会入列到这个BlockingCollection中。你可以在这里找到一个很好的介绍:http://blogs.msdn.com/b/csharpfaq/archive/2010/08/12/blocking-collection-and-the-producer-consumer-problem.aspx

答案 1 :(得分:1)

我完全不知道收到的“102数据”是什么意思,或者你实际问的是什么,但你的SendAsync()方法可以大大改进:

public static Task<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
{
    return Task.Factory.FromAsync<int>(
        socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
        socket.EndSend);
}

这样,您就不会阻止任何线程,因此您应该获得更高的效率。

修改后的代码与您的代码之间的一个区别是Task已经启动,因此您不应该在其上调用Start()

答案 2 :(得分:0)

SCompressor类是否为它返回的数据添加长度前缀或唯一结束标记?

没有它,客户端代码将无法知道在解压缩之前要接收多少字节。

这是我能看到的唯一可能会影响客户收到的数据量的潜在错误,假设客户端结果诊断显示的是什么。

如果是这样很容易修复 - 只需将压缩数据的长度添加到服务器发送的缓冲区的开头:

        var sendBuffer = new byte[cBuffer.Length + 4];
        BitConverter.GetBytes(cBuffer.Length).CopyTo(sendBuffer, 0);
        cBuffer.CopyTo(sendBuffer, 4);

然后将客户端更改为读取4个字节,将长度解码为int,继续读取直到 length 字节被接收,解压缩。

答案 3 :(得分:0)

此线程尚未提出的一点是TCP是一种流协议。这意味着如果服务器发送110个字节,则客户端可以在单个读取调用中接收1-110字节之间的任何内容。如果客户端知道预期有110个字节,则需要重复调​​用Read,直到所有110个字节都到达为止。