SocketAsyncEventArgs.Completed在Windows 8中无法触发

4

当我在安装了Windows 7 Ultimate和.NET 4的计算机上编译此代码时,它能正常运行。但是当我在安装了Windows 8 RTM和.NET 4.5的计算机上尝试时,Complete事件从未触发。

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);
    }
}

有趣的是,如果我在这一行设置一个断点并进行调试:

var arguments = new SocketAsyncEventArgs();

在继续执行之前,使用Hercules连接此服务器,它的效果非常好。我在开始时这样做,然后神奇地,在每个连接上都会调用OnAccepted并将"Accepted."写入控制台。我在拥有Windows 7和.NET 4的机器上使用相同的代码和相同的程序(Hercules),但它总是起作用。

  • 我做错了什么吗?
  • 如果不是,那么这是我操作系统或.NET Framework版本4.5的已知bug吗?
  • 有人能复现这个问题吗?

编辑:两个操作系统均为64位。
编辑2:我在Microsoft Connect上报告了这个错误,在这里
编辑3:找到了一个解决方法,并将其发布到Connect(通过创建虚假的第一个连接)。
编辑4:如果有人能够复现此问题,请在Connect中加入该问题。
编辑5:我看到了Thomas提到的问题,并测试了Console.ReadLine是否会导致这种情况。结果证明是这样的。如果在我的Console.ReadLine调用之前添加Thread.Sleep(3000)并在运行程序3秒钟后进行连接尝试,则它的效果非常好。同样,奇怪的是,在调用Console.ReadLine 之前只需要执行一次此操作。如果我在调用Console.ReadLine 之前进行一次连接,那么每个连续的连接都有效,即使在调用Console.ReadLine 之后也是如此。我将在Conect页面中提到这一点。
编辑6:我将链接添加到Connect页面中,并添加了另一个解决方法,其中包括在调用Console.ReadLine之前调用Thread.Sleep,就像我在上面的编辑中提到的那样。


你确定这不是防火墙或其他问题吗?它能用Socket.Accept吗?(我的意思是:问题出在AcceptAsync上还是整个网络通信上?) - Marc Gravell
@Marc:Accept起作用了。 AcceptAsync也可以,但是只有在调用AcceptAsync之前尝试连接时才有效。奇怪的是,这样做可以解决所有问题。如果我只是在Listen之后和AcceptAsync之前设置断点,那么所有后续的连接都没有问题。当执行停止时,我只需使用Hercules连接到该套接字并按F5键:每个后续连接都能完美地工作。 - Şafak Gür
@Peter:不是的。AcceptAsync返回true,因此代码不会同步调用OnAccepted,新连接也不会触发Completed事件,因此在那种情况下不会调用OnAccepted。 - Şafak Gür
我遇到了与此处讨论的相同问题:https://dev59.com/pmcs5IYBdhLWcg3w8oXK 将在Microsoft Connect上加入。 - TJF
我在使用VS2012针对.Net4进行开发,并在Win8RTM上运行时遇到了同样的问题。为了让AcceptAsync任务完成,我必须激活/切换到包含服务器进程的窗口。 - Monroe Thomas
显示剩余2条评论
1个回答

1

原来是Windows 8的一个bug。目前我找到的最佳解决方法是在不同的线程上启动IOCP操作。

因此,在问题中给出的代码示例中,我应该更改这一行:

Accept(arguments);

Main方法的这一行:

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

这可以防止Console.ReadLine()调用阻塞IOCP操作。

顺便提一下:这只是一个操作系统错误的解决方法,很可能会通过更新来修复,并且希望使这个解决方法变得多余。

这个问题已经在最新版本的Windows 8中得到了解决。


编辑:我在Connect上发布的反馈项目的状态已更改为“设计原因”。
我还收到了一封电子邮件,其中包含以下内容:

这种行为背后的基本问题与Windows 8中如何处理IO完成端口有关。.NET使用完成端口工作;当它们的行为发生变化时,.NET的行为也会发生变化。

编辑2:反馈的状态再次更改为“活动状态”,但没有详细信息。

编辑3:微软又回复了我的反馈,并声明:

Windows 8的最新版本应该已经解决了这个问题。请注意,这是一个操作系统问题,而不是.NET问题:您需要确保自己拥有最新版本的操作系统。.NET未对此问题进行任何更改,也未引起或修复此问题。”


1
有没有官方的微软错误报告可以公开跟踪? - mizi_sk
我找不到相关的信息。我在这篇 TechNet 论坛帖子中找到了一个 MS Connect 链接,但它已经无效了。在同一页中,Len Holgate 评论了微软的回应:“我们已经将此提交给基础操作系统团队,他们将考虑在未来的更新中解决这个问题。我将此标记为已推迟。”所以目前尚无确切的日期或活动的错误报告可供跟踪。 - Şafak Gür
@mizi_sk:他们关闭了这个案例,称这种行为是设计上的。 - Şafak Gür
@mizi_sk:问题已经通过Windows 8的更新得到解决。我已经编辑了答案并提供了详细信息。 - Şafak Gür

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接