【发布时间】:2014-02-04 15:07:09
【问题描述】:
我正在使用异步套接字在 C# 中为每个服务器应用程序编写多个客户端。连接上的每个客户端发送 10 个项目。问题是,当快速启动大量客户端时,似乎每个客户端发送的项目少于 10 个,有时它什么也不发送,服务器只记录它们的连接。
数据包结构是前 4 个字节是一个 int,其中包含数据大小。这是服务器代码的一部分。每个连接的客户端都有自己的接收缓冲区,BeginReceive 应写入该缓冲区。
private void Recieve(IAsyncResult iar) //Called when socket receives something.
{
Socket server_conn = (Socket)iar.AsyncState;
if (!SocketConnected(server_conn))
{
server_conn.Close();
logthis("Client Disconnected");
return;
}
int n = server_conn.EndReceive(iar); //Stop Receiving and parse data, n is number of bytes received
ClientData asdf = null;
foreach (ClientData cls in clientlist)
{
if (server_conn.RemoteEndPoint == cls.clientsock.RemoteEndPoint) //Who sent this data
{
asdf = cls; //cls is who sent this data
//Start a new thread and pass received bytes to it in order to be parsed
var t = new Thread(() => parse(cls, n,cls.recvbuffer));
t.Start();
Thread.Sleep(100);
break;
}
}
asdf.recvbuffer = new byte[1024]; //Clear buffer of client
server_conn.BeginReceive(asdf.recvbuffer, 0, asdf.recvbuffer.Length, SocketFlags.None, new AsyncCallback(Recieve), server_conn); //Start receiving again
}
private void parse(ClientData theclient, int nobytesreceived, byte[] bytesreceived)
{
ClientData cls = theclient;
int n = nobytesreceived;
byte[] receivedbytes = bytesreceived;
lock(s)
{
if (!cls.dataphase) //If there's no fragmented packets still waiting to be read
{
cls.dataphase = true;
byte[] sizeinbytes = new byte[4];
for (int i = 0; i < 4; i++)
{
sizeinbytes[i] = receivedbytes[i];
}
int size = BitConverter.ToInt32(sizeinbytes, 0); //Read first four bytes of packet to get size of data
if ((n - 4) == size) //If received number of bytes - 4 is equals to datasize
{
byte[] payload = new byte[size];
Array.Copy(receivedbytes, 4, payload, 0, (n - 4)); //Copy data to separately to payload array to be displayed to user
logthis(cls.clientsock.RemoteEndPoint.ToString());
logthis(Encoding.ASCII.GetString(payload));
cls.dataphase = false; //packet read successfully
}
else if ((n - 4) < size) //If received amount of bytes is less than data size (fragmented data)
{
cls.data = new byte[size];
for (int i = 4; i <= n - 4; i++)
{
cls.data[i - 4] += receivedbytes[i];
}
cls.datasize = size; //And cls.dataphase will remain true so it can be handled correctly the next time we receive something from same client
}
else if((n-4) > size) //If received amount of bytes is bigger than data (lumped packets)
{
byte[] payload = new byte[size];
byte[] otherpacket = new byte[(n - 4) - size];
for(int i = 0; i < size; i++)
{
payload[i] += receivedbytes[i + 4];
}
logthis(cls.clientsock.RemoteEndPoint.ToString());
logthis(Encoding.ASCII.GetString(payload));
Array.Copy(receivedbytes, (size + 4), otherpacket, 0, ((n - 4) - size));
receivedbytes = new byte[(n - 4) - size];
receivedbytes = otherpacket;
cls.dataphase = false;
parse(cls, ((n - 4) - size), receivedbytes); //Send rest of packet to read again
}
}
else
{
//not implemented, supposed to handle fragmented packets
if (n >= cls.datasize)
{
}
else if (n < cls.datasize)
{
}
}
}
}
【问题讨论】:
-
那里的比赛条件非常糟糕。如果
asdf.recvbuffer = new byte[1024];发生在parse(cls, n,cls.recvbuffer)之前,您将向parse发送一个空缓冲区。 -
是的,我刚才在修改代码时注意到了这一点,我现在在调用之前将 recvbuffer 复制到另一个字节数组并将其传递给 parse()。现在我只从第一个连接的客户端获得前 10 项,而其他客户端则什么都没有:/
-
代码太多了。我不会花时间去理解它。
-
我本可以将 FIFO 队列用于缓冲区,这会使代码更加简单易读,但现在为时已晚。