使用TPL创建套接字过度杀手的集合还是有更好的方法来创建多个套接字?

时间:2011-12-06 16:35:42

标签: c# .net sockets task-parallel-library

我正在更新支持单个连接的应用程序,以及能够支持多个连接的应用程序。我认为使用任务并行库可能是创建和管理多个套接字的最佳方法。因此,我已经开始编写一些代码,并在调试时遇到了一个可能的问题。似乎我创建的用于建立连接的一些任务永远不会完成,并且我想知道我是否正确使用TPL或者是否是其他东西。如果我调试代码,看起来好像集合中只有一个任务实际上包含一个Socket。任何帮助表示赞赏。以下是沙箱控制台应用程序的代码。

主要:

        static void Main(string[] args)
        {
            Console.WriteLine("Creating connections.....");
            var sockets = CreateListeners(5);

            Console.WriteLine("Socket Info:");

            foreach (var socket in sockets)
            {                
                if (socket.Result != null)
                {
                    var con = socket.Result;
                    IPEndPoint ipep = (IPEndPoint)con.LocalEndPoint;
                    string port = ipep.Port.ToString();
                    Console.WriteLine("Socket #{0} - Listening on Port {1}:", socket.Id.ToString(), port);
                }
            }
            Console.WriteLine("Press any key to exit..");
            Console.ReadLine();
        }

创建任务列表的功能:

private static List<Task<Socket>> CreateListeners(int numberToCreate)
            {
                var sockets = new List<Task<Socket>>();
                for (int n = 0; n < numberToCreate; n++)
                {
                    var currentSocket = Task<Socket>.Factory.StartNew(() => CreateSocketConnection(n));
                    sockets.Add(currentSocket);
                }

                return sockets;            
            }

创建单个套接字连接的函数..

private static Socket CreateSocketConnection(int port)
            {

                try 
                {
                    IPAddress ipAddress = null;
                    IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
                    if (Socket.OSSupportsIPv6)
                    {
                        ipAddress = ipHostInfo.AddressList[1];
                    }
                    else
                    {
                        ipAddress = ipHostInfo.AddressList[0];
                    }


                    //Create a new connection on specified port
                    IPEndPoint endPoint = new IPEndPoint(ipAddress, port+6000);
                    Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    listener.Bind(endPoint);
                    //listener.Blocking = false;
                    listener.Listen(500);

                    return listener;

                }
                catch (Exception ex)
                {
                   throw ex;        
                }
                finally
                {

                }

                return null;            
            }

3 个答案:

答案 0 :(得分:2)

您的问题似乎是您正在启动异步任务,但在查询结果之前没有等待它们完成。

CreateListeners电话之后,您知道您已经开始了5项任务。您让任务库决定并行性或序列。

因此,您需要在socket.Wait();之前调用if (socket.Result != null),因为如果尚未完成,它可能会为空。

正如VirtualBlackFox所说,如果认为合适,它可能会顺序执行它们,这可能意味着当你开始查询结果时,只有一个任务已经完成。

作为一般答案,对我来说,使用平行主义进行此操作似乎并不过分。 IO类型的操作通常可以从并行性中获益,因为它们涉及大量的开销。

答案 1 :(得分:1)

如果你没有将你的任务标记为LongRunning,他们使用线程池线程(在当前实现中),TPL决定何时真正运行它们:它可以在同一个线程上一个接一个地串行运行它们它想要并且在你的情况下它不会是一件好事

Task<Socket>.Factory.StartNew(start, CancellationToken.None, 
    TaskCreationOptions.LongRunning, // The interpretation depends on scheduler
    TaskScheduler.Default // The default one place them in their own thread
    );

同样如我的评论所述

throw ex;

正从您的异常中删除所有堆栈跟踪信息,在调试时您永远不会想要这个,用户之一:

throw; // To re-throw the same exception
throw new Exception("bla", ex); // To specify a new message, or change the
                                // exception type. A new stack trace is created
                                // but you can still access the original one
                                // using InnerException

答案 2 :(得分:0)

这个链接很好地介绍了如何使用异步TCP,它使用IO线程池进行回调。

http://msdn.microsoft.com/en-us/library/5w7b7x5f(v=VS.100).aspx

由于您要绑定到多个端口而不是WaitOne,因此为每个套接字创建一个并使用WaitAny。这样你仍然可以听一个线程。

根据我对IO完成端口的异步回调的理解,在这些回调中不做很多工作。如果你需要做任何繁重的工作,请卸载到任务/线程。