Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async socket crashes when I close the socket

Tags:

c#

sockets

The code shown below appears to almost work. If I create an instance of it and call "Connect" all works fine. When I call "Disconnect", sometimes everything is fine (mainly if I add a breakpoint and step through the function slowly). If I don't use a breakpoint the class(being hosted as a win forms app) seems to disappear (the form does) but visual studio still thinks it's running. In visual studio's output window I get "A first chance exception of type 'System.ObjectDisposedException' occurred in System.dll". Can anyone spot what I'm doing wrong?

// State object for reading client data asynchronously
public class StateObject
{
    private Guid ID = Guid.NewGuid();
    // Client socket.
    public Socket workSocket = null;
    // Size of receive buffer.
    public const int BufferSize = 1024;
    // Receive buffer.
    public byte[] buffer = new byte[BufferSize];
}

public class NetworkComms : IBasePanel
{
    private static ILog _log = LogManager.GetCurrentClassLogger();

    // ManualResetEvent instances signal completion.
    private static ManualResetEvent connectDone = new ManualResetEvent(false);
    private static ManualResetEvent sendDone = new ManualResetEvent(false);
    private static ManualResetEvent receiveDone = new ManualResetEvent(false);

    private static Socket _client = null;
    private static IPEndPoint _endpoint = null;

    public event ReceiveMessageEventHandler OnReceiveMessage; 

    public NetworkComms(string address, int port)
    {
        _endpoint = new IPEndPoint(GetIPAddress(address), port);
    }

    private IPAddress GetIPAddress(string address)
    {
        IPAddress ipAddress = null;

        if (IPAddress.TryParse(address, out ipAddress))
        {
            return ipAddress;
        }
        else
        {
            IPHostEntry ipHostInfo = Dns.GetHostEntry(address);
            return ipHostInfo.AddressList[ipHostInfo.AddressList.Count() - 1];
        }
    }

    private  void ConnectCallback(IAsyncResult ar)
    {
        // Retrieve the socket from the state object.
        Socket client = (Socket)ar.AsyncState;

        // Complete the connection.
        client.EndConnect(ar);

        _log.DebugFormat("Socket connected to {0}", client.RemoteEndPoint.ToString());

        // Signal that the connection has been made.
        connectDone.Set();
    }

    private void Receive()
    {
        // Create the state object.
        StateObject state = new StateObject();
        state.workSocket = _client;

        // Begin receiving the data from the remote device.
        _client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
    }

    private void ReceiveCallback(IAsyncResult ar)
    {
        // Retrieve the state object and the client socket 
        // from the asynchronous state object.
        StateObject state = (StateObject)ar.AsyncState;
        Socket client = state.workSocket;

        // Read data from the remote device.
        int bytesRead = client.EndReceive(ar);

        if (bytesRead > 0)
        {
            // There might be more data, so store the data received so far.
            ReceivedNewMessage(Encoding.Default.GetString(state.buffer, 0, bytesRead));

            // Get the rest of the data.
            client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
        }
        else
        {
            // Signal that all bytes have been received.
            receiveDone.Set();
        }
    }

    private static void SendCallback(IAsyncResult ar)
    {
        // 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);
        _log.DebugFormat("Sent {0} bytes to server.", bytesSent);

        // Signal that all bytes have been sent.
        sendDone.Set();
    }

    public void SendMessage(byte[] message)
    {
        _client.BeginSend(message, 0, message.Length, 0, new AsyncCallback(SendCallback), _client);
        sendDone.WaitOne();
    }

    public void Connect()
    {
        _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _client.BeginConnect(_endpoint, new AsyncCallback(ConnectCallback), _client);
        connectDone.WaitOne();

        Receive();
    }

    public void Disconnect()
    {
        try
        {
            _client.Shutdown(SocketShutdown.Both);
            _client.Close();
        }
        finally
        {
            _client = null;

            connectDone.Reset();
            sendDone.Reset();
            receiveDone.Reset();
        }
    }

    private void ReceivedNewMessage(string message)
    {
        if (this.OnReceiveMessage != null)
        {
            this.OnReceiveMessage(message);
        }
    }

    public bool IsConnected
    {
        get
        {
            if (_client == null) return false;
            return _client.Connected;
        }
    }
}
like image 840
Retrocoder Avatar asked Nov 13 '22 20:11

Retrocoder


1 Answers

All of your callbacks need to handle exceptions, which are relativly common in network programming.

In this case what is probably happening is that client.EndReceive(ar); is throwing an ObjectDisposedException because the socket is already closed when its called.

like image 154
Yaur Avatar answered Dec 25 '22 13:12

Yaur