Click here to Skip to main content
15,885,914 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more: , +
I would like to be able to get the length of the data available from a tcp network stream in C# to set the size of the buffer before reading from the network stream. There is a NetworkStream.Length property but it isn't implemented yet, and I don't want to allocate an enormous size for the buffer as it would take up too much space. The only way I though of doing it would be to precede the data transfer with another telling the size, but this seems a little messy. What would be the best way for me to go about doing this.

-Jordan
Posted
Comments
Sergey Alexandrovich Kryukov 15-Oct-13 14:40pm    
Why "messy"? This is the best thing you could think of.
—SA

It's not that this property is not "yet" implemented; such implementation would not make any sense. Think about the nature of network streams: a remote part writing to the stream can always write some more data. Also note that the stream is the abstraction behind the sequence of TCP packets, so, even though you can determine the size of the package, it does not tell you the expected size of all of the data, because you don't know in advance if all the data is even sent in full or not. Actually, this is a matter of some application-layer protocol, which always present on top of some transport-layer protocol (TCP or any other). Even you did not define such protocol explicitly for your applications (but you should better do it), it always exist implicitly. So, such protocol may or may not assume some finite stream size. In cases different from yours, it may assume "infinite" exchange of small messages.

So, what to do? Actually, you already had the perfect idea: "precede the data transfer with another telling the size". It's not "messy" at all, just to opposite, this is the best thing you can do. You should consciously include this size in your application-layer protocol, formally and accurately. It does not have to be "low-level" size in bytes, it could be, say, something like "number of sent elements". And then, if each element also can have variable size, you can preface each element with its own size, and in case of compose object, do it hierarchically. This is how, in particular, binary serializers work. So, one optional technique would be using a binary serializer for your data.

—SA
 
Share this answer
 
v2
Comments
Sicppy 15-Oct-13 14:51pm    
It just seemed like more work than really needed to be done. I achieved the same sort of result by reading the stream into memory in 1kb segments until the stream was empty, then I could simply read the length from the memory stream, thank you for the reply however.
Sergey Alexandrovich Kryukov 15-Oct-13 15:02pm    
I don't think so. If you really need to know the size of the data of variable size, this is what you need. Your method certainly involves a lot more hassle. And in prefix approach, this is (in the simplest case), you just add reading/writing just one extra number.

You are about to make a wrong decision. Think again, I tell you.

—SA
Sicppy 15-Oct-13 15:09pm    
I don't need to know the size, I just needed to be able to get the entire stream
Sergey Alexandrovich Kryukov 15-Oct-13 15:24pm    
Nevertheless... I warn you: you about to make a big mistake to be sorry about later. Now, you decide.
—SA
Sicppy 15-Oct-13 15:35pm    
This has worked for me perfectly fine so far, but I will try the other as well.
I have answered my question but I'll post it here for anyone who is curious, it is impossible to read the length of the data without any secondary information being sent, however it is possible to load chunks off the stream until the stream is empty, this is what I have achieved with these pieces of code:

Send:
C#
public void SendPacket(Packet pkt)
        {
            NetworkStream Stream = Socket.GetStream();
            Byte[] Buffer = pkt.Serialize();
            Byte[] Size = new Byte[8];
            long len = Buffer.Length;
            using (MemoryStream mem = new MemoryStream(Size))
            {
                using (BinaryWriter BW = new BinaryWriter(mem))
                {
                    BW.Write(len);
                }
            }
            Byte[] Bytes = new Byte[8 + Buffer.Length];
            Array.Copy(Size, Bytes, Size.Length);
            Array.Copy(Buffer, 0, Bytes, 8, Buffer.Length);
            Stream.Write(Bytes, 0, Bytes.Length);
        }


Receive:
C#
private void StreamUpdater()
        {
            while(true){
                NetworkStream Stream = Socket.GetStream();
                if (Stream.DataAvailable)
                {
                    Byte[] Bytes = new Byte[0];
                    Byte[] buffer = new Byte[8];
                    Stream.Read(buffer, 0, 8);
                    long i;
                    using (MemoryStream mem = new MemoryStream(buffer))
                    {
                        using (BinaryReader BR = new BinaryReader(mem))
                        {
                            i = BR.ReadInt64();
                        }
                    }
                    long BiM = 0;
                    if(DataArrived != null)
                        DataArrived.Invoke(this, new TcpEventArgs(new Packet(Packet.Type.Null, null)));
                    Array.Clear(buffer, 0, 8);
                    Array.Resize(ref buffer, 1024);
                    while (BiM < i)
                    {
                        int bytesRead;
                        int BytesToRead = 1024;
                        if (BytesToRead > (i - BiM)) BytesToRead = (int)(i - BiM);
                        if ((bytesRead = Stream.Read(buffer, 0, BytesToRead)) > 0)
                        {
                            Array.Resize(ref Bytes, Bytes.Length + bytesRead);
                            Array.Copy(buffer, 0, Bytes, BiM, bytesRead);
                            BiM += bytesRead;
                            double percent = BiM / i;
                            percent = percent * 100;
                            PercentReceived = (int)percent;
                        }
                    }
                    Packet pkt = SerialMediator.Deserialize(Bytes);
                    if (DataAvailable != null)
                    {
                        DataAvailable.Invoke(this, new TcpEventArgs(pkt));
                    }
                }
            }
        }


Ignore the last four lines of code. The Packet and SerialMediator classes are classes I made to wrap up data being sent between the server and client, and the DataArrived is a custom event I created. The TcpEventArgs is a custom EventArgs class I created.
 
Share this answer
 
v3
Comments
SoMad 15-Oct-13 15:31pm    
That works right up until it breaks. How do you determine that the stream is empty?
When you get to the point where there is no more data available for you to fetch, the sending side might not be done sending yet. A lot of things can happen to cause intermittent delays on TCP connections.

Now, you have not said what kind of data you are transmitting and there are cases where the object size can be deduced from information within the data, but as a general rule, I agree with what Sergey said in his answer.

Soren Madsen
Sicppy 15-Oct-13 15:35pm    
It will only loop until there is no bytes left hence the "> 0" in the while loop

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900