Hello,
I'm making a networked client/server program (with a reverse connection, i.e. server connects to the client). I'm wanting to take a screenshot(server side) and transfer this file over to the client so that it can be saved.

Now before I go any further, the code I'm about to disclose works perfectly when running the server/client on the same machine. However, if I try to run it over a network I get a several rows of pixels in the saved screenshot file (the top), but nothing more - also the file size is never larger than 4kb where as it's usually around 300-400kb when working correctly.

Client Side - Receiving

byte[] buffer = new byte[1024];
int numberOfBytesRead = 0;

MemoryStream receivedData = new MemoryStream();

do
  {
   numberOfBytesRead = stream.Read(buffer, 0, buffer.Length); //Read from network stream
   receivedData.Write(buffer, 0, buffer.Length); //Write to memory stream

   } while (stream.DataAvailable);
                            
File.WriteAllBytes(@"C:\file.png", receivedData.ToArray());

Server Side - Sending

//Take screenshot and save to memory
MemoryStream screenshotMemory = new MemoryStream();
TakeScreenshot().Save(screenshotMemory, ImageFormat.Png); //TakeScreenshot is my own method that returns a Bitmap
clientConnection.WriteLine("SEND"); //Just my own message code to let the client know a file is being sent
clientConnection.SendData(screenshotMemory.ToArray());

Edit: Woops, also clientConnection.SendData() is just a method for:

m_Client.GetStream().Write(data, 0, data.Length); //Where m_Client is the TcpClient

Hope that makes sense. If you need any more information please ask, as it's starting to get really annoying :(

Thanks!

Is it possible that stream.DataAvailable is going false due to network delays?

If this is the case then change the transmission protocol to send the data size first; then the client side knows how much data to expect.
Now terminate the read when receivedData is the correct size (i.e. all data read) or some time-out elapses.

Right. I just tried what you suggested, and it writes more to the screenshot than before - but it's all corrupt. It writes about half of the screen, but the original few rows that I was getting before (when I wrote this thread) is clear/uncorrupt..

Very confusing :(

Adjusted my code like so:

byte[] buffer = new byte[1024];
                            int numberOfBytesRead = 0;
                            int totalNumberOfBytesRead = 0;
                            int FILE_SIZE = int.Parse(clientMessage.Split(' ')[1]);
                            MemoryStream receivedData = new MemoryStream();

                            do
                            {
                                numberOfBytesRead = stream.Read(buffer, 0, buffer.Length); //Read from network stream
                                receivedData.Write(buffer, 0, buffer.Length); //Write to memory stream

                                totalNumberOfBytesRead += numberOfBytesRead;
                            } while (totalNumberOfBytesRead < FILE_SIZE);
                            


                            File.WriteAllBytes(@"C:\file.png", receivedData.ToArray());

In place of line 10 use

if (numberOfBytesRead > 0) 
    receivedData.Write(buffer, 0, numberOfBytesRead); //Write to memory stream

to allow for when the read gets nothing or less than buffer.Length!
[Edit]
Don't forget to add a time-out.
Otherwise the loop might go on forever if the file size is wrong or the data gets "lost in the post"!

Just though I'd add my 2p.

You're probably best not writing the entire of your data buffer to the memory every time.

You have a value you can use numberOfBytesRead . Instead of receivedData.Write(buffer, 0, buffer.Length); I believe you ought to be using receivedData.Write(buffer, 0, numberOfBytesRead); otherwise you could be messing up your memory stream with 0 bytes at the end of the file.


Also, what it may be worth doing, is actually setting your data buffer to the correct size you're expecting first.

So change your receive method to be something more like:

Byte[] dataBuffer = new Byte[4];
Int32 totalBytes = 0;
do
{
    totalBytes += stream.Read(dataBuffer, totalBytes, 4 - totalBytes);
} while(totalBytes < 4);
Int32 dataSize = BitConverter.ToInt32(dataBuffer, 0);

Int32 bytesRead = 0;
totalBytes = 0;
dataBuffer = new Byte[dataSize];

do
{
    bytesRead = stream.Read(dataBuffer, totalBytes, dataSize - totalBytes);
} while(totalBytes < dataSize);

File.WriteAllBytes(@"C:\file.png", dataBuffer);

That should get you what you want, I haven't tested this, but I'm pretty confident :)

commented: Good one. Much more memory efficient. +10

Cheers guys. Got it working now! :-) Thanks very much for your help.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.