CSharp/C# >> read from TCP/IP socket with blocking

by Julie » Fri, 22 Jan 2010 06:41:45 GMT

So, I currently have code for reading in TCP/IP messages that looks
something like this:

StreamReader reader = new StreamReader(new NetworkStream(mySocket)));
while (true)
{
string inputString = reader.ReadLine();
_newMessages.Enqueue(inputString);
}

This is one thread in my application, and as it is a non-blocking
infinite loop, it is causing my application to consume 100% of my
CPU.

Is there a simple method of rewriting this to cause it to block when
there's nothing else to read? (thereby freeing up CPU)

I attempted to use StreamReader.ReadBlock(), but then realized that
this really doesn't block when the stream is empty either.

Thanks!

CSharp/C# >> read from TCP/IP socket with blocking

by Andrew Poelstra » Fri, 22 Jan 2010 07:57:38 GMT



You can create (a single) thread and use Thread.Sleep(). This also
lets you expand to allow multiple connections easily.

CSharp/C# >> read from TCP/IP socket with blocking

by Peter Duniho » Fri, 22 Jan 2010 08:08:59 GMT


That doesn't make sense. StreamReader.ReadLine() blocks until input is
available from the underlying Stream, which in turn should block until
input is available. It doesn't poll the socket (or at least, it should
noty experience is that it does not).


You should post a concise-but-complete code example that demonstrates
the 100% CPU usage. I suspect that the problem is not in the code
you've shown, as StreamReader should be using the NetworkStream in a
blocking manner.

Pete

CSharp/C# >> read from TCP/IP socket with blocking

by Peter Duniho » Fri, 22 Jan 2010 08:38:58 GMT


The Thread.Sleep() method is primarily for when a thread knows it has a
specific length of time it has to sleep. It can be used for yielding,
but that's almost always incorrect design.

In this particular example, there's nothing in the code posted to
suggest the thread isn't blocking correctly already, and all that
calling Thread.Sleep() will do is slow that thread down. Sure, it'll
use less CPU, but it'll do less work over a given amount of time too.

Pete

CSharp/C# >> read from TCP/IP socket with blocking

by Andrew Poelstra » Fri, 22 Jan 2010 11:08:05 GMT


The code looked okay, but the OP claimed that it was spinning the CPU
and that a delay would be nice.

One project I did used Thread.Sleep() after some packets were sent
deliberately to slow the computer down, and give the client time
to process the request.

CSharp/C# >> read from TCP/IP socket with blocking

by Peter Duniho » Fri, 22 Jan 2010 13:18:11 GMT


She did claim that.


I don't think she did claim that.

I agree that the code as shown looks fine. Hence, IMHO we should not be
suggesting changes to that. Instead, we should insist on seeing the
code that's _not_ fine before making specific suggestions.


There are situations when it might be beneficial to throttle output.
For example, bandwidth restrictions on the recipient, or you'd like to
reduce network congestion when using UDP.

But those kinds of situations fall exactly into the category I described
as a valid use of Thread.Sleep(): you have a specific, known amount of
time for which it's important for the thread to delay.

There's no indication that the OP in this case would benefit from the
use of Thread.Sleep().

Pete

CSharp/C# >> read from TCP/IP socket with blocking

by Julie » Sat, 23 Jan 2010 04:08:05 GMT

Hey guys,

So sorry - I posted this and then completely forgot to check the
responses! Thanks for responding.

I would not like to sleep in this thread because it actually does need
to operate as quickly as possible to process all of the TCP/IP input.

I was able to narrow it down to this thread causing my CPU usage. The
CPU was at 100%, then I froze the thread, and the CPU went back down.

That's interesting, Peter, that you believe ReadLine does block when
there's no incoming TCP/IP.

The code I posted that I'm using wasn't actually complete. Here is the
actual:

StreamReader reader = new StreamReader(new NetworkStream(mySocket)));
while (true)
{
string inputString = reader.ReadLine();
if (inputString != null)
{
_newMessages.Enqueue(inputString);
}
}

So, I'm actually checking for null, and I find that if there is no
incoming message, ReadLine() does not block and instead returns null.

Peter, are you able to run this same thing and find that ReadLine()
will block when there are no new messages? I can try to provide more
code if that's helpful.

Thanks! (and I promise this time not to forget about my post. :o)

CSharp/C# >> read from TCP/IP socket with blocking

by Peter Duniho » Sat, 23 Jan 2010 04:30:00 GMT


Ah. So it is possible in that loop for ReadLine() to return null.

ReadLine() returns null when you've reached the end of the stream. So,
basically your code causes the thread to go into an infinite loop once
reaching the end of the stream.

And yes, not only will that prevent your thread from ever getting any
further than that loop, but once reaching the end of the stream there is
nothing to restrict the execution of the thread, so CPU usage will hit 100%.

Without more context, it's not possible to know exactly what the right
thing to do is. But, given that the TCP connection can eventually be
closed, somehow you need to handle that appropriately.

That might be as simple as just exiting the thread when the return value
is null:

StreamReader reader = new StreamReader(new NetworkStream(mySocket));
string str;

while ((str = reader.ReadLine()) != null)
{
_newMessages.Enqueue(inputString);
}

But like I said, not possible to say for sure with the given information.

Pete

CSharp/C# >> read from TCP/IP socket with blocking

by Peter Duniho » Sat, 23 Jan 2010 04:37:24 GMT


Of course, the above only works if you use the same variable name for
the "string" variable everywhere. Should be "str" or "inputString"
only; mixing and matching won't compile. :)

Sorry for my sloppiness.

Pete

CSharp/C# >> read from TCP/IP socket with blocking

by Julie » Sat, 23 Jan 2010 04:49:54 GMT

On Jan 22, 12:37m, Peter Duniho < XXXX@XXXXX.COM >


> > string str; >> >>>> while ((str = reader.ReadLine()) != null)> > > > >> > _newMessages.Enqueue(inputStrin>)>
> >>>
>
> Of course, the above only works if you use the same variable name>for
> the "string" variable everywhere. hould be "str" or "inputStr>ng"
> only; mixing and matching won't compile> gt;)
>
> Sorry for my sloppi>es>.
>
> Pete

But what if the client takes a break from sending but still has more
to send? I don't want to stop checking for messages, but I don't want
to use up CPU either. Hmm...

CSharp/C# >> read from TCP/IP socket with blocking

by Julie » Sat, 23 Jan 2010 04:52:43 GMT

Perhaps, if I get null, then I can sleep; and if I don't get null, I
won't sleep(?).

CSharp/C# >> read from TCP/IP socket with blocking

by Julie » Sat, 23 Jan 2010 04:58:15 GMT


Awesome! I just implemented this, and the CPU's no longer at 100%!

Thanks, everyone, for your ideas.

CSharp/C# >> read from TCP/IP socket with blocking

by Peter Duniho » Sat, 23 Jan 2010 05:06:33 GMT


As you may or may not have figured out by now, there's a big difference
between the end-of-stream, and temporary cessation of data being sent.

If the client "takes a break" by simply not sending more data,
ReadLine() will block. If the client "takes a break" by closing the
connection, then no more data will EVER arrive on that connection, and
ReadLine() will return null. In the latter case, there is no point in
continuing the loop. You'll NEVER see any more data returned by the
ReadLine() method of that instance of StreamReader.

If you have, as you seem to be implying in your subsequent replies,
added a call to Thread.Sleep() to your code, then you have NOT fixed the
problem correctly. You do NOT need Thread.Sleep() in this situation.

Pete

CSharp/C# >> read from TCP/IP socket with blocking

by Julie » Sat, 23 Jan 2010 05:15:12 GMT

On Jan 22, 1:06m, Peter Duniho < XXXX@XXXXX.COM >




Ahhh...I didn't realize that null meant the client had closed the
connection. Thanks for the clarification.

CSharp/C# >> read from TCP/IP socket with blocking

by Julie » Sat, 23 Jan 2010 05:20:42 GMT

Okay, so I've implemented what you recommended (only reading for as
long as inputString != null) - looks good! CPU is normal.

Similar Threads

1. Asynchronous image transfer over TCP/IP socket

Hi every1,

I am writing a small client/server application and in it I want to
send an image asynchronous from the client to the server through a TCP
socket. I found an example code on the MSDN site, which is actually
for sending strings. I tried to adapt this code so that the client
sends an image instead of a string. However, there is something wrong
on the server side (i guess)...
The server starts listening, the client starts sending and I become an
indication, that the server receives something... but doesn't stop
receiving. The else-condition in the function Send_Image() is never
reached.

As far as I understood the Microsoft-code, the data is being sent in
parts (every part is with the size of the buffer). That's why the
Read_Image() function is called as long as all the data is transfered
(that is exactly the if-condition ->  if bytesRead>0 ). I save these
data parts (in bytes) in an ArrayList and at the end I convert this
ArrayList to a byte array and then to a Bitmap.

Here is the code. Can anyone help me to find my mistake ... please, I
am stuck!!! :(


The client side:

public static void StartClient()
        {
            // Connect to a remote device.
            try
            {
                // Establish the remote endpoint for the socket.
		// The name of the remote device is "host.domain.com".
                IPHostEntry ipHostInfo =
Dns.Resolve("host.domain.com");
                IPAddress ipAddress = ipHostInfo.AddressList[0];
                IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);

                // Create a TCP/IP socket.
                Socket client = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.Tcp);

                // Connect to the remote endpoint.
                client.BeginConnect(remoteEP,
                    new AsyncCallback(ConnectCallback), client);
                connectDone.WaitOne();

                // Send the image to the remote device.
                string fName = @"\Program Files\Client\image.jpg"; //
path to image
                SendImage(client, new Bitmap(fName));
                sendDone.WaitOne();

                // Receive the response from the remote device.
                Receive(client);
                receiveDone.WaitOne();

                // Write the response to the console.
                Console.WriteLine("Response received : {0}",
response);
                //System.Windows.Forms.MessageBox.Show("Otgovor
poluchen");

                // Release the socket.
                client.Shutdown(SocketShutdown.Both);
                client.Close();

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
//--------------------------------------------------------------------
        //convet Image to byte[]
        public static byte[] imageToByteArray(Bitmap imageIn)
        {
            MemoryStream ms = new MemoryStream();
            imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            return ms.GetBuffer();
        }
//--------------------------------------------------------------------
        private static void SendImage(Socket client, Bitmap img)
        {
            // Convert the image to byte data
            byte[] byteData = imageToByteArray(img);

            // Begin sending the data to the remote device.
            client.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), client);
        }
//--------------------------------------------------------------------
        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket client = (Socket)ar.AsyncState;

                // Complete sending the data to the remote device.
                int bytesSent = client.EndSend(ar);
                Console.WriteLine("Sent {0} bytes to server.",
bytesSent);  //this msg never shows up!

                // Signal that all bytes have been sent.
                sendDone.Set();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }


The server side:

     // State object for reading client data asynchronously
public class StateObject {
    // Client  socket.
    public Socket workSocket = null;
    // Size of receive buffer.
    public const int BufferSize = 1024*10;
    // Receive buffer.
    public byte[] buffer = new byte[BufferSize];
// Received image bytes
    public ArrayList imageBytes = new ArrayList();
}
//----------------------------------------------------------------------
    public static void StartListening() {
        // Data buffer for incoming data.
        byte[] bytes = new Byte[1024];

        // Establish the local endpoint for the socket.
        // The DNS name of the computer running the listener is
"server.domain.com".
        IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
        IPAddress ipAddress = ipHostInfo.AddressList[0];
        IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);

        // Create a TCP/IP socket.
        Socket listener = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream, ProtocolType.Tcp );

        // Bind the socket to the local endpoint and listen for
incoming connections.
        try {
            listener.Bind(localEndPoint);
            listener.Listen(100);

            while (true) {
                // Set the event to nonsignaled state.
                allDone.Reset();

                // Start an asynchronous socket to listen for
connections.
                System.Windows.Forms.MessageBox.Show("Waiting for a
connection...");
                listener.BeginAccept(
                    new AsyncCallback(AcceptCallback),
                    listener );

                // Wait until a connection is made before continuing.
                allDone.WaitOne();
            }

        } catch (Exception e) {
            Console.WriteLine(e.ToString());
            System.Windows.Forms.MessageBox.Show(e.ToString());
        }

        System.Windows.Forms.MessageBox.Show("\nPress ENTER to
continue...");
        Console.Read();

    }
//----------------------------------------------------------------------
    public static void AcceptCallback(IAsyncResult ar) {
        // Signal the main thread to continue.
        allDone.Set();

        // Get the socket that handles the client request.
        Socket listener = (Socket) ar.AsyncState;
        Socket handler = listener.EndAccept(ar);

        // Create the state object.
        StateObject state = new StateObject();
        state.workSocket = handler;
        handler.BeginReceive( state.buffer, 0, StateObject.BufferSize,
0,
            new AsyncCallback(Read_Image), state);
}
//--------------------------------------------------------
    public static void Read_Image(IAsyncResult ar)
    {
        StateObject so = (StateObject)ar.AsyncState;
        Socket s = so.workSocket;

        int read = s.EndReceive(ar);

        if (read > 0)
        {
            //so.sb.Append(Encoding.ASCII.GetString(so.buffer, 0,
read));
            foreach (byte el in so.buffer)
            {
                so.imageBytes.Add(el);
            }
            s.BeginReceive(so.buffer, 0, StateObject.BufferSize, 0,new
AsyncCallback(Read_Image), so);
            System.Windows.Forms.MessageBox.Show("read > 0, call
Read_Image recursively");
        }
        else
        {
            System.Windows.Forms.MessageBox.Show("Else case");
            if (so.imageBytes.Count > 0)
            {
                //All of the data has been read, so displays it to the
console
                byte[] imageBytesArray = new
byte[so.imageBytes.Count];
                so.imageBytes.CopyTo(imageBytesArray);
                myForm.pictureBox1.Image =
ImgConverter.byteArrayToImage(imageBytesArray);
                //Send(s, "<EOF>");
                //System.Windows.Forms.MessageBox.Show("Sent!");
            }
            System.Windows.Forms.MessageBox.Show("Receive done!");
            Send(s, "<EOF>");  //this sends a msg to the client and
closes the socket connection. I didn't paste the function
            s.Close();
        }
    }




Thanks in advance!

Cheers,
Vasil

2. C# and TCP/IP sockets - CSharp/C#

3. File Transfer over a LAN via TCP/IP Sockets

Can anyone help me out how to transfer multiple files  over a local network 
via TCP /IP from a server to a client..using TCP Listener and Client..Any 
good resource would be helpful...

4. Implementing an Http layer over a TCP/IP Socket - CSharp/C#

5. Where to Post C# TCP/IP Socket questions

Is this the right forum to post questions concerning TCP/IP Socket questions?

6. C++ stream-compatible TCP/IP sockets (Demo application)

7. looking for code sample for non-blocking TCP sockets

Hi,

I need a code sample for doing this:

-  support for multiple agents that know each other and can talk each other
   over a non-blocking TCP socket.
   each agent can send and recieve data to/from each other.
-  handling agent down events (i don't know how to do it in an elegant way 
when using the socket select method).

I prefer to use the Socket.Select method.
All the socket operations must be in a non-blocking mode.

Thanks.

8. TCP Socket class send() never blocks... - CSharp/C#