【问题标题】:C# Sockets: Client not recieving all bytes when receiving large bufferC# Sockets:客户端在接收大缓冲区时未收到所有字节
【发布时间】:2016-08-22 14:55:43
【问题描述】:

所以当服务器发送少于 16384 字节 时,客户端会毫无问题地接收所有数据。 但是一旦服务器发送的数量超过了这个数量,客户端就会收到奇怪的数量,比如 6140。它并不总是这样做,但它大部分时间都会这样做,但它经常这样做。它很少收到完整的 16384 字节。

起初我认为连接发送全部金额需要很长时间,所以我将接收超时设置为 30 秒(30000 毫秒)。 那并没有解决任何问题。每次我收到数据时,我都会使用一个新的字节数组,这样它就不会被另一个BeginRecieve() 覆盖。

我唯一能想到的是硬件(计算机)无法在内存中保存这些大量字节,因此会截断缓冲区。 我有很多代码,但这是事情的基础:

服务器代码(只是其中的一部分):

private void RecieveCallBack(IAsyncResult ar)
{
    Socket client = (Socket)ar.AsyncState;
    try
    {
        int len = client.EndReceive(ar);
        byte[] tempBuffer = new byte[len];
        Array.Copy(buffer, tempBuffer, len);
        object RequestObject = GetRequestObject(tempBuffer); // just deserializes the buffer
        byte[] response = GetResponseFromObject(RequestObject, client); // creates a buffer to return to the client
        client.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(SendCallBack), client);
    }
    catch (Exception)
    {
        Program.Log("Client disconnected");
        client.Close();
        clientSockets.Remove(client);
        Program.UpdateClientCount(clientSockets.Count);
    }
}

private void SendCallBack(IAsyncResult ar)
{
    Socket client = (Socket)ar.AsyncState;
    int sent = client.EndSend(ar);
    Program.Log(sent); // log shows that server sent 16384 bytes
    try
    {
        client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(RecieveCallBack), client);
    }
    catch (Exception e)
    {
        Program.Log(e.Message);
    }
}

客户端代码(只是其中的一部分):

static void SendRequest(object obj)
{
    byte[] buffer = SerializeObject(obj);
    try
    {
        serverSockect.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendCallBack), null);
    }
    catch // the send failed
    {
        serverSockect.Close();
        ConnectedToServer = false;
    }
}

static Queue<byte[]> buffers = new Queue<byte[]>();

static private void SendCallBack(IAsyncResult ar)
{
    serverSockect.EndSend(ar);
    try
    {
        // Create a new buffer for each new BeginRecieve.
        // If one global buffer was used, then it might get overwritten by
        // a second request when the first request hasn't yet been completed.
        byte[] buffer = new byte[16384];
        buffers.Enqueue(buffer);
        serverSockect.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(RecieveCallBack), null);
    }
    catch // the recieve failed
    {
        serverSockect.Close();
        ConnectedToServer = false;
    }
 }

static private void RecieveCallBack(IAsyncResult ar)
{
    byte[] buffer = buffers.Dequeue();
    int len = serverSockect.EndReceive(ar); // len is usually some weird number
    byte[] tempBuffer = new byte[len];
    Array.Copy(buffer, tempBuffer, len);
    object returnObj = GetResponseObject(tempBuffer);
    HandleResponse(returnObj);
}

【问题讨论】:

  • 听起来你只得到了 1 个数据包然后继续前进,而不是等待其余的
  • 这听起来像是常规的数据包碎片;您是否检查过其余数据是否在下一个数据包中到达?我不清楚您是否尝试在ReceiveCallBack 之后继续阅读。使用 TCP 时,您永远不应该依赖与发送数据相同的数据到达。它所提供的只是 stream(使用时)会以正确的顺序为您提供所有正确的数据。

标签: c# sockets tcp client-server buffer


【解决方案1】:

正如在 cmets 中已经提到的 BugFinder,可以将 TCP 流拆分为多个数据包。数据包将以正确的顺序接收,但您不能确定缓冲区是否包含完整的消息。因此,在您的接收方法中,您必须合并接收到的数据,直到获得完整的消息。

另一方面,短消息可以组合成一个数据包。这两者的混合(数据包 1 包含消息 1 的部分 1,数据包 2 包含消息 1 的部分 2 和消息 2 的部分 1,数据包 3 包含消息 2 的部分 2)也是可能的。因此,您需要一个可以解析的协议来识别消息的结尾。

如果您有序列化对象,最好的方法是在开始时设置序列化对象的长度。但是如果没有收到完整的长度数据,请确保能够正确处理数据。在最坏的情况下,每个字节都可能出现在自己的数据包中!

【讨论】:

  • 好吧,这是有道理的,我一直依赖 1 发送,1 接收的心态。您是否有代码或链接,其中包含如何处理多个数据包的示例?我不确定使用什么方法/属性来确定是否需要等待更多数据包。感谢您的回答,我已经更多地研究了数据包和套接字。
  • 请注意我正在序列化对象,而不仅仅是简单的字符串。所以我不能简单地将“”添加到字符串的末尾。这是我在 MSDN 站点上看到的异步服务器和客户端示例的示例。 msdn.microsoft.com/en-us/library/fx6588te%28v=vs.110%29.aspx - 服务器示例
  • 更新了答案,提示如何处理序列化对象。
猜你喜欢
  • 1970-01-01
  • 2019-03-03
  • 1970-01-01
  • 1970-01-01
  • 2011-11-15
  • 2012-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多