C#套接字接收字节数组长度

5

我想学习如何在C#中使用socket,我有一个疑问,我正在使用类似于以下代码的东西:

byte[] data = new byte[64]; 
int length = 0;
length = sock.Receive(data);
//more code...

因此,byte [] 数据被填充了接收到的数据,并且数组中剩余的空间被填充为0。 byte [] 是否完全分配到内存中(全部64字节)?如果是这样的话,是否有一种方法可以使byte []与实际发送的数据大小相同?

1
你可以检查 sock.Available 来查看已经从网络中接收了多少数据。请注意,之后可能会有更多的数据到来。 - ebyrob
是的,这64个字节都已经分配好了。它们在接收调用之前就已经被分配了,所以有谁能知道数据的实际大小呢?这就像为包装一个你从未见过的包裹剪一段合适长度的细绳一样。你需要一个水晶球。 - phoog
@ebyrob 谢谢。这个 .Available() 方法对我有用。 - Nayan Dey
3个回答

4
你可以检查 sock.Available 来查看已经接收到的内容。(目前为止)
byte[] data = new byte[sock.Available]; 
int length = sock.Receive(data);
//more code...

注意:由于您可能并不知道网络上即将到来的内容,因此通常更明智的做法是先仅阅读标题(带有大小信息)或分配比必要空间更多,并多次调用.Recieve(),直到达到记录的末尾。
注意:此代码假定您已经知道有一些数据可供接收,并且您等待了足够长的时间以获取一些有用的数据。
如果您选择使用长度标头,.Available可以帮助您避免读取部分标头并且需要重新组装它,这很好。(在这种情况下,只有大型消息可能需要手动重新组装)

不确定这有什么帮助。如果可用数量为零怎么办? - usr
哦,这正是我正在寻找的,我不知道“available”是那个:P,谢谢。 - Pau C
如果 Available 为零,可能仍然会有数据稍后可读。这对 @user3324670 没有帮助。Available 对于几乎任何目的都是无用的。 - usr
例如,在选择90%的时间后,您可能已经准备好了一个完整的消息,其大小小于MTU,并可以进行读取。Available对套接字编程并不是必需的,但它可能会派上用场。 - ebyrob
@user3324670 你可能需要一个记录分隔符或者一个包含大小数据的头部。然而,头部通常不仅仅包含大小信息。(协议类型、协议版本、消息类型(如果有)) - ebyrob
显示剩余3条评论

2

正如所述,通常情况下read函数可能会返回比它要求的字节数更少的字节。请参考下面的解决方法,它可以确保读取和传递缓冲区大小相同的字节数。该函数来源于这里

/// Reads data into a complete array, throwing an EndOfStreamException
/// if the stream runs out of data first, or if an IOException
/// naturally occurs.
/// </summary>
/// <param name="stream">The stream to read data from</param>
/// <param name="data">The array to read bytes into. The array
/// will be completely filled from the stream, so an appropriate
/// size must be given.</param>
public static void ReadWholeArray (Stream stream, byte[] data)
{
    int offset=0;
    int remaining = data.Length;
    while (remaining > 0)
    {
        int read = stream.Read(data, offset, remaining);
        if (read <= 0)
            throw new EndOfStreamException 
                (String.Format("End of stream reached with {0} bytes left to read", remaining));
        remaining -= read;
        offset += read;
    }
}

您可以首先使用此方法读取一个2字节整数,该整数应表示接下来将要读取的字节数。然后再次读取,但现在要读取指定的两个字节整数中指定的字节数。
但是,为了使其工作,显然发送方首先必须发送一个代表随后数据长度的两个字节整数 - 然后才是数据本身。
因此,基本上您需要先在大小为2的字节数组上调用上述函数(以获取数据长度),然后在大小与该2字节整数指示的大小相同的字节数组上调用该函数(以获取数据)。
您可以使用此方法从NetworkStream中读取。有关此主题的更多阅读,请参见这里

我可能会这样做,只需读取2字节并将其转换为整数,然后读取发送的大小。 - Pau C
@user3324670:是的,你应该走这条路线;你可以使用上述函数来读取。 - Giorgi Moniava
我有点担心这个问题,如果有人发送了一个不正确的大小,一切都会混乱(我想如果我想让服务器安全,我必须关注许多其他事情)。 - Pau C
@user3324670:是的,那现在是一个安全问题,你需要以不同的方式来解决。 - Giorgi Moniava
我不知道该怎么做,但我肯定还有很多其他错误的地方,至少我正在学习。 - Pau C
@user3324670 是的,这就是你学习的方式。如果你想确保别人不会发送消息,可以尝试验证消息(谷歌搜索该术语)。 - Giorgi Moniava

2

你只需要使用Receive的返回值,就可以了解到有多少数据已经到达。如果你想要缩短缓冲区,可以使用Array.Resize,但通常这意味着出现了问题。

此外,请注意,TCP是一系列字节流,不保留消息边界。


1
我尝试过使用“byte [] data = new byte [1048576];”,CPU使用率从0.4升高到2.7,内存从6mb增加到20mb,因此已经分配。问题是,我不知道接收到的数据大小,直到我接收它,而我需要一个至少与发送数据大小相同的byte [](或无论如何,我都不知道该怎么做)。我能想到的唯一解决方案是让客户端发送最大数量的数据,而不再多发送。 - Pau C
了解客户端的期望是网络编程的重要组成部分。此外,您可以仅读取可用数据的一部分,然后再次读取以获取其余部分。(欠读和过度读取都可以,只需在获得消息后重新组装即可) - ebyrob
唯一知道要传输多少的方法是让另一端告诉你,例如通过发送一个整数表示他们现在将发送多少字节。这是一种常见的方法。 - usr
关于分配,为什么它不会被分配?这是一个普通的数组。将其与套接字一起使用并不使其特殊。 - usr
无论如何,您都需要循环直到获得所需的所有数据。使用BinaryReader可以自动化其中的一些操作。 - usr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接