Search code examples
c#socketscustom-events

Socket OnConnectionLost Custom Event


I have written a class that is essentially a heartbeat, the client sends a message to the server every x number of seconds.

Shamelessly stolen Send code

  private void SendUdpPacket() {
        byte[] data = new byte[1024];
        Socket udpClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        data = Encoding.ASCII.GetBytes("lubdub");
        udpClientSocket.SendTo(data, 0, data.Length, SocketFlags.None, ipep);
    }

Shamelessly stolen Receive code

void ReceiveData(IAsyncResult iar) {
        byte[] buffer = new byte[1024];
        IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
        EndPoint tempRemoteEP = (EndPoint)sender;
        Socket remote = (Socket)iar.AsyncState;
        int recv = remote.EndReceiveFrom(iar, ref tempRemoteEP);
        string stringData = Encoding.ASCII.GetString(buffer, 0, recv);
        Console.WriteLine(stringData);
        lastUpdate = DateTime.Now.ToUniversalTime();
        if (!this.IsDisposed) {
            udpServerSocket.BeginReceiveFrom(buffer, 0, 1024, SocketFlags.None, ref ep, new AsyncCallback(ReceiveData), udpServerSocket);
        }

This is then monitored by an internal timer

private void clTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
        try {
            SendUdpPacket();
            connected = true;
        } catch {
            connected = false;
        }
    }
    private void srTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
        // Calculate the Timespan since the Last Update from the Client.
        timeSinceLastHeartbeat = DateTime.Now.ToUniversalTime() - lastUpdate;

        if (timeSinceLastHeartbeat > TimeSpan.FromMilliseconds(Timer.Interval))
            connected = false;
        else
            connected = true;
    }

if the message is successful then the socket is connected, this is shown via a public boolean value connected

So in my application I would then have a timer running the following:

        private void ServerCheck() {
        if (heartin.connected) {
            GKstat = true;
            GKStatus.Text = "GateKeeper Status: Connected";
        } else {
            GKstat = false;
            GKStatus.Text = "GateKeeper Status: Disconnected";
        }
    }

However this is not ideal, as it requires timer on the client or server to constantly check if the Heart is connected.

I was wondering if it would be beneficial to convert this to a set of events, say OnConnectionLost and OnConnected

I've been looking around and reading various pages, they've only confused me further.

This is what I have so far

    public delegate void OnConnectionLost(Heart sender, EventArgs e);
    public delegate void OnConnected(object sender, EventArgs e);

    public event OnConnectionLost ConnectionLost;

My question is, would this be beneficial, and if so, how would I create the event in such a way that it would only fire when connection state changes?


Solution

  • This is one of the scenarios where booleans are evil: I strongly recommend you make an enum such as:

    public enum ConnectionState
    {
        Disconnected = 0,
        Disconnecting = 1,
        Connecting = 2,
        Connected = 3,
        // etc.
    }
    

    You can then use this for a property such as ConnectionState. In addition, it's common practice to use the standard EventHandler<T> delegate instead of your own custom delegate types. For example:

    public sealed class ConnectionStateEventArgs : EventArgs
    {
        public ConnectionState ConnectionState { get; private set; }
        public ConnectionStateEventArgs(ConnectionState connectionState)
        {
            ConnectionState = connectionState;
        }
    }
    public event EventHandler<ConnectionStateEventArgs> ConnectionStateChanged;
    

    This will allow you to expand your connection states beyond the simple "on-off" that you have right now. The ConnectionState property coupled with the ConnectionStateChanged event is a common practice that I've noticed across various APIs.

    To have it only fire when the connection state really changes, simply add a body to your ConnectionState property:

    private ConnectionState _connectionState;
    public ConnectionState ConnectionState
    {
        get { return _connectionState; }
        set
        {
            if (value != _connectionState)
            {
                _connectionState = value;
                var tmp = ConnectionStateChanged;
                if (tmp != null)
                    tmp (this, new ConnectionStateEventArgs(value));
            }
        }
    }
    

    All you then need do is assign a different value to the property and the event will fire.