我在服务器上有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.
}
}
是否有其他方式或如何正确有效地向多个客户端发送数据?
如果可以,请告诉我示例。
感谢您的建议。
答案 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个字节都到达为止。