47

Naturally, BeginReceive() will never end if there's no data. MSDN suggests that calling Close() would abort BeginReceive().

However, calling Close() on the socket also performs a Dispose() on it, as figured out in this great answer, and consequently EndReceive() would throw an exception because the object is already disposed (and it does!).

How should I proceed?

Kevin
  • 16,549
  • 8
  • 60
  • 74
Day_Dreamer
  • 3,311
  • 7
  • 34
  • 61
  • http://stackoverflow.com/questions/1921611/c-how-do-i-terminate-a-socket-before-socket-beginreceive-calls-back – SwDevMan81 Jan 11 '11 at 21:05

7 Answers7

56

It seems like this is by (the very dumb) design. You must have this exception thrown and caught in your code.

MSDN looks silent about it indeed, but if you look at the documentation of another asynchronous socket method, BeginConnect(), here's what we find:

To cancel a pending call to the BeginConnect() method, close the Socket. When the Close() method is called while an asynchronous operation is in progress, the callback provided to the BeginConnect() method is called. A subsequent call to the EndConnect(IAsyncResult) method will throw an ObjectDisposedException to indicate that the operation has been cancelled.

If it is the proper way of doing for BeginConnect, it is probably so for BeginReceive as well. This is certainly a poor design on the part of Microsoft's async API, because making the user necessarily throw and catch exception as a part of a normal flow would annoy the debugger. You have really no way to "wait" until the operation is completed, because Close() is what completes it in the first place.

Pavel Radzivilovsky
  • 18,794
  • 5
  • 57
  • 67
  • 7
    Can't believe it's built to work like this but it is what it is I suppose. – Kelly Mar 30 '12 at 20:05
  • @Kelly: What's wrong with it as a design? Code which calls `EndReceive` will need to handle failures regardless of whether an exception is thrown or not. If the condition can be handled right at the `EndReceive`, not having it throwing an exception would allow a `catch` to be replaced with an `if`, but if it can't be handled then, one would have to add `if ( [it failed] ) throw [something]` and would *still* need a catch. Connections shouldn't be aborted often enough for the performance overhead of using an exception vs an `if` to matter. – supercat Feb 10 '14 at 16:42
  • 6
    Because throwing and catching an exception as a part of a normal input, as opposed to an 'exceptional' flow, is a bad design regardless of performance implications. It may also annoy debugging under "break on throw" settings which is often useful. – Pavel Radzivilovsky Feb 12 '14 at 14:27
  • 1
    a lot of things are dumb in .Net, StreamReader, BinaryReader, (leaveOpen, DiscardBufferedData...) The ASync and Begin methods really just wrap the logic of Send and Receive in most anyway so in this case as most others the synchronous operation has to be cancel-able to support such a concept. – Jay Dec 26 '14 at 16:45
2

I am surprised no one recommended using SocketOptions.

Once the stack has the send or receive operation it is bound by the socket options of the socket.

Use a small send or receive timeout and use it before the operation so you don't care if it's changed during that same operation to something shorter or longer.

This will cause more context switching but will not require closing the socket under any protocol.

For example:

1) Set a small timeout

2) Perform operations

3) Set timeout larger

This is similar to using Blocking = false but with an automatic timeout that you specify.

Jay
  • 3,276
  • 1
  • 28
  • 38
  • 1
    The socket send/receive timeout options only apply while a blocking I/O operation is in progress. I haven't looked at how the Framework's async I/O is implemented, but I'm hoping it's with an actual async mechanism such as WSASelect or I/O completion ports, and not some thread sitting in a blocking operation. – Michael Wojcik May 17 '17 at 12:19
0

You can read my solution of this problem here(using comment of Pavel Radzivilovsky here): UdpClient.ReceiveAsync correct early termination

Community
  • 1
  • 1
EgoPingvina
  • 734
  • 1
  • 10
  • 33
0

For TCP socket connections, you can use the Connected property to determine the state of the socket before trying to access any disposed methods. Per MSDN:

"The Connected property gets the connection state of the Socket as of the last I/O operation. When it returns false, the Socket was either never connected, or is no longer connected."

Since it says "no longer connected" it implies that a Close() was previously called on the socket. If you check whether the socket is Connected at the start of the receive callback, there will be no exception.

geekus42
  • 11
  • 4
  • 1
    You realize these async sockets are within a multithreaded program right? You can check on one line in thread A and the execution then can close in thread B, then back in thread A your condition is out the window. – Christopher Pisz Jan 18 '18 at 21:51
0

In the ReceiveCallback I checked client.Connected within the try block. Now, when data is received after BeginReceive, I can call client.Close(); This way, I do not see exceptions. I send modbus-TCP requests every 200mS, and get responses in time. The console output looks clean. I used a windows forms app, to test this.

    private static void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            // Retrieve the state object and the client socket
            // from the asynchronous state object.  
            StateObject state = (StateObject)ar.AsyncState;
            Socket client = state.workSocket;
            if (client.Connected)
            {
                // Read data from the remote device.  
                state.dataSize = client.EndReceive(ar);
                if (state.dataSize > 0)
                {
                    Console.WriteLine("Received: " + state.dataSize.ToString() + " bytes from server");
                    // There might be more data, so store the data received so far.  
                    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, state.dataSize));
                    //  Get the rest of the data.  
                    client.BeginReceive(state.buffer, 0, StateObject.BUFFER_SIZE, 0,
                        new AsyncCallback(ReceiveCallback), state);

                    state.dataSizeReceived = true;           //received data size?

                    dataSize = state.dataSize;
                    buffer = state.buffer.ToArray();
                    dataSizeReceived = state.dataSizeReceived;

                    string hex = ByteArrayToString(state.buffer, state.dataSize);
                    Console.WriteLine("<- " + hex);

                    receiveDone.Set();
                    client.Close();
                }
                else
                {
                    Console.WriteLine("All the data has arrived");
                    // All the data has arrived; put it in response.  
                    if (state.sb.Length > 1)
                    {
                        Console.WriteLine("Length: " + state.sb.Length.ToString());
                    }
                    // Signal that all bytes have been received.  
                    receiveDone.Set();
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }
Jack
  • 151
  • 4
  • 12
-1

Another solution would be to send "yourself" a "control message" using a socket bound to a different port. It's not exactly an abort, but it would end your async operation.

Adi
  • 106
  • 1
  • 6
  • They could also use a wait handle or event. They could also could use options as I indicated in my answer. This requires another socket per client and server. – Jay Dec 29 '14 at 09:10
-1

I was struggling with this as well but as far as I can tell using a simple boolean flag before calling .BeginReceive() will work as well (so there'll be no need for exception handling). Since I already had start/stop handling, this fix was a matter of one if statement (scroll down to the bottom of the OnReceive() method).

if (_running)
{
    _mainSocket.BeginReceive(_data, 0, _data.Length, SocketFlags.None, OnReceive, null);
}                

Should I have overlooked something with this approach, let me know!

Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
  • This doesn't work if he wants to abort AFTER calling BeginReceive. Given BeginReceive creates an Asynchronous callback, it will have that Asynchronous callback waiting until there is data to receive. What if he wants to abort after calling BeginReceive but before the Async callback executes (which is what his question is suggesting)? Your solution wouldn't work in this case. – Tom Heard Jun 26 '14 at 02:19
  • Therefore Exception handling is needed in the OP's case, as the only way to Abort the asynchronous callback is to close the socket. Which will cause an ObjectDisposedException to be thrown on EndReceive. – Tom Heard Jun 26 '14 at 02:22