10 Replies - 1693 Views - Last Post: 14 July 2012 - 07:23 AM Rate Topic: -----

#1 whunter1955  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 13-July 12

Asynchronous Client Socket ManualResetEvent holding up execution

Posted 13 July 2012 - 12:21 PM

May I ask for help?

I am attempting to utilize MSDN's Asynchronous Client Socket Example code sample to connect and control some home equipment. As I understand, the sample code's ReceiveCallback method uses an instance of the EventWaitHandle ManualResetEvent and the method receiveDone.WaitOne() to hold processing of the current thread until the thread receives a signal that all of the socket's data has been transmitted from the remote device. After all of the socket's data has been transmitted (the socket's data is empty and bytesRead = 0), the Waithandle is removed and the application continues processing.

Unfortunately, by stepping-through the execution of the code, it appears that after the last time that the client returns data from the remote device, ReceiveCallback never returns to see if the data-queue is empty (i.e. bytesRead = 0), and thus never enters the "else" condition in ReceiveCallback where the state of the ManualResetEvent would have been reset and releasing the application to continue processing. Thus, since it never enters the "else" condition, *ManualResetEvent* is never reset and the application freezes.

Although I can remove the "receiveDone.WaitOne()" method from the code - permitting execution without waiting for the ManualResetEvent's notification that all of the data has been received; this returns a data-string from the equipment that is typically incomplete.

An example of the data returned from the equipment would be as follows:
"!
ZMP,0001000000000000000X00XXXXXXXXXX,S1
ZMP,0000000000000100000000XXXXXXXXXX,S2
LMP,000010000000000
"
Am I using this code sample incorrectly? Has anyone seen this before or had any experience on how to work-around this issue? Any advice?
public static Socket LutronClient;
public static String LutronResponse = String.Empty;
private const int LutronPort = 4999;
private const string LutronIP = "192.168.1.71";
private static ManualResetEvent LutronConnectDone = new ManualResetEvent(false);
private static ManualResetEvent LutronSendDone = new ManualResetEvent(false);
private static ManualResetEvent LutronReceiveDone = new ManualResetEvent(false);

private static void StartLutronClient()
        {
            try
            {
                var lutronIPAddress = IPAddress.Parse(LutronIP);
                var lutronRemoteEP = new IPEndPoint(lutronIPAddress, LutronPort);
                LutronClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                LutronClient.BeginConnect(lutronRemoteEP, LutronConnectCallback, LutronClient);
                LutronConnectDone.WaitOne();

                LutronSend(LutronClient, "sdl,14,100,0,S2\x0d");
                LutronSendDone.WaitOne();
                LutronReceive(LutronClient);
                LutronReceiveDone.WaitOne(new TimeSpan(5000));
                MessageBox.Show("Response received from Lutron: " + LutronResponse);
                txtBoxLutron.Text = LutronResponse;

                LutronClient.Shutdown(SocketShutdown.Both);
                LutronClient.Close();
            }
            catch (Exception e) { MessageBox.Show(e.ToString()); }
        }
        
        private static void LutronConnectCallback(IAsyncResult lutronAr)
        {
            try
            {
                var lutronClient = (Socket)lutronAr.AsyncState;
                lutronClient.EndConnect(lutronAr);
                LutronConnectDone.Set();
            }
            catch (Exception e) { MessageBox.Show(e.ToString()); }
        }

        private static void LutronReceive(Socket lutronClient)
        {
            try
            {
                var lutronState = new LutronStateObject { LutronWorkSocket = lutronClient };
                lutronClient.BeginReceive(lutronState.LutronBuffer, 0, LutronStateObject.BufferSize, 0, new AsyncCallback(LutronReceiveCallback), lutronState);
            }
            catch (Exception e) { MessageBox.Show(e.ToString()); }
        }

        private static void LutronReceiveCallback(IAsyncResult lutronAR)
        {
            try
            {
                var lutronState = (LutronStateObject)lutronAR.AsyncState;
                var lutronClient = lutronState.LutronWorkSocket;
                var bytesRead = lutronClient.EndReceive(lutronAR);
                if (bytesRead > 0)
                {
                    lutronState.LutronStringBuilder.AppendLine(Encoding.ASCII.GetString(lutronState.LutronBuffer, 0, bytesRead));
                    lutronClient.BeginReceive(lutronState.LutronBuffer, 0, LutronStateObject.BufferSize, 0, new AsyncCallback(LutronReceiveCallback), lutronState);
                }
                else
                {
                    if (lutronState.LutronStringBuilder.Length > 0) { LutronResponse = lutronState.LutronStringBuilder.ToString(); }
                    LutronReceiveDone.Set();
                }
            }
            catch (Exception e) { MessageBox.Show(e.ToString()); }
        }

        public static void LutronSend(Socket client, String data)
        {
            var byteData = Encoding.ASCII.GetBytes(data);
            client.BeginSend(byteData, 0, byteData.Length, 0, LutronSendCallback, client);
        }

        private static void LutronSendCallback(IAsyncResult ar)
        {
            try
            {
                var client = (Socket)ar.AsyncState;
                var bytesSent = client.EndSend(ar);
                LutronSendDone.Set();
            }
            catch (Exception e) { MessageBox.Show(e.ToString()); }
        }
        public class LutronStateObject
        {
            public Socket LutronWorkSocket;
            public const int BufferSize = 256;
            public byte[] LutronBuffer = new byte[BufferSize];
            public StringBuilder LutronStringBuilder = new StringBuilder();
        }

    }



Is This A Good Question/Topic? 0
  • +

Replies To: Asynchronous Client Socket ManualResetEvent holding up execution

#2 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3416
  • View blog
  • Posts: 10,515
  • Joined: 05-May 12

Re: Asynchronous Client Socket ManualResetEvent holding up execution

Posted 13 July 2012 - 12:43 PM

Perhaps you meant to use this link for the Client Sample: http://msdn.microsof...ibrary/bew39x2a
Was This Post Helpful? 1
  • +
  • -

#3 whunter1955  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 13-July 12

Re: Asynchronous Client Socket ManualResetEvent holding up execution

Posted 13 July 2012 - 01:16 PM

View PostSkydiver, on 13 July 2012 - 12:43 PM, said:

Perhaps you meant to use this link for the Client Sample: http://msdn.microsof...ibrary/bew39x2a


Yes Skydiver - you are correct. I inadvertently pointed to the wrong Microsoft webpage within my summary above; however the code was taken from the Client Sample code for the webpage you provided the link to.

Any chance you have any advice as to how to implement the ReceiveCallback code so the ManualResetEvent fires and I can get the entire feedback string? Thanks again.

Cheers!
Bill
Was This Post Helpful? 0
  • +
  • -

#4 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3416
  • View blog
  • Posts: 10,515
  • Joined: 05-May 12

Re: Asynchronous Client Socket ManualResetEvent holding up execution

Posted 13 July 2012 - 02:05 PM

I remember playing with that pair of samples back when .NET 2.0 was released and it seemed to work well for me. But my memory maybe fuzzy because that was a few years ago. I'm equally puzzled as to why it's not working.

I would expect the receive callback to be called one more time when there was no more data. Out of curiosity, is the server (aka the Lutron device that you are talking to) closing the connection after it has sent all its data? Or does it hold it open? If so, how does the server tell your client that there is no more data?
Was This Post Helpful? 1
  • +
  • -

#5 whunter1955  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 13-July 12

Re: Asynchronous Client Socket ManualResetEvent holding up execution

Posted 13 July 2012 - 02:21 PM

View PostSkydiver, on 13 July 2012 - 02:05 PM, said:

I remember playing with that pair of samples back when .NET 2.0 was released and it seemed to work well for me. But my memory maybe fuzzy because that was a few years ago. I'm equally puzzled as to why it's not working.

I would expect the receive callback to be called one more time when there was no more data. Out of curiosity, is the server (aka the Lutron device that you are talking to) closing the connection after it has sent all its data? Or does it hold it open? If so, how does the server tell your client that there is no more data?


Skydiver - Thank you again. To answer your question, the Lutron server does not close the connection. My belief is that the ReceiveCallback is supposed to recheck if all of the data has been transmitted until it confirms that there is nothing left in the queue. When it confirms that all of the data has been transmitted (bytesRead = 0) the ManualResetEvent is reset and the application continues. Unfortunately however, it doesn't seem to be working that way. Specifically, after the callback returns the last of the data from the remote device, it does not return to ReceiveCallback to retest if there is any data left in queue. Thus, it never enters the "else" condition and the application freezes.
Was This Post Helpful? 0
  • +
  • -

#6 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3416
  • View blog
  • Posts: 10,515
  • Joined: 05-May 12

Re: Asynchronous Client Socket ManualResetEvent holding up execution

Posted 13 July 2012 - 03:13 PM

So you got me curious...
I pasted in the server and client code and built them. Ran the two program with no modifications. (Fought with firewall for a little while, though to get things working.)

Anyway, the demos work as they stood.

To simulate your situation where the device keeps the connection open, I commented out the handler.Shutdown() and handler.Close() lines on the server. And as you've observed the receive callback isn't called again.
Was This Post Helpful? 1
  • +
  • -

#7 whunter1955  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 13-July 12

Re: Asynchronous Client Socket ManualResetEvent holding up execution

Posted 13 July 2012 - 03:19 PM

View PostSkydiver, on 13 July 2012 - 03:13 PM, said:

So you got me curious...
I pasted in the server and client code and built them. Ran the two program with no modifications. (Fought with firewall for a little while, though to get things working.)

Anyway, the demos work as they stood.

To simulate your situation where the device keeps the connection open, I commented out the handler.Shutdown() and handler.Close() lines on the server. And as you've observed the receive callback isn't called again.


Thanks Skydiver. So, would that mean that the connection needs to be closed to get past the WaitOne() method in your mind? I was hoping to just leave the connection open so that the application could listen for updates, which happen continually. If the connection has to be closed, it totally defeats the purpose of the application. Love to get your insight.

Regards,
Bill
Was This Post Helpful? 0
  • +
  • -

#8 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3416
  • View blog
  • Posts: 10,515
  • Joined: 05-May 12

Re: Asynchronous Client Socket ManualResetEvent holding up execution

Posted 13 July 2012 - 04:22 PM

Don't take what you are about to read as gospel. All of this is just based on the servers that I've had the (mis)fortune of having to try to debug code on.

I've seen a couple different servers that accept TCP connections and most of them are structured like the synchronous sample in MSDN: http://msdn.microsof...ibrary/6y0e13d3

The ones that are meant to handle bigger loads follow the asynchronous pattern that you previously linked to.

Anyway, once a connection has been accepted, the typical pattern I've seen is:
socket = AcceptConnection()
do
{
    message = socket.Recieve();
    if (message != "Goodbye")
        ProcessMessage(message, socket);
} while (message != "Goodbye")
socket.Close();

function ProcessMessage(message, socket)
{
    switch (message)
    {
    case "foo":
        socket.Send("foo results");
        break;

    case "bar":
        socket.Send("bar results");
        break;

    case "ping":
        socket.Send("ACK");
        break;

    :
    }
}



I don't know if these have been particularly well designed or not, but the basic pattern is that the server always sends something back to the client, and it's the client which initiates the closing of the connection in the happy case. There are also unhappy cases where the server can send a message to the client telling the client to disconnect now, but still the point is that a message is sent. I've not seen a protocol where the client assumes that it is finished when no more data is coming from the server.

Edit after: There are protocols that have built in size information so that the client or server knows not to expect more data.

This post has been edited by Skydiver: 13 July 2012 - 05:04 PM

Was This Post Helpful? 1
  • +
  • -

#9 whunter1955  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 13-July 12

Re: Asynchronous Client Socket ManualResetEvent holding up execution

Posted 14 July 2012 - 06:35 AM

Skydiver - Once again, thank you for your support and guidance. I believe you are suggesting that I use a Synchronous Server Socket model as opposed to the Asynchronous Client to avoid problems that the Asynchronous Client model presents. May I ask --- I have hoped to be able to control three different pieces of equipment with this Client, each having it's own "channel" on which to receive commands and provide response. Will using the Synchronous model require waiting for a response from one piece of equipment to process the next piece of equipment? Will using the Synchronous model create any other performance issues?

Thanks again, Skydiver.

Regards,
Bill
Was This Post Helpful? 0
  • +
  • -

#10 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3416
  • View blog
  • Posts: 10,515
  • Joined: 05-May 12

Re: Asynchronous Client Socket ManualResetEvent holding up execution

Posted 14 July 2012 - 07:18 AM

No, I'm not suggesting using the one client over the other. I was just pointing out how a typical server is structured. For low volume servers, the synchronous model is good enough where it services one request at a time. For higher volumes, the asynchronous model works better because it can handle multiple requests.

If you go with a synchronous client, you'll block on each send and receive call. Note that there is nothing to prevent you from having each "channel" be on it's own thread, though.

Personally, if I were in your shoes and I understood how the async client works, I would stick with that.

What I wouldn't do, though, is mix all three in a single class like:
class MyBigAssClient
{
    public Socket LutronClient1;
    public String LutronResponse1;
    const int LutronPort1;
    const string LutronIP1;
    ManualResetEvent LutronConnectDone1;
    ManualResetEvent LutronSendDone1;
    ManualResetEvent LutronReceiveDone1;

    public Socket LutronClient2;
    public String LutronResponse2;
    const int LutronPort2;
    const string LutronIP2;
    ManualResetEvent LutronConnectDone2;
    ManualResetEvent LutronSendDone2;
    ManualResetEvent LutronReceiveDone2;

    public Socket LutronClient3;
    public String LutronResponse3;
    const int LutronPort3;
    const string LutronIP3;
    ManualResetEvent LutronConnectDone3;
    ManualResetEvent LutronSendDone3;
    ManualResetEvent LutronReceiveDone3;
}



This path leads to madness (not to mention premature baldness, and cyrhossis of the liver).

I would rather go with:
class MyLittleClient
{
    public Socket Client { get; private set; }
    public String Response { get; private set; }
    int Port;
    string IP;
    ManualResetEvent ConnectDone;
    ManualResetEvent SendDone;
    ManualResetEvent ReceiveDone;

    public event MessageRecievedHandler MessageRecieved;
}

class MyBigContainer
{
    List<MyLittleClient> Clients;
}


This post has been edited by Skydiver: 14 July 2012 - 07:20 AM

Was This Post Helpful? 1
  • +
  • -

#11 whunter1955  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 13-July 12

Re: Asynchronous Client Socket ManualResetEvent holding up execution

Posted 14 July 2012 - 07:23 AM

Thank you sir. Excellent advice. I very much appreciate your guidance!

Regards,
Bill
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1