Reputation: 1798
I am creating a Remote Desktop program, so I have developed a class that takes snapshot of the client's computer and sends each image in byte array format to the server. Then the server reads those bytes and converts the stream to type System.Drawing.Image. But Image.FromStream takes too long. Everytime I break point this line below it get stuck. No exception gets thrown and nothing happens after that line of code.
Line causing problem:
StreamingData(client, new DataEventArgs(Image.FromStream(stream, false, false)));
Full code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
using System.Drawing;
namespace Simply_Remote_Desktop_Library
{
public sealed class RemoteDesktopListener
{
private TcpListener listner;
private TcpClient client;
public delegate void ConnectedEventHandler(TcpClient sender, EventArgs e);
public event ConnectedEventHandler Connected;
public delegate void IncommingDataEventHandler(TcpClient sender, DataEventArgs e);
public event IncommingDataEventHandler StreamingData;
public delegate void ConnectionEndedEventHandler(RemoteDesktopListener sender, EventArgs e);
public event ConnectionEndedEventHandler ConnectionEnded;
/// <summary>
/// Constructor (+1 Overloads)
/// </summary>
/// <param name="port">The port to listen for incoming connections.</param>
/// <param name="host_ip">The IP address of the client computer.</param>
public RemoteDesktopListener(int port, string host_ip)
{
IPAddress addr;
if (IPAddress.TryParse(host_ip, out addr))
{
if (port > 0 || port < 65535)
{
listner = new TcpListener(addr, port);
}
else if (port < 0 || port > 65535)
{
throw new RemoteDesktopException(new ArgumentOutOfRangeException("port"));
}
}
else
{
throw new RemoteDesktopException("Error: Invalid IP address in string format. Make sure it is in the right format.");
}
}
/// <summary>
/// Constructor (+1 Overloads)
/// </summary>
/// <param name="port">Port for server to listen for incoming connections.</param>
/// <param name="host_ip">Ip address of client.</param>
public RemoteDesktopListener(int port, IPAddress host_ip)
{
if (port > 0 && port < 65535)
{
listner = new TcpListener(host_ip, port);
}
else if (port < 0 && port > 65535)
{
throw new RemoteDesktopException("Error: Port number invalid! Range is from 0 - 65535.");
}
}
/// <summary>
/// Starts the listening process and establishes a connection to a client.
/// </summary>
public void BeginListening()
{
if (listner == null)
{
throw new RemoteDesktopException(new NullReferenceException());
}
else if (listner != null)
{
byte[] bytes = new byte[204800];
bool connected = false;
listner.Start();
//int i;
while (true)
{
client = listner.AcceptTcpClient();
if (connected == false)
{
Connected(client, new EventArgs());
connected = true;
}
NetworkStream stream = client.GetStream();
while (stream.DataAvailable == true)
{
StreamingData(client, new DataEventArgs(Image.FromStream(stream, false, false)));
}
client.Close();
ConnectionEnded(this, new EventArgs());
}
}
return;
}
/// <summary>
/// Starts listening for incoming connection requests.
/// </summary>
/// <param name="backlog">Maximum number of pending connection</param>
public void BeginListening(int backlog)
{
if (listner == null)
{
throw new RemoteDesktopException(new NullReferenceException());
}
else if (listner != null)
{
//byte[] bytes = new byte[204800];
bool connected = false;
listner.Start(backlog);
//int i;
while (true)
{
client = listner.AcceptTcpClient();
if (connected == false)
{
Connected(client, new EventArgs());
connected = true;
}
NetworkStream stream = client.GetStream();
while (stream.DataAvailable == true)
{
StreamingData(client, new DataEventArgs(Image.FromStream(stream, false, false)));
}
client.Close();
ConnectionEnded(this, new EventArgs());
}
}
return;
}
public void StopListening()
{
client.Close();
listner.Stop();
}
/// <summary>
/// Returns the System.Net.Sockets.Socket of the current session.
/// </summary>
public Socket Socket
{
get
{
return listner.Server;
}
}
~RemoteDesktopListener()
{
client.Close();
listner.Stop();
ConnectionEnded(this, new EventArgs());
}
}
public sealed class RemoteDesktopClient
{
private TcpClient client;
private Timer timer;
private Bitmap bitmap;
private System.Drawing.Imaging.PixelFormat format;
private Graphics g;
private NetworkStream stream;
public delegate void ConnectionCloseEventHandler(RemoteDesktopClient sender, EventArgs e);
public event ConnectionCloseEventHandler ConnectionClosed;
/// <summary>
/// Constructor (1 Overload)
/// </summary>
/// <param name="pixel_format">The bitmap's pixel format that will be used for every frame that is sent across the network.</param>
public RemoteDesktopClient(System.Drawing.Imaging.PixelFormat pixel_format)
{
format = pixel_format;
}
public void BeginConnection(int port, string host_ip)
{
IPAddress addr;
if (IPAddress.TryParse(host_ip, out addr))
{
if (port > 0 && port < 65535)
{
client = new TcpClient(host_ip, port);
timer = new Timer();
timer.Interval = Convert.ToInt32(Math.Pow(24, -1) * 1000); // 24 frames per second.
timer.Tick += new EventHandler(timer_Tick);
stream = client.GetStream();
timer.Enabled = true;
}
else if (port > 0 && port > 65535)
{
throw new RemoteDesktopException(new ArgumentOutOfRangeException("port"));
}
}
else
{
throw new RemoteDesktopException("Error: Invalid IP address in string format. Make sure it is in the right format.");
}
}
void timer_Tick(object sender, EventArgs e)
{
bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
g = Graphics.FromImage(bitmap);
g.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
Cursors.Default.Draw(g, new Rectangle(Cursor.Position, Cursors.Default.Size));
byte[] buffer = imageToByteArray(bitmap);
stream.Write(buffer, 0, buffer.Length);
}
public void EndConnection()
{
g.Dispose();
stream.Close();
stream.Dispose();
bitmap.Dispose();
timer.Enabled = false;
client.Close();
ConnectionClosed(this, new EventArgs());
return;
}
public Socket Socket
{
get
{
return client.Client;
}
}
byte[] imageToByteArray(System.Drawing.Image imageIn)
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
}
/// <summary>
/// Represents all remote desktop runtime errors. Inherits from System.Exception.
/// </summary>
public class RemoteDesktopException : Exception
{
private string message;
private Exception ex;
/// <summary>
/// Constructor (+1 Overloads)
/// </summary>
/// <param name="error">The error message in string format.</param>
public RemoteDesktopException(string error)
{
message = error;
}
/// <summary>
/// Constructor (+1 Overloads)
/// </summary>
/// <param name="error">The error in System.Exception format.</param>
public RemoteDesktopException(Exception error)
{
ex = error;
}
public override string StackTrace
{
get
{
return base.StackTrace;
}
}
public override string HelpLink
{
get
{
return base.HelpLink;
}
set
{
base.HelpLink = value;
}
}
public override string Message
{
get
{
if (message != null)
{
return message;
}
else
{
return ex.Message;
}
}
}
}
public class DataEventArgs : EventArgs
{
private Image image;
public DataEventArgs(Image img)
{
image = img;
}
public System.Drawing.Image CurrentFrame
{
get
{
return image;
}
}
}
}
I have added flush here
void timer_Tick(object sender, EventArgs e)
{
bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
g = Graphics.FromImage(bitmap);
g.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
Cursors.Default.Draw(g, new Rectangle(Cursor.Position, Cursors.Default.Size));
byte[] buffer = imageToByteArray(bitmap);
stream.Write(buffer, 0, buffer.Length);
stream.Flush();
}
Was that the place you were referring me to place the stream.Flush() subroutine? *Because it still doesn't work. I also tried setting the NoDelay property to true.*
Upvotes: 1
Views: 1383
Reputation: 133975
It's unlikely that the speed of Image.FromStream
is your problem. Your code is trying to send 24 frames per second. Assuming that your screen is set to 1024 x 768, that's 786,432 pixels. With 24 bit color, that's about 2.25 megabytes. 24 frames per second would be 54 megabytes per second going down the pipe.
Your imageToByteArray
method creates a GIF, which will compress that some. How large is the byte array you get back? That is, in your timer_tick
method, you have:
byte[] buffer = imageToByteArray(bitmap);
stream.Write(buffer, 0, buffer.Length);
What is the value of buffer.Length
?
A gigabit network will give you something less that 100 megabytes per second due to TCP overhead. I suspect you're bumping up against network limitations here.
Typically, applications that transmit video (which is in effect what you're doing) do differential compression. They figure out which pixels have changed and only send those pixels. That greatly reduces network bandwidth. You'll probably have to do something like that if you really want 24 FPS with a screen of any size.
Upvotes: 4
Reputation: 51329
Are you not flushing the stream on the server side? Try calling Stream.Flush() in your server code. This sounds like the client is waiting for more data to finish the image, but it is still sitting in the server stream's buffer.
Upvotes: 1