C# 并发和异步套接字连接

4
我正在开发一个简单的套接字服务器,它应该能够接受多个连接并通过 EndAccept() 将它们交给 "connection" 类处理。问题在于,一旦接受了一个连接,代码就不会再接受任何其他连接直到当前连接结束。
我在 Main() 中创建套接字,然后通过 Initialize() 将套接字传递给 ServerEnvironment(静态类)。
MainSocket.Bind(new IPEndPoint(IPAddress.Parse(Addr), Port));
MainSocket.Listen(10);

ServerEnvironment.Initialize(MainSocket);

while (true)
{
    Console.ReadLine();
    Console.WriteLine(">>");
}

一旦传递了MainSocketServerEnvironment就会接管。
static Socket MainSocket;

public static void Initialize(Socket _MainSocket)
{
    MainSocket = _MainSocket;

    AcceptGameConnection = new AsyncCallback(AddConnection);
    MainSocket.BeginAccept(AcceptGameConnection, null);
}

public static void AddConnection(IAsyncResult Result)
{
    Socket UserSocket = MainSocket.EndAccept(Result);
    ConnectionCount++;

    // Removed the stuff that happens to UserSocket because the same
    // symptoms occur regardless of their presence.

    MainSocket.BeginAccept(AcceptGameConnection, null);
 }

当我搜索这个问题时,我发现多线程可能是我目的的必需品。然而,当我在Initialize();AddConnection();中使用Console.WriteLine(Thread.CurrentThread.ManagedThreadId);时,会出现两个不同的线程ID,因此我认为多线程已经是一种能力,我不需要手动创建一个线程。但这并不能解释我的问题。
对于我是否能够拥有并发和异步套接字连接,多线程是否必要?
编辑:绑定错误...绑定到我的局域网地址导致一些问题。

2
也许你应该看一下 http://www.asp.net/signalr,这是一个非常易于使用的库。 - bto.rdz
你是否正在使用 .net >= 4.5? - Aron
2个回答

4
如果您使用的是.NET 4.0或更低版本,则可以按照以下方式进行操作:
public static void Initialize(Socket _MainSocket)
{
    MainSocket = _MainSocket;

    AcceptGameConnection = new AsyncCallback(AddConnection);
    MainSocket.BeginAccept(result => {
        var userSocket = MainSocket.EndAccept(result);
        var thread = new Thread(AddConnection);
        thread.Start(userSocket);
        Initialize(MainSocket);
    }, null);
 }

 public static void AddConnection(IAsyncResult Result)
 {
     MainSocket.BeginAccept(AcceptGameConnection, null);
 }

 private static void AddConnection(Socket userSocket)
 {
     // Removed the stuff that happens to UserSocket because the same
     // symptoms occur regardless of their presence.
 }

但在 .net 4.5 或以上版本中,您可以这样做。

public static void Initialize(Socket mainSocket)
{           
    while(true)
    {
        var userSocket = await Task.Factory.FromAsync(
                                mainSocket.BeginAccept,
                                mainSocket.EndAccept);
        ThreadPool.QueueUserWorkItem(_ => AddConnection(userSocket));
    }
 }

 private static void AddConnection(Socket userSocket)
 {
     // Removed the stuff that happens to UserSocket because the same
     // symptoms occur regardless of their presence.
 }

所以我肯定会尝试上面的和后面的方法,但您介意向我解释一下为什么我的方法适用于其他应用程序吗?我查看了一些其他游戏套接字服务器,并且它们使用了我的确切模式。 - Fuselight
@Fuselight,问题在于你必须再次调用BeginAccept才能打开新的连接。因为这就是BeginAccept的作用,接受新的连接。我正在启动一个线程来执行工作,并立即调用BeginAccept。你的其他应用程序可能会正常工作,因为你所做的“工作”完成得相当快。 - Aron
实际上,为了让服务器打开多个连接,您需要在用户连接时立即调用“Accept”。 - Aron

3
问题在于接受连接后,代码不会再接受任何内容,直到连接结束为止。这是因为下一个BeginAccept调用被某些延迟了,很可能是由于此处未显示的代码造成的。异步接受几乎总是没有意义的。不要盲目从糟糕的Microsoft教程中复制。您的接受循环应该如下所示:
while (true) {
 var socket = Accept();
 StartProcessing(socket); //Cannot block!
}

这就是全部。

您可以使用Taskawait来实现StartProcessing,这使得异步socket IO非常容易。或者,每个收到的连接启动一个新的任务/线程。

简单的socket服务器

这样的东西不存在。您最好不要使用sockets,而是使用现成的更高级的协议,如WCF或HTTP。


关于Accept不继续的问题,您确定有新连接进来吗?无论如何,您应该转换到更简单的accept循环。在这种情况下,没有理由使用异步接受。所有那些不知道自己在做什么并从他人复制的人都是如此;关于线程,确保只使用带有等待的异步IO。谷歌“.net socket await”。无论您做什么,请放弃APM模式。 - usr
不,应该使用异步IO来运行它们。它们使用独立的套接字。已接受的套接字与其“父”套接字没有关联。 - usr
那么你的原始代码很可能会直接运行。我建议你按照推荐的现代方式进行重写,但它仍然可以工作。 - usr
等等,这里有什么问题?我正在打开两个套接字,但只有一个连接成功。即使我断开其中一个,第二个也无法被接受,甚至在重新加载网页后也是如此。顺便说一下,这是一个Flash客户端。 - Fuselight
1
如果您无法使用该故障转移测试代码接受两个连接,则没有两个连接进入。客户端存在错误。 - usr
显示剩余3条评论

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