SocketAsyncEventArgs.Completed doesn't fire in

2019-05-08 05:22发布

When I compile this code on a machine with Windows 7 Ultimate and .NET 4 installed, it works just fine but when I try it on one with Windows 8 RTM and .NET 4.5 installed, Complete event never fires.

class Program
{
    private static Socket _Socket = new Socket(
        AddressFamily.InterNetwork,
        SocketType.Stream,
        ProtocolType.Tcp);

    private static void Main(string[] args)
    {
        _Socket.Bind(new IPEndPoint(IPAddress.Any, 5012));
        _Socket.Listen(100);

        var arguments = new SocketAsyncEventArgs();
        arguments.Completed += OnAccepted;
        Accept(arguments);

        Console.ReadLine();
    }

    private static void Accept(SocketAsyncEventArgs args)
    {
        args.AcceptSocket = null;
        if (!_Socket.AcceptAsync(args))
            OnAccepted(null, args);
    }

    private static void OnAccepted(object sender, SocketAsyncEventArgs e)
    {
        Console.WriteLine("Accepted.");
        Accept(e);
    }
}

The interesting thing here is if I put a breakpoint at this line and debug it:

var arguments = new SocketAsyncEventArgs();

And connect this server using Hercules before continuing execution, it works like a charm. I do this at the start and then magically, OnAccepted gets called and writes "Accepted." to the console on every single connection. I use the same code and same program (Hercules) on the machine with Windows 7 and .NET 4 but it always works.

  • Am I doing something wrong?
  • If not, is it a known bug of my OS or .NET Framework version 4.5?
  • Can anyone reproduce this?

Edit: Both operating systems are 64 bit.
Edit 2: I reported this as a bug on Microsoft Connect, here.
Edit 3: Found a workaround and post it to Connect (Simply by creating a fake, first connection).
Edit 4: If anyone can reproduce this, please join the issue in Connect.
Edit 5: I saw the question Thomas has mentioned and I tested whether Console.ReadLine was causing this or not. Turned out it was. If I add Thread.Sleep(3000) before my Console.ReadLine call and make a connection attempt in 3 seconds after I run the program, it works like a charm. Again, the odd thing is that I need to do this only once before calling Console.ReadLine. If I make one connection before calling Console.ReadLine then every consecutive connection works, even after Console.ReadLine is called. I'll mention this in the Conect page.
Edit 6: I added the link to the other question to the Connect page and added another workaround that involves calling Thread.Sleep before calling Console.ReadLine like I mentioned in the above edit.

1条回答
孤傲高冷的网名
2楼-- · 2019-05-08 05:57

It turned out to be a Windows 8 bug. Best workaround I could find so far is to start the IOCP operation on a different thread.

So the thing I should do in the code sample that is given in the question is changing this line:

Accept(arguments);

To this line in the Main method:

Task.Run(() => Accept(arguments)).Wait();

This prevents Console.ReadLine() call to block the IOCP operation.

As a side note: This is just a workaround to an operating system bug which will most likely be fixed via an update and -hopefully- making this workaround redundant.

This issue is fixed with the latest version of Windows 8.


Edit: Status of the feedback item I have posted in Connect is changed to "By Design".
I also received an e-mail which contains the following:

The underlying issue for this behavior has to do with how IO Completion Ports are handled in Windows 8. .NET works using the completion ports; when their behavior changed, so did the .NET behavior.

Edit 2: Status of the feedback is changed again to "Active" with no details.

Edit 3: The feedback received another answer from Microsoft, stating:

The latest version of Windows 8 should have this fixed. Note that it's an OS issue, not a .NET issue: you need to make sure you have the latest version of the OS. No changes were made to .NET to either cause or fix this issue."

查看更多
登录 后发表回答