Jorne De Blaere
Jorne De Blaere

Reputation: 39

Sending data over TCP

I have a client server situation, where the client sends the data (a movie for example) to the server, the server saves that data to the HDD.

It sends the data by a fixed array of bytes. After the bytes are sent, the server asks if there is more, if yes, send more and so on. Every thing is going well, all the data gets across.

But when I try to play the movie, it cant be played and if I look to the file length of each movie (client and server) the server movie is bigger then the client movie.also when I look at the command screen at the end of the sending/receiving data there is more then a 100% of the bytes that are across.

The only thing I can think of that can be wrong is the fact that my server reads in the stream till the fixed buffer array is full and therefor has at the end more bytes then the client. However if that is the problem how can I solve this?

I've just added the 2methods of sending, because the tcp connection works, any help is welcome.

Client

public void SendData(NetworkStream nws, StreamReader sr, StreamWriter sw)
{
    using (FileStream reader = new FileStream(this.path, FileMode.Open, FileAccess.Read))
    {                
         byte[] buffer = new byte[1024];
         int currentBlockSize = 0;

         while ((currentBlockSize = reader.Read(buffer, 0, buffer.Length)) > 0)
         {
             sw.WriteLine(true.ToString());
             sw.Flush();
             string wait = sr.ReadLine();
             nws.Write(buffer, 0, buffer.Length);
             nws.Flush();
             label1.Text = sr.ReadLine();
         }
         sw.WriteLine(false.ToString());
         sw.Flush();                
    }
}

Server

    private void GetMovieData(NetworkStream nws, StreamReader sr, StreamWriter sw, Film filmInfo)
    {
        Console.WriteLine("Adding Movie: {0}", filmInfo.Titel);
        double persentage = 0;

        string thePath = this.Path + @"\films\" + filmInfo.Titel + @"\";
        Directory.CreateDirectory(thePath);

        thePath += filmInfo.Titel + filmInfo.Extentie;

        try
        {
            byte[] buffer = new byte[1024]; //1Kb buffer

            long fileLength = filmInfo.TotalBytes;
            long totalBytes = 0;

            using (FileStream writer = new FileStream(thePath, FileMode.CreateNew, FileAccess.Write))
            {
                int currentBlockSize = 0;
                bool more;
                sw.WriteLine("DATA");
                sw.Flush();
                more = Convert.ToBoolean(sr.ReadLine());


                while (more)
                {
                    sw.WriteLine("SEND");
                    sw.Flush();
                    currentBlockSize = nws.Read(buffer, 0, buffer.Length);
                    totalBytes += currentBlockSize;
                    writer.Write(buffer, 0, currentBlockSize);

                    persentage = (double)totalBytes * 100.0 / fileLength;
                    Console.WriteLine(persentage.ToString());

                    sw.WriteLine("MORE");
                    sw.Flush();
                    string test = sr.ReadLine();
                    more = Convert.ToBoolean(test);                       
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

Upvotes: 0

Views: 2520

Answers (2)

KBoek
KBoek

Reputation: 5975

Let me give it a try, but don't shoot me if it doesn't work.

I see that you have a buffer size of 1024, regardless of how many bytes there are left in the file that you send. Say you have a file of 2900 bytes, which would require to send 3 times, the last you send there will only be 852 bytes left to send. Yet, you create a buffer of 1024 and send over 1024 bytes. This means that your server receives 852 bytes of real data, and 172 zero-filled bytes. Even though, all those 172 bytes are save to the movie file on the server.

I guess there's an easy fix: When you write the data to the server, use the currentBlockSize as argument for the length. So in method SendData on the client, inside the while loop, change:

nws.Write(buffer, 0, buffer.Length);

to this:

nws.Write(buffer, 0, currentBlockSize);

Upvotes: 0

svick
svick

Reputation: 244777

There is a reason why Read() returns the number of bytes read: it's possible it will return less than the size of the buffer. Because of this, you should do something like nws.Write(buffer, 0, currentBlockSize); in SendData(). But this will break your protocol, because the blocks won't have the size anymore.

But I find it hard to believe your code actually behaves the way you describe. That's because Read() in GetMovieData() also may not fill the whole buffer. Also, StreamReader is allowed to keep some data in an internal buffer, which would mean you could read some completely bogus data.

I think code like this, where you're combining Streams and StreamReaders/StreamWriters is a really bad idea. It would be hard to make it actually correct. What you should do instead is to make your protocol completely byte-based (not character-based), even if those bytes are ASCII-encoded "SEND".

Upvotes: 2

Related Questions