Search code examples
c#protocolsbytevnc

RFB/VNC Receiving FrameBuffer and Byte Data issue C#


I'm hoping someone that's had experience with the RFB protocol will give me an answer.

Following the RFB protocol, I've implemented a 3.3 client, the handshake and all is fine. What I don't understand / having issues with, is the FrameUpdateRequest and FrameUpdate using Raw data.

I've read and implemented the documentation verbatim @ contents 6.4.3 and 6.5.1 from http://www.realvnc.com/docs/rfbproto.pdf

It's a bit messy as I've been playing left, right and center with it. But here's what I'm doing:

public int beginNormalOperation()
{        
byte[] fbUp = new byte[10];                    

fbUp[0] = 0003; //Message type, 3= FrameBufferUpdate
fbUp[1] = 0001; //Incremental 0=true, 1=false
fbUp[2] = 0000; //X Position High Byte
fbUp[3] = 0000; //X Position Low Byte
fbUp[4] = 0000; //Y Position High Byte
fbUp[5] = 0000; //Y Position Low Byte

fbUp[6] = INTtoU16(serverSetWidth)[0];
fbUp[7] = INTtoU16(serverSetWidth)[1];
fbUp[8] = INTtoU16(serverSetHeight)[0];
fbUp[9] = INTtoU16(serverSetHeight)[1];

//fbUp[6] = 0000;
//fbUp[7] = 100;
//fbUp[8] = 0000;
//fbUp[9] = 100;   

sock.Send(fbUp);

System.Drawing.Image img;

byte[] bufferInfo = new byte[4];

try
{
sock.Receive(bufferInfo);
textBox4.AppendText("\r\n" + Encoding.Default.GetString(bufferInfo));
}
catch (SocketException ex) { MessageBox.Show("" + ex); }         

return U16toINT(bufferInfo[2], bufferInfo[3]);                     
}

The return value is the number of rectangles, because I'm calling this method from a button click, then passing it to:

public void drawImage(int numRectangles)
{
 //Now within the class
//int xPos = 0;
//int yPos = 0;
//int fWidth = 0;
//int fHeight = 0;

if (myBmp == null)
{
myBmp = new Bitmap(serverSetWidth, serverSetHeight); //I'm requesting full size, so using server bounds atm
}


for (int i = 0; i < numRectangles; i++)
{
byte[] bufferData = new byte[12];
int headerLen = 0;

if (!gotRectangleHeader)
{
try
{
sock.Receive(bufferData);
}
catch (SocketException ex) { MessageBox.Show("" + ex); }

xPos = U16toINT(bufferData[0], bufferData[1]);
yPos = U16toINT(bufferData[2], bufferData[3]);
fWidth = U16toINT(bufferData[4], bufferData[5]);
fHeight = U16toINT(bufferData[6], bufferData[7]);

//headerLen = 12; //I'm now reading the first 12 bytes first so no need for this

gotRectangleHeader = true;
}

bufferData = new byte[((fWidth * fHeight)*4)];


try
{                                            
sock.Receive(bufferData);
}
catch (SocketException ex) { MessageBox.Show("" + ex); }

//Testing to see where the actual data is ending
//byte[] end = new byte[1000];

//Array.Copy(bufferData, 16125, end, 0, 1000);

//for(int f=0; f<bufferData.Length;f++)
//{
//    if (Convert.ToInt32(bufferData[f].ToString()) == 0 &&
//       Convert.ToInt32(bufferData[f + 1].ToString()) == 0 &&
//       Convert.ToInt32(bufferData[f + 2].ToString()) == 0 &&
//       Convert.ToInt32(bufferData[f + 3].ToString()) == 0)
//    {

//        Array.Copy(bufferData, f-30, end, 0, 500);
//        int o = 1;
//    }
//}

int curRow = 0;
int curCol = 0;

for (int curBit = 0; curBit < (bufferData.Length - headerLen) / 4; curBit++)
{
int caret = (curBit * 4) + headerLen;

if (curRow == 200)
{
int ss = 4;
}

Color pixCol = System.Drawing.Color.FromArgb(Convert.ToInt32(bufferData[caret+3].ToString()), Convert.ToInt32(bufferData[caret+2].ToString()), Convert.ToInt32(bufferData[caret+1].ToString()), Convert.ToInt32(bufferData[caret].ToString()));


myBmp.SetPixel(curCol, curRow, pixCol);

if (curCol == (fWidth - 1))
{
curRow++;
curCol = 0;
}
else
{
curCol++;
}

}
}

imgForm.Show();
imgForm.updateImg(myBmp);

}

I'm sorry for the code, I've gone through so many permutations messing about it's become a mess.

This is what I'm trying to do and the way I imagine that it should work according to the protocol:

  1. I request a FrameBufferUpdateRequest, incremental is false (1, according to the Doc's), X and Y position set to 0 and width & height both U16 set to 1366 x 768 respectively.

  2. I receive a FrameBufferUpdate with Number of Rectangles

  3. I call drawImage passing Number of Rectangles in.

  4. I assume from the docs, for each rectangle then create a buffer to that rectangles height and width. And set the pixels on a BMP to the rectangles bounds.

The first rectangle always has a header, and within that header the requested width and height. The following rectangle doesn't have any header information. So i'm missing something here. I'm guessing I haven't received all of the first rectangles data even though I have set the sockets buffer size to width*height*bytes.

Sometimes I get say the top 200 pixels or so and full width though a quarter of the right hand screen is shown on the left hand side in my BMP. Sometimes I've had the full screen and that's what I want but mostly I get a slither say 10px of the top of the screen then nothing.

I'm doing something wrong, I know I am. But what??? The documentation isn't great. If someone could hold my hand through the FrameBufferUpdateRequest -> FrameBufferUpdate -> Display Raw Pixel Data!!

Thanks for any input

craig


Solution

  • I suggest you refer to http://tigervnc.org/cgi-bin/rfbproto which I've found to be a much better reference on the protocol. Specifically the sections on framebufferupdaterequest and framebufferupdate

    Some other notes:

    • you have your incremental values swapped. 1 is incremental and 0 is a full request. With incremental set to 1 you are only requesting rectangles that have changed since the last round.
    • don't assume that framebuffer updates comes synchronously right after you send a framebufferupdate. You really need to read the first byte first to determine what sort of message the server has sent and process the message accordingly.
    • you should really create your bitmap based on that actual size of each rectangle (i.e. after you read the size of the rectangle you are processing.
    • you need to take into account the encoding format (you're currently ignoring it). That will determine how large the image data for the rectangle is.
    • Also, I'm not familiar with how C# Socket.Receive works. Unless it is always guaranteed to block until the buffer is filled, you might need to check how much data was actually read since the servers don't always send the whole framebufferupdate message all at once and even if they do, the messages might get fragmented and not arrive all at once.