Is this the correct way to send a Bitmap over sockets?

E

eliss.carmine

I'm using TCP/IP to send a Bitmap object over Sockets. This is my first
time using C# at all so I don't know if this is the "right" way to do
it. I've already found out several times the way I was doing something
was really inefficient and could reduce 10 lines of code with 2, etc.

For reading, I am using a TcpClient and I call

NetworkStream ns = client.GetStream(); to get a stream

stream.Read(buffer, 0, buffer.Length);

now already my question is, how do I make buffer so that I don't have
to arbitrarily make it 1 MB? What if my Bitmap is larger than 1 MB? Do
I need to read multiple times and then "join" the buffers? How would I
do that?

MemoryStream ms = new MemoryStream(buffer);

Bitmap = new Bitmap(ms);

Does this seem like the correct way to send the Bitmap over my Socket?

I also found that saving the Bitmap as a PNG before sending makes it
smaller. Is this a good idea, and can I compress it even more?
bm.Save(ms, System.Drawing.Imaging.ImageFormat.Png);


Thanks so much for reading a question from a newbie. I know this might
not make sense to some of you, or seem trivial.
 
M

Marc Gravell

Can you not save / load directly to / from the NetworkStream?

bm.Save(ns, System.Drawing.Imaging.ImageFormat.Png);

Marc
 
L

Lenard Gunda

Hi,

I just tried sending an image (will post a sample in a minute).

GDI+ throws an error if you try to save into the NetworkStream directly.
I don't know why. You can however read the image from the NetworkStream
directly.

-Lenard
 
L

Lenard Gunda

Hi,

I played a little with this problem, and came up with the following
solution.

For sending, you could use:

TcpListener listener = new TcpListener ( 9998 );
listener.Start ();
TcpClient client = listener.AcceptTcpClient ();
NetworkStream stream = client.GetStream ();

Image image = Image.FromFile ( @"..file.." );
using ( MemoryStream ms = new MemoryStream () )
{
image.Save ( ms, ImageFormat.Png );
byte[] imageBuffer = ms.GetBuffer ();
stream.Write ( imageBuffer, 0, (int)ms.Length );
}

stream.Close ( 500 );
client.Close ();
listener.Stop ();

Here you don't have to worry about the buffer, because MemoryStream will
handle it for you. For some reason you cannot use the NetworkStream
directly (to save the image into that). GDI+ throws an exception if you
try that. I guess it has something to do with network streams not being
searchable (but that is just a guess).

On the receiving end, you can however read directly from the
NetworkStream. So you could create a method like:

private Image ReceiveBitmap ()
{
TcpClient client = new TcpClient ();
client.Connect ( "127.0.0.1", 9998 );
NetworkStream stream = client.GetStream ();

Image image = Image.FromStream ( stream );

stream.Close ();
client.Close ();

return image;
}

This will read your image and return it to the caller. As you can see
you do not need to worry about the length of the buffer, because the
image loader will read the correct number of bytes.

Note however, that I did not do any error checking above.

Another way to solve the problem is to not send just the image onto the
network stream, rather first the size (say an Int32) and then the
contents of the buffer. On the receiving end you would then always read
an Int32 (the length), allocate a buffer that fits the data, and then
receive into that buffer. That way you allocate just as much space as
you need.

Hope this helps

-Lenard
 
D

Dave Sexton

Hi,
I'm using TCP/IP to send a Bitmap object over Sockets. This is my first
time using C# at all so I don't know if this is the "right" way to do
it. I've already found out several times the way I was doing something
was really inefficient and could reduce 10 lines of code with 2, etc.

For reading, I am using a TcpClient and I call

NetworkStream ns = client.GetStream(); to get a stream

stream.Read(buffer, 0, buffer.Length);

now already my question is, how do I make buffer so that I don't have
to arbitrarily make it 1 MB? What if my Bitmap is larger than 1 MB? Do
I need to read multiple times and then "join" the buffers? How would I
do that?

MemoryStream ms = new MemoryStream(buffer);

Bitmap = new Bitmap(ms);

Does this seem like the correct way to send the Bitmap over my Socket?

I also found that saving the Bitmap as a PNG before sending makes it
smaller. Is this a good idea, and can I compress it even more?
bm.Save(ms, System.Drawing.Imaging.ImageFormat.Png);


Thanks so much for reading a question from a newbie. I know this might
not make sense to some of you, or seem trivial.

Network programming is never trivial ;)

First, I have to ask, why do you want to do that? (There may be a better
way)

Next, try the code after my signature if you really need to use Sockets.
Beware there is no error handling logic, which I'm sure you'll want to
implement otherwise the app could easily crash.

--
Dave Sexton

using System;
using System.Net.Sockets;
using System.Threading;
using System.Drawing;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Net;

namespace Testing
{
class Program
{
const int port = 9483; // arbitrary

static void Main(string[] args)
{
StartServer();

SendBitmap();

Console.WriteLine("Press [enter] to exit");
Console.ReadLine();
}

static void SendBitmap()
{
Bitmap bitmap = new Bitmap(300, 500);
byte[] bytes = null;

Console.WriteLine("Client: Created Bitmap: {0}x{1}", bitmap.Width,
bitmap.Height);

Console.WriteLine("Client: Serializing Bitmap");

using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, bitmap);

bytes = stream.ToArray();
}

Console.WriteLine("Client: Connecting to Server");

using (TcpClient client = new TcpClient("localhost", port))
{
Console.WriteLine("Client: Sending Bitmap");

client.GetStream().Write(bytes, 0, bytes.Length);
}

Console.WriteLine("Client: Bitmap Sent");
}

static Bitmap AcceptBitmap(TcpClient client)
{
NetworkStream stream = client.GetStream();

byte[] bytes = new byte[1024];
int read = 0;

using (MemoryStream bitmapStream = new MemoryStream())
{
Console.WriteLine("Server: Receiving Bitmap");

while ((read = stream.Read(bytes, 0, 1024)) != 0)
// read = 0 indicates that the Socket was closed
bitmapStream.Write(bytes, 0, read);

// reset position to beginning of stream
bitmapStream.Position = 0;

Console.WriteLine("Server: Bitmap Received");

BinaryFormatter formatter = new BinaryFormatter();

Console.WriteLine("Server: Deserializing Bitmap");

return (Bitmap) formatter.Deserialize(bitmapStream);
}
}

static void StartServer()
{
TcpListener listener = new TcpListener(IPAddress.Loopback, port);

Thread thread = new Thread(delegate()
// the listener will run in the background
{
listener.Start();

Bitmap bitmap = null;

using (TcpClient client = listener.AcceptTcpClient())
{
Console.WriteLine("Server: Client Connected");

bitmap = AcceptBitmap(client);

Console.WriteLine("Server: Received Bitmap: {0}x{1}", bitmap.Width,
bitmap.Height);
}

listener.Stop();
});

thread.IsBackground = true;
thread.Start();

Console.WriteLine("Server: Server Started");
}
}
}
 
D

Dave Sexton

Hi Lenard,

I like your idea of saving the bitmap directly to the stream. I used a
BinaryFormatter in my example, but it seems unnecessary now :)

However, you shouldn't use the GetBuffer method since it might return the
stream + empty buffer bytes. Use ToArray() instead.

"MemoryStream.ToArray Method"
http://msdn2.microsoft.com/en-us/library/system.io.memorystream.toarray.aspx

--
Dave Sexton

Lenard Gunda said:
Hi,

I played a little with this problem, and came up with the following
solution.

For sending, you could use:

TcpListener listener = new TcpListener ( 9998 );
listener.Start ();
TcpClient client = listener.AcceptTcpClient ();
NetworkStream stream = client.GetStream ();

Image image = Image.FromFile ( @"..file.." );
using ( MemoryStream ms = new MemoryStream () )
{
image.Save ( ms, ImageFormat.Png );
byte[] imageBuffer = ms.GetBuffer ();
stream.Write ( imageBuffer, 0, (int)ms.Length );
}

stream.Close ( 500 );
client.Close ();
listener.Stop ();

Here you don't have to worry about the buffer, because MemoryStream will
handle it for you. For some reason you cannot use the NetworkStream
directly (to save the image into that). GDI+ throws an exception if you
try that. I guess it has something to do with network streams not being
searchable (but that is just a guess).

On the receiving end, you can however read directly from the
NetworkStream. So you could create a method like:

private Image ReceiveBitmap ()
{
TcpClient client = new TcpClient ();
client.Connect ( "127.0.0.1", 9998 );
NetworkStream stream = client.GetStream ();

Image image = Image.FromStream ( stream );

stream.Close ();
client.Close ();

return image;
}

This will read your image and return it to the caller. As you can see you
do not need to worry about the length of the buffer, because the image
loader will read the correct number of bytes.

Note however, that I did not do any error checking above.

Another way to solve the problem is to not send just the image onto the
network stream, rather first the size (say an Int32) and then the contents
of the buffer. On the receiving end you would then always read an Int32
(the length), allocate a buffer that fits the data, and then receive into
that buffer. That way you allocate just as much space as you need.

Hope this helps

-Lenard




I'm using TCP/IP to send a Bitmap object over Sockets. This is my first
time using C# at all so I don't know if this is the "right" way to do
it. I've already found out several times the way I was doing something
was really inefficient and could reduce 10 lines of code with 2, etc.

For reading, I am using a TcpClient and I call

NetworkStream ns = client.GetStream(); to get a stream

stream.Read(buffer, 0, buffer.Length);

now already my question is, how do I make buffer so that I don't have
to arbitrarily make it 1 MB? What if my Bitmap is larger than 1 MB? Do
I need to read multiple times and then "join" the buffers? How would I
do that?

MemoryStream ms = new MemoryStream(buffer);

Bitmap = new Bitmap(ms);

Does this seem like the correct way to send the Bitmap over my Socket?

I also found that saving the Bitmap as a PNG before sending makes it
smaller. Is this a good idea, and can I compress it even more?
bm.Save(ms, System.Drawing.Imaging.ImageFormat.Png);


Thanks so much for reading a question from a newbie. I know this might
not make sense to some of you, or seem trivial.
 
L

Lenard Gunda

Hi Dave,

My understanding is that ToArray() would create a copy of the buffer,
while GetBuffer() would return the buffer itself. For big buffers
(although they do get cleaned up by GC) I think this would waste memory.

While it is true that there might be unused space at the end, this is
the reason why I used the MemoryStream instance's Length property, and
not the length of the byte[] itself. This way the correct number of
bytes would be sent over the network.

-Lenard
 
D

Dave Sexton

Hi Lenard,

I can see how that might be preferable.

--
Dave Sexton

Lenard Gunda said:
Hi Dave,

My understanding is that ToArray() would create a copy of the buffer,
while GetBuffer() would return the buffer itself. For big buffers
(although they do get cleaned up by GC) I think this would waste memory.

While it is true that there might be unused space at the end, this is the
reason why I used the MemoryStream instance's Length property, and not the
length of the byte[] itself. This way the correct number of bytes would be
sent over the network.

-Lenard

Dave said:
Hi Lenard,

I like your idea of saving the bitmap directly to the stream. I used a
BinaryFormatter in my example, but it seems unnecessary now :)

However, you shouldn't use the GetBuffer method since it might return the
stream + empty buffer bytes. Use ToArray() instead.

"MemoryStream.ToArray Method"
http://msdn2.microsoft.com/en-us/library/system.io.memorystream.toarray.aspx
 
E

eliss.carmine

Hi Lenarf,

Thanks for the quick response.

I am trying to get the ReceiveBitmap part working using your example. I
had a working solution and in mine, I replaced

byte[] buffer = new byte[1000000];
stream.Read(buffer, 0, buffer.Length);
MemoryStream ms = new MemoryStream(buffer);
ShownWindow.BackgroundImage = Bitmap.FromStream(ms);

with

ShownWindow.BackgroundImage = Bitmap.FromStream(stream);

in order to cut out two middlemen, but then it stops working. Any idea
why?


Lenard said:
Hi,

I played a little with this problem, and came up with the following
solution.

For sending, you could use:

TcpListener listener = new TcpListener ( 9998 );
listener.Start ();
TcpClient client = listener.AcceptTcpClient ();
NetworkStream stream = client.GetStream ();

Image image = Image.FromFile ( @"..file.." );
using ( MemoryStream ms = new MemoryStream () )
{
image.Save ( ms, ImageFormat.Png );
byte[] imageBuffer = ms.GetBuffer ();
stream.Write ( imageBuffer, 0, (int)ms.Length );
}

stream.Close ( 500 );
client.Close ();
listener.Stop ();

Here you don't have to worry about the buffer, because MemoryStream will
handle it for you. For some reason you cannot use the NetworkStream
directly (to save the image into that). GDI+ throws an exception if you
try that. I guess it has something to do with network streams not being
searchable (but that is just a guess).

On the receiving end, you can however read directly from the
NetworkStream. So you could create a method like:

private Image ReceiveBitmap ()
{
TcpClient client = new TcpClient ();
client.Connect ( "127.0.0.1", 9998 );
NetworkStream stream = client.GetStream ();

Image image = Image.FromStream ( stream );

stream.Close ();
client.Close ();

return image;
}

This will read your image and return it to the caller. As you can see
you do not need to worry about the length of the buffer, because the
image loader will read the correct number of bytes.

Note however, that I did not do any error checking above.

Another way to solve the problem is to not send just the image onto the
network stream, rather first the size (say an Int32) and then the
contents of the buffer. On the receiving end you would then always read
an Int32 (the length), allocate a buffer that fits the data, and then
receive into that buffer. That way you allocate just as much space as
you need.

Hope this helps

-Lenard




I'm using TCP/IP to send a Bitmap object over Sockets. This is my first
time using C# at all so I don't know if this is the "right" way to do
it. I've already found out several times the way I was doing something
was really inefficient and could reduce 10 lines of code with 2, etc.

For reading, I am using a TcpClient and I call

NetworkStream ns = client.GetStream(); to get a stream

stream.Read(buffer, 0, buffer.Length);

now already my question is, how do I make buffer so that I don't have
to arbitrarily make it 1 MB? What if my Bitmap is larger than 1 MB? Do
I need to read multiple times and then "join" the buffers? How would I
do that?

MemoryStream ms = new MemoryStream(buffer);

Bitmap = new Bitmap(ms);

Does this seem like the correct way to send the Bitmap over my Socket?

I also found that saving the Bitmap as a PNG before sending makes it
smaller. Is this a good idea, and can I compress it even more?
bm.Save(ms, System.Drawing.Imaging.ImageFormat.Png);


Thanks so much for reading a question from a newbie. I know this might
not make sense to some of you, or seem trivial.
 
E

eliss.carmine

Sorry, Lenard,

stream is a NetworkStream by the way

Hi Lenarf,

Thanks for the quick response.

I am trying to get the ReceiveBitmap part working using your example. I
had a working solution and in mine, I replaced

byte[] buffer = new byte[1000000];
stream.Read(buffer, 0, buffer.Length);
MemoryStream ms = new MemoryStream(buffer);
ShownWindow.BackgroundImage = Bitmap.FromStream(ms);

with

ShownWindow.BackgroundImage = Bitmap.FromStream(stream);

in order to cut out two middlemen, but then it stops working. Any idea
why?


Lenard said:
Hi,

I played a little with this problem, and came up with the following
solution.

For sending, you could use:

TcpListener listener = new TcpListener ( 9998 );
listener.Start ();
TcpClient client = listener.AcceptTcpClient ();
NetworkStream stream = client.GetStream ();

Image image = Image.FromFile ( @"..file.." );
using ( MemoryStream ms = new MemoryStream () )
{
image.Save ( ms, ImageFormat.Png );
byte[] imageBuffer = ms.GetBuffer ();
stream.Write ( imageBuffer, 0, (int)ms.Length );
}

stream.Close ( 500 );
client.Close ();
listener.Stop ();

Here you don't have to worry about the buffer, because MemoryStream will
handle it for you. For some reason you cannot use the NetworkStream
directly (to save the image into that). GDI+ throws an exception if you
try that. I guess it has something to do with network streams not being
searchable (but that is just a guess).

On the receiving end, you can however read directly from the
NetworkStream. So you could create a method like:

private Image ReceiveBitmap ()
{
TcpClient client = new TcpClient ();
client.Connect ( "127.0.0.1", 9998 );
NetworkStream stream = client.GetStream ();

Image image = Image.FromStream ( stream );

stream.Close ();
client.Close ();

return image;
}

This will read your image and return it to the caller. As you can see
you do not need to worry about the length of the buffer, because the
image loader will read the correct number of bytes.

Note however, that I did not do any error checking above.

Another way to solve the problem is to not send just the image onto the
network stream, rather first the size (say an Int32) and then the
contents of the buffer. On the receiving end you would then always read
an Int32 (the length), allocate a buffer that fits the data, and then
receive into that buffer. That way you allocate just as much space as
you need.

Hope this helps

-Lenard




I'm using TCP/IP to send a Bitmap object over Sockets. This is my first
time using C# at all so I don't know if this is the "right" way to do
it. I've already found out several times the way I was doing something
was really inefficient and could reduce 10 lines of code with 2, etc.

For reading, I am using a TcpClient and I call

NetworkStream ns = client.GetStream(); to get a stream

stream.Read(buffer, 0, buffer.Length);

now already my question is, how do I make buffer so that I don't have
to arbitrarily make it 1 MB? What if my Bitmap is larger than 1 MB? Do
I need to read multiple times and then "join" the buffers? How would I
do that?

MemoryStream ms = new MemoryStream(buffer);

Bitmap = new Bitmap(ms);

Does this seem like the correct way to send the Bitmap over my Socket?

I also found that saving the Bitmap as a PNG before sending makes it
smaller. Is this a good idea, and can I compress it even more?
bm.Save(ms, System.Drawing.Imaging.ImageFormat.Png);


Thanks so much for reading a question from a newbie. I know this might
not make sense to some of you, or seem trivial.
 
D

Dave Sexton

Hi,

This should work fine if you disconnect on the client immediately after the
Bitmap has been sent, without sending any other data:

byte[] bytes = new byte[1024];
int read = 0;

using (MemoryStream bitmapStream = new MemoryStream())
{
// stream is NetworkStream

while ((read = stream.Read(bytes, 0, 1024)) != 0)
// read = 0 indicates that the Socket was closed
bitmapStream.Write(bytes, 0, read);

// reset position to beginning of stream
bitmapStream.Position = 0;

ShownWindow.BackgroundImage =
Bitmap.FromStream(bitmapStream);
}


--
Dave Sexton

Sorry, Lenard,

stream is a NetworkStream by the way

Hi Lenarf,

Thanks for the quick response.

I am trying to get the ReceiveBitmap part working using your example. I
had a working solution and in mine, I replaced

byte[] buffer = new byte[1000000];
stream.Read(buffer, 0, buffer.Length);
MemoryStream ms = new MemoryStream(buffer);
ShownWindow.BackgroundImage = Bitmap.FromStream(ms);

with

ShownWindow.BackgroundImage = Bitmap.FromStream(stream);

in order to cut out two middlemen, but then it stops working. Any idea
why?


Lenard said:
Hi,

I played a little with this problem, and came up with the following
solution.

For sending, you could use:

TcpListener listener = new TcpListener ( 9998 );
listener.Start ();
TcpClient client = listener.AcceptTcpClient ();
NetworkStream stream = client.GetStream ();

Image image = Image.FromFile ( @"..file.." );
using ( MemoryStream ms = new MemoryStream () )
{
image.Save ( ms, ImageFormat.Png );
byte[] imageBuffer = ms.GetBuffer ();
stream.Write ( imageBuffer, 0, (int)ms.Length );
}

stream.Close ( 500 );
client.Close ();
listener.Stop ();

Here you don't have to worry about the buffer, because MemoryStream
will
handle it for you. For some reason you cannot use the NetworkStream
directly (to save the image into that). GDI+ throws an exception if you
try that. I guess it has something to do with network streams not being
searchable (but that is just a guess).

On the receiving end, you can however read directly from the
NetworkStream. So you could create a method like:

private Image ReceiveBitmap ()
{
TcpClient client = new TcpClient ();
client.Connect ( "127.0.0.1", 9998 );
NetworkStream stream = client.GetStream ();

Image image = Image.FromStream ( stream );

stream.Close ();
client.Close ();

return image;
}

This will read your image and return it to the caller. As you can see
you do not need to worry about the length of the buffer, because the
image loader will read the correct number of bytes.

Note however, that I did not do any error checking above.

Another way to solve the problem is to not send just the image onto the
network stream, rather first the size (say an Int32) and then the
contents of the buffer. On the receiving end you would then always read
an Int32 (the length), allocate a buffer that fits the data, and then
receive into that buffer. That way you allocate just as much space as
you need.

Hope this helps

-Lenard




(e-mail address removed) wrote:
I'm using TCP/IP to send a Bitmap object over Sockets. This is my
first
time using C# at all so I don't know if this is the "right" way to do
it. I've already found out several times the way I was doing
something
was really inefficient and could reduce 10 lines of code with 2, etc.

For reading, I am using a TcpClient and I call

NetworkStream ns = client.GetStream(); to get a stream

stream.Read(buffer, 0, buffer.Length);

now already my question is, how do I make buffer so that I don't have
to arbitrarily make it 1 MB? What if my Bitmap is larger than 1 MB?
Do
I need to read multiple times and then "join" the buffers? How would
I
do that?

MemoryStream ms = new MemoryStream(buffer);

Bitmap = new Bitmap(ms);

Does this seem like the correct way to send the Bitmap over my
Socket?

I also found that saving the Bitmap as a PNG before sending makes it
smaller. Is this a good idea, and can I compress it even more?
bm.Save(ms, System.Drawing.Imaging.ImageFormat.Png);


Thanks so much for reading a question from a newbie. I know this
might
not make sense to some of you, or seem trivial.
 
E

eliss.carmine

Thanks for the example Dave,

I'm currently workin to mesh this and my current application together.

Basically, I'm trying to create a Application Sharing program. Where
every 100ms, it does a PrintWindow of a window and then sends it over
the network and the other side replays that into a window. So it's like
Microsoft Remote Desktop but just for applications.

Dave said:
Hi,
I'm using TCP/IP to send a Bitmap object over Sockets. This is my first
time using C# at all so I don't know if this is the "right" way to do
it. I've already found out several times the way I was doing something
was really inefficient and could reduce 10 lines of code with 2, etc.

For reading, I am using a TcpClient and I call

NetworkStream ns = client.GetStream(); to get a stream

stream.Read(buffer, 0, buffer.Length);

now already my question is, how do I make buffer so that I don't have
to arbitrarily make it 1 MB? What if my Bitmap is larger than 1 MB? Do
I need to read multiple times and then "join" the buffers? How would I
do that?

MemoryStream ms = new MemoryStream(buffer);

Bitmap = new Bitmap(ms);

Does this seem like the correct way to send the Bitmap over my Socket?

I also found that saving the Bitmap as a PNG before sending makes it
smaller. Is this a good idea, and can I compress it even more?
bm.Save(ms, System.Drawing.Imaging.ImageFormat.Png);


Thanks so much for reading a question from a newbie. I know this might
not make sense to some of you, or seem trivial.

Network programming is never trivial ;)

First, I have to ask, why do you want to do that? (There may be a better
way)

Next, try the code after my signature if you really need to use Sockets.
Beware there is no error handling logic, which I'm sure you'll want to
implement otherwise the app could easily crash.

--
Dave Sexton

using System;
using System.Net.Sockets;
using System.Threading;
using System.Drawing;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Net;

namespace Testing
{
class Program
{
const int port = 9483; // arbitrary

static void Main(string[] args)
{
StartServer();

SendBitmap();

Console.WriteLine("Press [enter] to exit");
Console.ReadLine();
}

static void SendBitmap()
{
Bitmap bitmap = new Bitmap(300, 500);
byte[] bytes = null;

Console.WriteLine("Client: Created Bitmap: {0}x{1}", bitmap.Width,
bitmap.Height);

Console.WriteLine("Client: Serializing Bitmap");

using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, bitmap);

bytes = stream.ToArray();
}

Console.WriteLine("Client: Connecting to Server");

using (TcpClient client = new TcpClient("localhost", port))
{
Console.WriteLine("Client: Sending Bitmap");

client.GetStream().Write(bytes, 0, bytes.Length);
}

Console.WriteLine("Client: Bitmap Sent");
}

static Bitmap AcceptBitmap(TcpClient client)
{
NetworkStream stream = client.GetStream();

byte[] bytes = new byte[1024];
int read = 0;

using (MemoryStream bitmapStream = new MemoryStream())
{
Console.WriteLine("Server: Receiving Bitmap");

while ((read = stream.Read(bytes, 0, 1024)) != 0)
// read = 0 indicates that the Socket was closed
bitmapStream.Write(bytes, 0, read);

// reset position to beginning of stream
bitmapStream.Position = 0;

Console.WriteLine("Server: Bitmap Received");

BinaryFormatter formatter = new BinaryFormatter();

Console.WriteLine("Server: Deserializing Bitmap");

return (Bitmap) formatter.Deserialize(bitmapStream);
}
}

static void StartServer()
{
TcpListener listener = new TcpListener(IPAddress.Loopback, port);

Thread thread = new Thread(delegate()
// the listener will run in the background
{
listener.Start();

Bitmap bitmap = null;

using (TcpClient client = listener.AcceptTcpClient())
{
Console.WriteLine("Server: Client Connected");

bitmap = AcceptBitmap(client);

Console.WriteLine("Server: Received Bitmap: {0}x{1}", bitmap.Width,
bitmap.Height);
}

listener.Stop();
});

thread.IsBackground = true;
thread.Start();

Console.WriteLine("Server: Server Started");
}
}
}
 
E

eliss.carmine

I think this works pretty good. I'm satisfied with the receiving parts.
I have this in a while loop without closing the streams or sockets.
Thanks!

I guess the next step is being able to pass short messages (the
clickpoints) from the server to the client.

Am I going to be using client in:

TcpClient client = listener.AcceptTcpClient();

to get a stream to send data from the server?


And from the client's side, I will read from the memorystream to get
data that the server sent?


Dave said:
Hi,

This should work fine if you disconnect on the client immediately after the
Bitmap has been sent, without sending any other data:

byte[] bytes = new byte[1024];
int read = 0;

using (MemoryStream bitmapStream = new MemoryStream())
{
// stream is NetworkStream

while ((read = stream.Read(bytes, 0, 1024)) != 0)
// read = 0 indicates that the Socket was closed
bitmapStream.Write(bytes, 0, read);

// reset position to beginning of stream
bitmapStream.Position = 0;

ShownWindow.BackgroundImage =
Bitmap.FromStream(bitmapStream);
}


--
Dave Sexton

Sorry, Lenard,

stream is a NetworkStream by the way

Hi Lenarf,

Thanks for the quick response.

I am trying to get the ReceiveBitmap part working using your example. I
had a working solution and in mine, I replaced

byte[] buffer = new byte[1000000];
stream.Read(buffer, 0, buffer.Length);
MemoryStream ms = new MemoryStream(buffer);
ShownWindow.BackgroundImage = Bitmap.FromStream(ms);

with

ShownWindow.BackgroundImage = Bitmap.FromStream(stream);

in order to cut out two middlemen, but then it stops working. Any idea
why?


Lenard Gunda wrote:
Hi,

I played a little with this problem, and came up with the following
solution.

For sending, you could use:

TcpListener listener = new TcpListener ( 9998 );
listener.Start ();
TcpClient client = listener.AcceptTcpClient ();
NetworkStream stream = client.GetStream ();

Image image = Image.FromFile ( @"..file.." );
using ( MemoryStream ms = new MemoryStream () )
{
image.Save ( ms, ImageFormat.Png );
byte[] imageBuffer = ms.GetBuffer ();
stream.Write ( imageBuffer, 0, (int)ms.Length );
}

stream.Close ( 500 );
client.Close ();
listener.Stop ();

Here you don't have to worry about the buffer, because MemoryStream
will
handle it for you. For some reason you cannot use the NetworkStream
directly (to save the image into that). GDI+ throws an exception if you
try that. I guess it has something to do with network streams not being
searchable (but that is just a guess).

On the receiving end, you can however read directly from the
NetworkStream. So you could create a method like:

private Image ReceiveBitmap ()
{
TcpClient client = new TcpClient ();
client.Connect ( "127.0.0.1", 9998 );
NetworkStream stream = client.GetStream ();

Image image = Image.FromStream ( stream );

stream.Close ();
client.Close ();

return image;
}

This will read your image and return it to the caller. As you can see
you do not need to worry about the length of the buffer, because the
image loader will read the correct number of bytes.

Note however, that I did not do any error checking above.

Another way to solve the problem is to not send just the image onto the
network stream, rather first the size (say an Int32) and then the
contents of the buffer. On the receiving end you would then always read
an Int32 (the length), allocate a buffer that fits the data, and then
receive into that buffer. That way you allocate just as much space as
you need.

Hope this helps

-Lenard




(e-mail address removed) wrote:
I'm using TCP/IP to send a Bitmap object over Sockets. This is my
first
time using C# at all so I don't know if this is the "right" way to do
it. I've already found out several times the way I was doing
something
was really inefficient and could reduce 10 lines of code with 2, etc.

For reading, I am using a TcpClient and I call

NetworkStream ns = client.GetStream(); to get a stream

stream.Read(buffer, 0, buffer.Length);

now already my question is, how do I make buffer so that I don't have
to arbitrarily make it 1 MB? What if my Bitmap is larger than 1 MB?
Do
I need to read multiple times and then "join" the buffers? How would
I
do that?

MemoryStream ms = new MemoryStream(buffer);

Bitmap = new Bitmap(ms);

Does this seem like the correct way to send the Bitmap over my
Socket?

I also found that saving the Bitmap as a PNG before sending makes it
smaller. Is this a good idea, and can I compress it even more?
bm.Save(ms, System.Drawing.Imaging.ImageFormat.Png);


Thanks so much for reading a question from a newbie. I know this
might
not make sense to some of you, or seem trivial.
 
D

Dave Sexton

Hi,

Interesting idea. Maybe this information will help you (deals with Net
Meeting and Video conferencing APIs):

http://groups.google.com/group/micr...55d024?as_umsgid=ubDOASNiCHA.1428@tkmsftngp11

GL.

--
Dave Sexton

Thanks for the example Dave,

I'm currently workin to mesh this and my current application together.

Basically, I'm trying to create a Application Sharing program. Where
every 100ms, it does a PrintWindow of a window and then sends it over
the network and the other side replays that into a window. So it's like
Microsoft Remote Desktop but just for applications.

Dave said:
Hi,
I'm using TCP/IP to send a Bitmap object over Sockets. This is my first
time using C# at all so I don't know if this is the "right" way to do
it. I've already found out several times the way I was doing something
was really inefficient and could reduce 10 lines of code with 2, etc.

For reading, I am using a TcpClient and I call

NetworkStream ns = client.GetStream(); to get a stream

stream.Read(buffer, 0, buffer.Length);

now already my question is, how do I make buffer so that I don't have
to arbitrarily make it 1 MB? What if my Bitmap is larger than 1 MB? Do
I need to read multiple times and then "join" the buffers? How would I
do that?

MemoryStream ms = new MemoryStream(buffer);

Bitmap = new Bitmap(ms);

Does this seem like the correct way to send the Bitmap over my Socket?

I also found that saving the Bitmap as a PNG before sending makes it
smaller. Is this a good idea, and can I compress it even more?
bm.Save(ms, System.Drawing.Imaging.ImageFormat.Png);


Thanks so much for reading a question from a newbie. I know this might
not make sense to some of you, or seem trivial.

Network programming is never trivial ;)

First, I have to ask, why do you want to do that? (There may be a better
way)

Next, try the code after my signature if you really need to use Sockets.
Beware there is no error handling logic, which I'm sure you'll want to
implement otherwise the app could easily crash.

--
Dave Sexton

using System;
using System.Net.Sockets;
using System.Threading;
using System.Drawing;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Net;

namespace Testing
{
class Program
{
const int port = 9483; // arbitrary

static void Main(string[] args)
{
StartServer();

SendBitmap();

Console.WriteLine("Press [enter] to exit");
Console.ReadLine();
}

static void SendBitmap()
{
Bitmap bitmap = new Bitmap(300, 500);
byte[] bytes = null;

Console.WriteLine("Client: Created Bitmap: {0}x{1}", bitmap.Width,
bitmap.Height);

Console.WriteLine("Client: Serializing Bitmap");

using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, bitmap);

bytes = stream.ToArray();
}

Console.WriteLine("Client: Connecting to Server");

using (TcpClient client = new TcpClient("localhost", port))
{
Console.WriteLine("Client: Sending Bitmap");

client.GetStream().Write(bytes, 0, bytes.Length);
}

Console.WriteLine("Client: Bitmap Sent");
}

static Bitmap AcceptBitmap(TcpClient client)
{
NetworkStream stream = client.GetStream();

byte[] bytes = new byte[1024];
int read = 0;

using (MemoryStream bitmapStream = new MemoryStream())
{
Console.WriteLine("Server: Receiving Bitmap");

while ((read = stream.Read(bytes, 0, 1024)) != 0)
// read = 0 indicates that the Socket was closed
bitmapStream.Write(bytes, 0, read);

// reset position to beginning of stream
bitmapStream.Position = 0;

Console.WriteLine("Server: Bitmap Received");

BinaryFormatter formatter = new BinaryFormatter();

Console.WriteLine("Server: Deserializing Bitmap");

return (Bitmap) formatter.Deserialize(bitmapStream);
}
}

static void StartServer()
{
TcpListener listener = new TcpListener(IPAddress.Loopback, port);

Thread thread = new Thread(delegate()
// the listener will run in the background
{
listener.Start();

Bitmap bitmap = null;

using (TcpClient client = listener.AcceptTcpClient())
{
Console.WriteLine("Server: Client Connected");

bitmap = AcceptBitmap(client);

Console.WriteLine("Server: Received Bitmap: {0}x{1}", bitmap.Width,
bitmap.Height);
}

listener.Stop();
});

thread.IsBackground = true;
thread.Start();

Console.WriteLine("Server: Server Started");
}
}
}
 
D

Dave Sexton

Hi,
I think this works pretty good. I'm satisfied with the receiving parts.
I have this in a while loop without closing the streams or sockets.
Thanks!

I guess the next step is being able to pass short messages (the
clickpoints) from the server to the client.

Am I going to be using client in:

TcpClient client = listener.AcceptTcpClient();

to get a stream to send data from the server?

You only need to establish the connection once. You can send multiple
"pieces" of data across the wire, serially, instead of having to close and
reopen the connection each time.

So the above code can be used to establish a connection with the client
(from the server) and you can keep the client reference until it is no
longer needed.

Since our examples in this thread assumed that you were going to be closing
the client Socket immediately after the data was sent, we could simply call
Bitmap.FromStream(networkStream) or check that stream.Read returns 0,
indicating that the Socket was closed. However, now that the Socket is
going to remain open you'll need another way to find the end of each
"logical" stream (i.e., where one call to Send ends and the next one begins)

The easiest way to do that, IMO, is to pad each Send with 4 bytes of data
that indicates the size of the stream to come. It will be a data header
that can be read by the server each time data is received. Since you're
going to want high performance, I assume, I won't recommend serialization
(although I normally would). Instead, you might want to expand the data
header in each Send to include certain predetermined byte sequences that let
the client/server know what type of data is being sent. Then, instead of a
serialized object graph you could just use the BitConverter class or
BinaryWriter to convert your application's data into small byte arrays that
can be written directly to, or read directly from, the NetworkStream.

When reading, you shouldn't check that Read returns 0. Read will block
until data is present to be read on the Socket and should not return 0 until
the socket is closed. However, you should perform the blocking read on a
different thread if you want to allow other clients to connect to the
server, asynchronously.

There's a lot to think about here. That's why I originally asked why you
wanted to do this. But in light of your answer, it sounds like you're going
to need all of the performance you can get, so any other suggestions I may
have had, aside from using Win32 APIs directly, would now be inappropriate I
think (e.g., to use .NET Remoting instead, for example).
And from the client's side, I will read from the memorystream to get
data that the server sent?

In the same way as I described above, yes.

I've included some sample code after my signature.

--
Dave Sexton

const int port = 9483; // arbitrary

public static void Run()
{
StartServer();

using (TcpClient client = new TcpClient("localhost", port))
{
SendPoint(new Point(453, 391), client);
SendPoint(new Point(103, 13), client);
}

Console.WriteLine("Press [enter] to exit");
Console.ReadLine();
}

private static void SendPoint(Point point, TcpClient client)
{
Console.WriteLine("Client: Sending Point: " + point);

NetworkStream stream = client.GetStream();

// note that you can buffer this operation if you want by
// wrapping stream with a BufferedStream instance

BinaryWriter writer = new BinaryWriter(stream);

// write the header first

// first 4 bytes = size of data = 8 bytes (int X, int Y)
writer.Write(8);

// next byte indicates Point data (I arbitrarily chose 1)
writer.Write((byte) 1);

// write data
writer.Write(point.X);
writer.Write(point.Y);

Console.WriteLine("Client: Point Sent");
}

private static Point AcceptPoint(TcpClient client)
{
NetworkStream stream = client.GetStream();

BinaryReader reader = new BinaryReader(stream);

// read header first

// first 4 bytes = size of data
int size = reader.ReadInt32();

// next byte indicates type of data (Point = 1)
byte dataType = reader.ReadByte();

if (dataType != 1)
throw new InvalidOperationException(
"Server received invalid data type byte mark: " + dataType);

// point data is sent as (int X, int Y)
return new Point(reader.ReadInt32(), reader.ReadInt32());
}

private static void StartServer()
{
TcpListener listener = new TcpListener(IPAddress.Loopback, port);

Thread thread = new Thread(delegate()
// the listener will run in the background
{
listener.Start();

using (TcpClient client = listener.AcceptTcpClient())
{
Console.WriteLine("Server: Client Connected");

Point point = AcceptPoint(client);

Console.WriteLine("Server: Received Point: {0}", point);

point = AcceptPoint(client);

Console.WriteLine("Server: Received Point: {0}", point);
}

listener.Stop();

Console.WriteLine("Server: Server Stopped");
});

thread.IsBackground = true;
thread.Start();

Console.WriteLine("Server: Server Started");
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top