-1

I know this is a common question people ask but for days I have been researching and couldn't find a way that will help me.

I'm developing a screen sharing program and using TCP to send the desktop screenshots over the networkstream. I tried using UDP but later discovered that it has a message size limit of 65,507 bytes and I have way more bytes in a single message so I use TCP.

When I run the program I get some of the images and if the bytes size is increased then it while converting the bytes to image it throws

Exception thrown: 'System.ArgumentException' in System.Drawing.dll
System.ArgumentException: Parameter is not valid.

As far as I have read everywhere is networkstream takes time to read all the bytes sent from the server and if the server send another data before the networkstream finish the reading then the byte array will have insufficient data in it and hence it will throw the error. I also tried Thread.Sleep(milliseconds) but again I cannot predict the time to wait and it will make process much slower.

Is there any way I can wait for networkstream write to let networkstream read all the bytes that are sent and continue sending and waiting but this has to be fast as there are sets of images that combining makes a video in picturebox of client.

Code:

Server

    private void receiveScreenShare()
    {
        TcpListener list = new TcpListener(IPAddress.Parse("192.168.0.102"), 4567);
        list.Start();
        TcpClient client = list.AcceptTcpClient();
        NetworkStream streamScreenShare = client.GetStream();
        while (true)
        {
            try
            {
                byte[] bytes = new byte[1000000];
                streamScreenShare.Read(bytes, 0, bytes.Length);
                ImageConverter convertData = new ImageConverter();
                Image image = (Image)convertData.ConvertFrom(bytes); //Here is the error
                pictureBox1.Image = image;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }

Client

    byte[] b1 = null;
    private void sendScreen()
    {
        try
        {
            TcpClient client = new TcpClient("192.168.0.102", 4567);
            while (true)
            {
                NetworkStream streamScreen = client.GetStream();
                //Get Screenshot
                Bitmap bm = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
                Graphics g = Graphics.FromImage(bm);
                g.CopyFromScreen(0, 0, 0, 0, bm.Size);
                //Convert Screenshot to byte
                Bitmap newBM = new Bitmap(bm, new Size(bm.Width / 2, bm.Height / 2));
                MemoryStream ms = new MemoryStream();
                bm.Save(ms, ImageFormat.Jpeg);
                //Write Screenshot into stream
                b1 = ms.ToArray();
                streamScreen.Write(b1, 0, b1.Length);
            
                Thread.Sleep(500);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

I have referred these questions too:

  1. c# tcp How can i get exacly amount of bytes that are recived?
  2. C# TCP Read All Data - Server Side Issues

Working with the methods stated in above solutions I have found that the networkstream tries to read the bytes but the client still sends data to the server and server goes into infinite loop because it's receiving data every millisecond.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
karan ugale
  • 21
  • 1
  • 6
  • 1
    In short: you are assuming that what a single `Write` matches a single `Read`. This assumption is wrong - TCP is not a message protocol but a byte stream only. A common way is to prefix what you send with a length, so that you know how much you need to read. Does this answer your question? [Is recv(bufsize) guaranteed to receive all the data if sended data is smaller then bufsize?](/questions/67509709/), [Can TCP transmit multiple application layer messages concurrently withing the same TCP connection?](/questions/71367919/). – Steffen Ullrich Nov 04 '22 at 06:44
  • You have binary data and only way of reliably getting all the data is to proceed the data with a byte count when transmitting. Then on receive end read until all the bytes are received. You are using TCP where message can be split and recombined during transmission. So you will not get all the data in one chunk. NetworkStream will get EOF before all data is received which is called an underflow. – jdweng Nov 04 '22 at 06:50
  • @SteffenUllrich Yes, I have read about this but I don't want the single Write to match single Read rather I would like to know the process of how "I can wait for networkstream write to let networkstream read all the bytes that are sent and continue sending and waiting". – karan ugale Nov 04 '22 at 06:55
  • @jdweng Thanks for your answer but I'm new to this and find it difficult to understand. Can you by anyway provide me a code so I can test and understand the flow? That would help me alot. – karan ugale Nov 04 '22 at 06:57
  • @jdweng How can I proceed the data with a byte count when transmitting? – karan ugale Nov 04 '22 at 06:59
  • @karanugale: I'm not familiar enough with c# to provide code. But the idea is: On the sender a) determine size of message b) send the length as fixed-size (like as 8 byte uint64) c) send the actual message. On the receiver: a) read the fixed-size length prefix b) extract the length c) read as much as the length says. – Steffen Ullrich Nov 04 '22 at 07:20
  • @SteffenUllrich Thank you for your answer. I will keep your suggestion in mind and research some more. – karan ugale Nov 04 '22 at 07:27
  • byte[] size = BitConverter.GetBytes(b1.Length); streamScreen.Write(size, 0, size.Length); streamScreen.Write(b1, 0, b1.Length); – jdweng Nov 04 '22 at 09:03
  • @jdweng thank you for the answer and what about the read side? How can I read the length and data? – karan ugale Nov 04 '22 at 12:45
  • byte[] lengthArray = streamScreenShare.Read(bytes, 0, 4); int length = BitConverter.ToInt32(lengthArray); Then loop until the number of bytes are read. – jdweng Nov 04 '22 at 13:23
  • @jdweng I tried this but the problem is write is writing data in a while(true) loop and thus when read while reading goes into infinite loop and never break out of it's while loop. – karan ugale Nov 04 '22 at 13:47

1 Answers1

0

Try something like this

           TcpListener list = new TcpListener(IPAddress.Parse("192.168.0.102"), 4567);
            list.Start();
            TcpClient client = list.AcceptTcpClient();
            NetworkStream streamScreenShare = client.GetStream();
            Boolean readCount = false;
            byte[] bytes;
            int length = 0;
            while (true)
            {
                try
                {
                    if (!readCount)
                    {
                        if (streamScreenShare.Length.Position >= 4)
                        {
                            bytes = new byte[4];
                            streamScreenShare.Read(bytes, 0, 4);
                            length = BitConverter.ToInt32(bytes);
                            readCount = true;

                        }
                    }
                    else
                    {
                        if (streamScreenShare.Position  >= length)
                        {
                            bytes = new byte[length];
                            streamScreenShare.Read(bytes, 0, bytes.Length);
                            ImageConverter convertData = new ImageConverter();
                            Image image = (Image)convertData.ConvertFrom(bytes); //Here is the error
                            pictureBox1.Image = image;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • `length = BitConverter.ToInt32(bytes);` this line is causing error `There is no argument given that corresponds to the required formal parameter 'startIndex' of 'BitConverter.ToInt32(byte[], int)'`. What should be the start index? – karan ugale Nov 04 '22 at 14:42
  • and also `if (streamScreenShare.Length >= 4)` throws this error `This stream does not support seek operations.`. – karan ugale Nov 04 '22 at 14:45
  • Should be position not length in two places. You may be using a different library and need to make start index zero. – jdweng Nov 04 '22 at 18:10
  • not working and still returning the same error. – karan ugale Nov 05 '22 at 04:50