0

Using .NET 6.0.

I called BeginAccept like so:

AsyncCallback callback = new AsyncCallback(AcceptCallback);
object? state = this.listenSocket;
_ = this.listenSocket.BeginAccept(callback, state);

and my AcceptCallback method gets called as expected. I then have to call EndAccept to obtain the receive socket:

    private static void AcceptCallback(IAsyncResult result)
    {
        Socket? listenSocket = result.AsyncState as Socket;

        if (listenSocket == null)
        {
            // note: This is impossible but it helps getting the possible
            //       null reference warnings out of the way.
            throw new ArgumentNullException(nameof(result.AsyncState), "The state object is null.");
        }

        Socket receiveSocket = listenSocket.EndAccept(
            out byte[] buffer,
            out int bytesRead,
            result);  // and here it blows up

The last call gives me a "System.ArgumentException: 'Value does not fall within the expected range. (Parameter 'asyncResult')'".

Apparently is has a problem with the last argument. It is not something I provide though, it is the result argument I get passed by the system which (according to the debugger) is a proper System.Threading.Tasks.TaskToApm.TaskAsyncResult. I am just following the examples I found.

I started out without the wrapping new AsyncCallback() in the call to BeginAccept because I did not understand what good it does (I would like to know though). I also initially had non-static methods. Because I had this problem I changed it to be more like the original examples but the same exception remains.

What is the problem and what can I do to overcome it?

[Edit]

As pointed out by Hans Passant the result argument should not be a System.Threading.Tasks.TaskToApm.TaskAsyncResult and this causes the error. There is little talk or documentation about TaskAsyncResult on the internet which made me suspicious, thinking it could be a bug in new framework code. So I rebuild with .NET 5.0 but the result was the same.

I am just using System.Net.Sockets.Socket classes, nothing fancy from a custom library or anything like that.

Martin Maat
  • 714
  • 4
  • 23
  • It should not be a TaskAsyncResult, that's why it failed. How Task played a role in this code is quite unclear. – Hans Passant Jan 02 '22 at 14:32
  • The following may be helpful: https://stackoverflow.com/questions/70551165/c-sharp-multiuser-tcp-server/70551348 – Tu deschizi eu inchid Jan 02 '22 at 15:42
  • @HansPassant Thank you. Can you tell what the async result should be, type-wise? I updated my question with some additional information. – Martin Maat Jan 02 '22 at 16:08
  • `BeginXXX` and `EndXXX` are legacy APIs (APM) you should avoid. Please read more in https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/ – Lex Li Jan 02 '22 at 18:07
  • @LexLi Oooo-kaay... It is my ambition with this project to be future proof and portable (so .NET Standard compliancy would be preferred and I do want to avoid anything (about to be) deprecated). But it looks like the Begin...-End... pattern is pretty much tied to sockets programming so what do you suggest? – Martin Maat Jan 02 '22 at 19:24
  • 1
    1) If you want to program against `System.Net.Sockets.Socket` you have to be a master of Windows/.NET socket API, which honestly is far beyond the capability of ordinary .NET developers. That's why people turn to third party libraries which wrap over the raw API and provide much easier API to consume. 2) There are already new API there like `AcceptAsync` for years (called TAP in that article), and that's why I said `BeginAccept/EndAccept` should be avoided. 3) even Microsoft has new things like https://devblogs.microsoft.com/dotnet/system-io-pipelines-high-performance-io-in-net/ – Lex Li Jan 02 '22 at 20:00
  • @LexLi I was not afraid of a little low level stream handling and already thought of the complications outlined in the article you linked to, but those pipelines sure look worth looking into. So thanks for the heads-up, I have some reading to do. An answer to my original question would still be nice though... – Martin Maat Jan 02 '22 at 20:41

1 Answers1

0

It appears this first bit:

AsyncCallback callback = new AsyncCallback(AcceptCallback);
object? state = this.listenSocket;
_ = this.listenSocket.BeginAccept(callback, state);

makes it fail. If I change it to:

int maxSize = 0x2000;  // 8K
AsyncCallback callback = new AsyncCallback(AcceptCallback);
object? state = this.listenSocket;
_ = this.listenSocket.BeginAccept(maxSize, callback, state);

it succeeds, no exception anymore on EndAccept and I get the expected data in my ReceiveCallback (the next step in the asynchronous flow).

Because I was new to socket programming I figured it would be wise to start simple so I used the BeginAccept overload with the least parameters. I assumed it would use a default for maximum size. Well, whatever that "simple" overload does, it is not good.

So the type System.Threading.Tasks.TaskToApm.TaskAsyncResult for the result argument of AcceptCallback is perfectly fine, no problem there (I checked, it is still the same).

I can proceed now.

Martin Maat
  • 714
  • 4
  • 23