【问题标题】:Not the whole data received in a socket C# WP8不是在套接字 C# WP8 中接收到的全部数据
【发布时间】:2015-09-15 17:03:27
【问题描述】:

当我收到“小”数据时,下面的代码可以正常工作。 但是当我尝试接收大数据时,它总是被切断。 我收到的数据不完整

static ManualResetEvent _clientDone = new ManualResetEvent(false);

        const int TIMEOUT_MILLISECONDS = 500000000;

        const int MAX_BUFFER_SIZE = 2048;

 public string Receive_msg()
        {
            string response = "Operation Timeout";

            if (_socket != null)
            {
                SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
                socketEventArg.RemoteEndPoint = _socket.RemoteEndPoint;

                socketEventArg.SetBuffer(new Byte[MAX_BUFFER_SIZE], 0, MAX_BUFFER_SIZE);


                socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
                {
                    if (e.SocketError == SocketError.Success)
                    {
                        response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
                        response = response.Trim('\0');
                        Debug.WriteLine(response);
                    }
                    else
                    {
                        response = e.SocketError.ToString();
                    }

                    _clientDone.Set();
                });

                _clientDone.Reset();

                _socket.ReceiveAsync(socketEventArg);

                _clientDone.WaitOne(TIMEOUT_MILLISECONDS);
            }
            else
            {
                response = "Socket is not initialized";
            }
            return response;
        }

我尝试增加 TIMEOUT_MILLISECONDS 的值和我的 MAX_BUFFER_SIZE 的大小,但没有任何改变。我需要能够获得更大的数据。 而且我必须使用这种方式,我不能使用WebClient或HttpWebRequest...

【问题讨论】:

  • 据我所知,您应该将大数据块制作成小字节,然后首先发送一个包含您尝试发送的所有数据长度的数据包。之后,您应该提出 n 请求以获取小尺寸的数据,并在完成后将它们放在一起
  • 是什么让您认为您可以一次获得完整、有效的 UTF-8 编码字符串?您调用了一次Receive,但这并不意味着您一定会在套接字中接收到全部数据。请记住,TCP 套接字实际上是数据流,因此您应该以这种方式对待它们……不要假设您已经阅读完毕,直到您看到流已关闭。如果您尝试在单个连接上交换多条消息,您可能需要为每条消息添加长度前缀。
  • 我尝试了几件事情都没有奏效......我仍然无法获得全部数据
  • 不幸的是,你把自己画到了一个角落。为此,您需要一个流式 UTF8 解码器。没有其他方法可以判断您是否掌握了全部信息。您正在为糟糕的协议设计付出代价——消息层是在没有计划的情况下实施的。

标签: c# sockets buffer


【解决方案1】:

如果您没有收到您想要的所有数据,只需再次调用您的接收函数。

另外,这一点非常重要,在您在 TCP 之上实现任何东西之前,记录您正在使用的协议,除非您正在实现一个已经记录在案的协议。阅读一些基于 TCP 的协议(如 SMTP、IRC、HTTP 等)的协议文档可能会有所帮助。

【讨论】:

  • 我尝试了几件事,但都不起作用...我仍然无法获得全部数据
  • 有什么问题?再次调用接收函数会发生什么?
  • 我得到的比以前多得多! (我用好的循环编辑了循环)但仍然不是所有数据,这次我真的不知道为什么
  • 您怎么知道您没有准确获取所有数据?你能准确地说出哪里出了问题吗?你发送多少字节?你收到多少?发生了什么或没有发生什么?
  • 我正在使用一个发送数据的终端(我无法触摸代码来知道发送了多少字节)。终端工作正常,数据正确发送。我的问题之一是我无法计算我得到/接收了多少字节。我只知道数据(字符串),我可以看到我的结果。而且不匹配。即使使用循环,我的结果也比我的数据小
【解决方案2】:

您不能指望在单个套接字读取中获取所有数据。您必须放入一个循环,该循环将继续接收数据,直到没有更多数据要接收。

那么,您如何判断何时收到了所有数据?您有两个选择:您可以发送一个仅出现在消息末尾的分隔符字节;如果你看到这是收到的最后一个字节,你就完成了。否则,您可以在消息的开头发送消息的长度(这就是 HTTP 所做的,顺便说一句)。或者您可以让发送方在消息完成后关闭套接字,然后继续阅读直到套接字上出现 EOF 条件。

【讨论】:

  • 实际上,HTTP 支持所有这三种方法。您可以发送长度(Content-Length),可以使用分隔符(分块编码),也可以使用关闭连接(如果没有长度标头且未使用分块编码)。
【解决方案3】:

问题是接收调用只会读取接收数据包大小和您用于读取的缓冲区之间的最小长度。我怀疑您发送的数据太大并且被系统分成更小的数据包(称为碎片)。

我会在你发送的任何东西前面加上一个整数(4 个字节,即将到来的数据的大小),在接收端读取它并从套接字读取字节,只要你没有收到你发送的长度一个标题。

编辑:由于您可以使用 TcpClient,我建议您使用它和 .GetStream()。如果我错了,请告诉我,但这个程序应该只发送和接收字符串(根据你的代码)。这就是我要做的:

public async Task<string> ReceiveString()
{
    NetworkStream netStream = _client.GetStream(); //Using _client instead of _socket
    StreamReader streamReader = new StreamReader(netStream);
    string result = await streamReader.ReadLine(); //Reading the stream until we find a newline
    return result;
}

你会像这样使用它:

void MyMethod()
{
    string message = await ReceiveString(); //This is the fully async way, the "MyMethod" signature should be "async void" too in order to use "await"

    string message = ReceiveString().Result; //This is a blocking call, waits for everything to be received before proceding
}

注意:如果您以换行符结束所有消息,则此代码有效。它也仅适用于您只想传输字符串(如上所述,我假设)。

【讨论】:

  • 我尝试了几件事情都没有奏效......我仍然无法获得全部数据
  • 我会尝试使用 async-await 和 Socket.ReceiveAsync() 而不是 SocketAsyncEventArgs 中的事件
  • 你能给我指路吗?有了我正在阅读的内容,我的问题可以通过发送给我的数据循环来解决......但我仍然没有成功......如果你有任何想法。
  • 很抱歉,我对 ReceiveAsync 方法调用有误。我认为签名不同并返回 Task。你试过BeginReceive 吗?
  • 我已经试过了……你觉得以我的实际方式,我不可能成功吗?
猜你喜欢
  • 1970-01-01
  • 2012-06-01
  • 2011-05-29
  • 1970-01-01
  • 1970-01-01
  • 2020-02-10
  • 1970-01-01
  • 2020-09-24
  • 2012-04-23
相关资源
最近更新 更多