【问题标题】:How do I create a new thread each time a new connection is received in C#?每次在 C# 中收到新连接时,如何创建一个新线程?
【发布时间】:2011-09-19 03:04:04
【问题描述】:

所以,我对多线程和套接字编程有点陌生,尤其是在 C# 中。

无论如何,我正在尝试创建一个程序,将每个新接受的 TcpClient 创建为一个新线程。

这是我做的:

    public static void Listen()
    {
        try
        {
            listener = new TcpListener(IPAddress.Any, port);
            listener.Start();

            while (true)
            {
                t = new Thread((client = listener.AcceptTcpClient));

            }
        }
        catch { Listen(); }
    }

我也已经将 listener 声明为 TcpListener,t 声明为 Thread,client 声明为 TcpClient。

代码很好,除了我创建线程的地方,它给了我一个错误。

我的最终目标是为每个接受的连接创建一个新线程,然后能够将数据发送到特定连接。

那么,如何在新线程中创建每个连接/客户端?如何从另一个名为 Send 的方法引用某个线程/连接(通过流仅将数据发送到特定线程/连接)?

【问题讨论】:

  • -1 用于提及错误,但未提供更多信息。错误是什么?它是一个例外吗?哪里出错了?
  • 它出现了多个错误,但这一切都围绕着它不是开始新线程的正确“类型”。

标签: c# multithreading sockets


【解决方案1】:

既然您提到您是线程和套接字的新手,我将建议您改变方法。为每个传入连接创建一个新线程将无法很好地扩展。 1000 个用户产生 1000 个线程,您的服务器将花费大部分时间进行上下文切换。相反,您应该考虑使用异步 I/O 方法(例如 TcpListener.BeginAcceptTcpClient)。仅当需要执行某些操作时,才会在 .NET 线程池线程上调用您提供给此方法的回调。只需确保同步对实例变量的访问(例如,通过 lock 语句),因为如果两个客户端同时连接,回调可能会并行运行(当然,这是目标)。祝你好运。

【讨论】:

  • 如何使用线程池防止 DOS 攻击?我想我上一次使用套接字代码编写和运行项目时是 18 岁,而 DOS 绝对是一个持续存在的问题。我花了整整 6 个月的时间才设法让服务器处于玩家无法破坏它的状态。如果您使用池,恶意行为者不能有效地阻止一个或多个共享线程并影响其他连接吗?
【解决方案2】:

这是我的套接字服务器示例的启动方法。注意 new thread 将 new ThreadStart 作为参数。如果需要,我可以发送更多样品。

它使用 ThreadPool.QueueUserWorkitem 而不是每个请求使用一个线程。看听者。

    public void Start()
    {
        m_protocol = LoadProtocolPlugIn();

        // Create a TcpListener to accept client connection requests
        TcpListener tcpListener = new TcpListener(m_address, m_port);
        tcpListener.Start();

        //
        // Create a protocol listener per thread
        //
        for (int i = 0; i < m_listeners; i++)
        {
            ProtocolListener listener = new ProtocolListener(tcpListener, m_protocol);
            Thread thread = new Thread(new ThreadStart(listener.Start));
            thread.Start();

            Console.WriteLine("Listening on thread: {0}", thread.Name);
        }

        m_state = ServerState.Started;
    }

这是协议监听器:

class ProtocolListener
{
    TcpListener m_listener;
    IProtocol   m_protocol = null;
    TcpClient   m_client = null;

    internal ProtocolListener(TcpListener listener, IProtocol protocol)
    {
        m_listener = listener;
        m_protocol = protocol;
    }

    internal void Start()
    {
        //
        // Block waiting for socket connection and then process.  Repeat in endless loop.
        //
        while (true)
        {
            try
            {
                m_client = m_listener.AcceptTcpClient();
                ThreadPool.QueueUserWorkItem (new WaitCallback(ProcessClientProtocol), m_protocol);
            }
            catch (SocketException se)
            {
                // TODO: replace with logging and event log
                Console.WriteLine("Exception = " +  se.Message);
            }
        }
    }

    private void ProcessClientProtocol (object protocol)
    {
        Debug.Assert(m_client != null);
        Debug.Assert(protocol != null);

        ((IProtocol)protocol).Client = m_client;
        ((IProtocol)protocol).ProcessClient();
    }
}

【讨论】:

    【解决方案3】:

    这不是一个很好的做事方式。您应该查看套接字的 BeginXXX/EndXXX 模式或异步模式。

    http://msdn.microsoft.com/en-us/library/bbx2eya8.aspx

    【讨论】:

      【解决方案4】:

      Thread 构造函数接受一个委托,而您正试图给它一个对象。 创建线程时,需要提供在该线程中执行的方法。例如:

      private void HandleClient(TcpClient client)
      {
         // ... 
      }
      

      但是,创建自己的线程通常不是一个好主意。如果您使用的是 .NET 4,则应使用 ThreadPool 或任务并行库:

      ThreadPool.QueueUserWorkItem(() => HandleClient(acceptedClient));
      

      Task.Factory.StartNew(() => HandleClient(acceptedClient));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-08-13
        • 2017-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多