Igor
Igor

Reputation: 6285

Is it possible to wait for connection on asynchronous socket

ALL, I am using an Asynchronous socket in C#. My problem is: I want to wait for a callback to finish with connection, because I need to immediately send the information to a server.

Here is a code snippet:

class InternetConnector
{
    private struct ConnectionData
    {
        public Action<Exception> ErrorHandler { get; set; }
        public Socket Socket { get; set; }
    }
    public void ConnectToHost(Action<Exception> errorHandler)
    {
        IPEndPoint ip = new IPEndPoint(IPAddress.Parse(connector_host), connector_port);
        client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        var ConnectionData = new ConnectionData { ErrorHandler = errorHandler, Socket = client };
        client.Blocking = true;
        client.BeginConnect(ip, new AsyncCallback(ConnectCallback), ConnectionData);
        connectDone.WaitOne(100);
    }

    private static void ConnectCallback(IAsyncResult ar)
    {
        ConnectionData connectionData = new ConnectionData();
        try
        {
            connectionData = (ConnectionData)ar.AsyncState;
            connectionData.Socket.EndConnect(ar);
            connectDone.Set();
            Connected = true;
        }
        catch (Exception e)
        {
            if (connectionData.ErrorHandler != null)
                connectionData.ErrorHandler(e);
        }
    }
}

public partial class Form1 : Form
{
    private bool isRunning = false;
    private InternetConnector client = new InternetConnector();

    private void AsyncErrorHandler(Exception e)
    {
        if (status.InvokeRequired)
        {
            status.BeginInvoke(new Action(() => AsyncErrorHandler(e)));
            return;
        }
        InternetConnector.Connected = false;
        isRunning = false;
        startStop.Text = "Start";
        status.ForeColor = Color.Red;
        status.Text = "Socket Error: " + e.Message;
    }

    private void startStop_Click(object sender, EventArgs e)
    {
        if (!isRunning || !InternetConnector.Connected)
        {
            if (!InternetConnector.Connected)
            {
                client.SetAddress(ipAddress.Text);
                client.SetPort(Convert.ToInt32(connectionport.Text));
                client.ConnectToHost( AsyncErrorHandler );
                status.Text = "Signals Receiver: Connected";
                status.ForeColor = Color.Green;
                startStop.Text = "Stop";
                isRunning = true;
     // if connection successful, send some data and start reading the socket
            }
            else
            {
                startStop.Text = "Start";
                client.DisconnectFromHost(AsyncErrorHandler);
                isRunning = false;
            }
        }
    }
}

I can handle the exception in the connection. Now I need to handle the successful connection as well.

Thank you.

Upvotes: 0

Views: 2729

Answers (2)

flodis
flodis

Reputation: 1241

Maybe some variant of this extension method can inspire. It takes a an action and a timespan and waits for one of task or timeout to complete first.

In case the timeout wins the race, the supplied action is executed.

public static async Task<bool> OnTimeout<T>(this T t, Action<T> action, TimeSpan timespan) where T : Task
{
    var timeout = Task.Delay(timespan);

    if (await Task.WhenAny(t, timeout) == timeout)
    {
      //Enter here on timeout
      action(t);
      return true;
    }
    else
    {
      return false;
    }
}

Used like this where some actions can be taken in case of a timeout.

await socket.ConnectAsync().OnTimeout(t => {
  throw new TimeoutException();
}, TimeSpan.FromSeconds(5));  

Upvotes: 0

Monroe Thomas
Monroe Thomas

Reputation: 5042

You can follow the same pattern, and supply a handler to be called on success as well as on error:

class InternetConnector 
{ 
    private struct ConnectionData 
    { 
        public Action<Socket> SuccessHandler { get; set; } 
        public Action<Exception> ErrorHandler { get; set; } 
        public Socket Socket { get; set; } 
    } 

    public void ConnectToHost(Action<Socket> successHandler, Action<Exception> errorHandler) 
    { 
        IPEndPoint ip = new IPEndPoint(IPAddress.Parse(connector_host), connector_port); 
        client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
        var ConnectionData = new ConnectionData 
        { 
            SuccessHandler = successHandler,
            ErrorHandler = errorHandler, 
            Socket = client 
        }; 
        client.Blocking = true; 
        client.BeginConnect(ip, new AsyncCallback(ConnectCallback), connectionData); // <--  make sure to use the lower-case connectionData here!  :)
        connectDone.WaitOne(100); 
    } 

    private static void ConnectCallback(IAsyncResult ar) 
    { 
        ConnectionData connectionData = new ConnectionData(); 
        try 
        { 
            connectionData = (ConnectionData)ar.AsyncState; 
            connectionData.Socket.EndConnect(ar); 
            connectDone.Set(); 
            Connected = true; 
            if (connectionData.SuccessHandler != null)
                connectionData.SuccessHandler(connectionData.Socket);
        } 
        catch (Exception e) 
        { 
            if (connectionData.ErrorHandler != null) 
                connectionData.ErrorHandler(e); 
        } 
    } 
} 

The signature of the function you pass as a success handler must match the Action<Socket> delegate, which would look something like:

void MySuccessHandler(Socket socket) 
{ 
    // do stuff with the connected socket..        
    Console.WriteLine("Connected to {0}", socket.RemoteEndPoint);         
}

void MyErrorHandler(Exception e)
{
    Console.WriteLine("Connection error {0}", e.Message);         
}

...

myConnector.ConnectToHost(MySuccessHandler, MyErrorHandler);

Upvotes: 2

Related Questions