【问题标题】:How can I keep a Console App open when all processes are running on separate threads in c#?当所有进程都在 c# 的单独线程上运行时,如何保持控制台应用程序打开?
【发布时间】:2019-04-24 08:52:33
【问题描述】:

我正在尝试制作一个简单的服务器应用程序来学习 C# 中多线程服务器编程的基础知识。基本思想很简单:客户端连接到服务器并发送:“get time”来接收服务器的当前时间。所有 tcplistener 线程和套接字都应该在单独的线程上运行。我不知道为什么,但是当应用程序完成所有线程的初始化时,控制台应用程序就会关闭。

这是服务器代码:

class Program
{
    static public int minPort;
    static public int maxPort;
    static int openPort = 0;
    static byte[] sendData = new byte[1024];
    static TcpListener[] listeners;
    static Thread[] connectionThreads;

    static void Main()
    {
        Console.Write("What do you want your minimum port to be? ");
        minPort = Convert.ToInt32(Console.ReadLine());
        Console.Write("What do you want your maximum port to be? ");
        maxPort = Convert.ToInt32(Console.ReadLine());

        //init
        ThreadStart streamThreadStart = new ThreadStart(DataStream);
        openPort = maxPort - minPort;
        listeners = new TcpListener[maxPort - minPort];
        connectionThreads = new Thread[maxPort - minPort];

        for (int i = 0; i == maxPort - minPort; i++)
        {
            listeners[i] = new TcpListener(IPAddress.Any, minPort + i);
            connectionThreads[i] = new Thread(streamThreadStart);
            Thread.Sleep(10);
            openPort = openPort + 1;
        }
    }

    static void DataStream()
    {
        int port = openPort;
        byte[] receiveData = new byte[1024];
        listeners[openPort].Start();
        Socket socket = listeners[port].AcceptSocket();
        NetworkStream stream = new NetworkStream(socket);
        while (true)
        {
            socket.Receive(receiveData);
            Console.WriteLine("Received: " + BitConverter.ToString(receiveData));
            socket.Send(parseCommand(receiveData));
        }

    }
    static byte[] parseCommand(byte[] input)
    {
        string RCommand = BitConverter.ToString(input);
        string SCommand;
        if (RCommand == "get time")
        {
            SCommand = DateTime.Now.ToUniversalTime().ToString();
        }else
        {
            SCommand = "Unknown Command, please try again";
        }
        byte[] output = Encoding.ASCII.GetBytes(SCommand);
        return output;
    }
}

【问题讨论】:

  • 在控制台应用程序中,当 main 完成时,应用程序将关闭,因为没有更多代码可以执行。您需要在 main 中构建一个循环以保持控制台应用程序运行。
  • 您知道一个侦听器套接字可以服务多个客户端并移交接受的套接字吗?除非您正在做一些非常奇怪的事情,否则永远不需要在多个端口上运行侦听器。例如。普通的 Web 服务器监听端口 80 和/或 443。你认为这些处理数千个客户端的服务器一次只处理一个客户端吗?这里需要大量的研究/阅读。
  • 另外,您必须注意来自Receive 的返回值和道德等价物。因为 TCP 给你的只是一个字节流,而不是消息。它不保证会填满您的缓冲区,或者您将收到的内容与传递给另一端 Send 的单个调用的单个缓冲区匹配。
  • 另一种选择是创建一个Windows Service,它旨在长期运行而不关闭。

标签: c# .net multithreading sockets console-application


【解决方案1】:

您需要在退出之前join您的线程。

static public void Main()
{
    /*
        Existing code goes here
    */

    //Before exiting, make sure all child threads are finished
    foreach (var thread in connectionThreads) thread.Join(); 
}

当你的程序调用 Thread.Join 时,它告诉操作系统它不需要为任何时间片调度主线程,直到子线程完成。这使得它比其他技术更轻,例如busywaiting(即运行while循环);虽然主线程仍然会占用资源,但它不会消耗任何 CPU 时间。

另见When would I use Thread.Join?c# Waiting for multiple threads to finish

【讨论】:

  • 虽然我喜欢这个答案,因为它讨论了将线程连接回主线程的正确方法。它不处理无法控制的控制台应用程序的问题。解决方案是,如 Windows 服务所建议的那样,或更改主线程以便它也对用户输入做出反应。
【解决方案2】:

一般来说,最佳做法是让控制台应用程序在用户想要停止应用程序时提供“输入任意键退出”提示。但是您始终可以查询要退出的特定按键,例如“q”。

这里有一些代码可以帮助您入门:

Console.WriteLine("Press any key to exit..."); Console.ReadKey();

【讨论】:

    【解决方案3】:

    您的代码没有启动新线程! 因此,我对您的代码进行了一些更改:

    class Program
        {
            static public int minPort;
            static public int maxPort;
            //static int openPort = 0;
            static byte[] sendData = new byte[1024];
            static TcpListener[] listeners;
            static Thread[] connectionThreads;
    
            static void Main()
            {
                Console.Write("What do you want your minimum port to be? ");
                minPort = Convert.ToInt32(Console.ReadLine());
                Console.Write("What do you want your maximum port to be? ");
                maxPort = Convert.ToInt32(Console.ReadLine());
    
                //init
               // ThreadStart streamThreadStart = new ThreadStart(DataStream(0));
                //openPort = minPort;
                listeners = new TcpListener[maxPort - minPort];
                connectionThreads = new Thread[maxPort - minPort];
    
                //for (int i = 0; i == maxPort - minPort; i++) it can't work
                for (int i = 0; i < maxPort - minPort; i++)
                {
                    listeners[i] = new TcpListener(IPAddress.Any, minPort + i);
                    connectionThreads[i] = new Thread(new ParameterizedThreadStart(DataStream)); //thread with start parameter
                    connectionThreads[i].Start(i); // start thread with index
                    Thread.Sleep(10);
    
                }
    
            }
    
            static void DataStream(object o)
            {
                int index = (int)o; //get index
                byte[] receiveData = new byte[1024];
                listeners[index].Start(); // start listener with right index
                Socket socket = listeners[index].AcceptSocket();
                NetworkStream stream = new NetworkStream(socket);
                while (true)
                {
                    socket.Receive(receiveData);
                    Console.WriteLine("Received: " + BitConverter.ToString(receiveData));
                    socket.Send(parseCommand(receiveData));
                }
    
            }
            static byte[] parseCommand(byte[] input)
            {
                string RCommand = BitConverter.ToString(input);
                string SCommand;
                if (RCommand == "get time")
                {
                    SCommand = DateTime.Now.ToUniversalTime().ToString();
                }
                else
                {
                    SCommand = "Unknown Command, please try again";
                }
                byte[] output = Encoding.ASCII.GetBytes(SCommand);
                return output;
            }
        }
    

    【讨论】:

    • 请详细描述您所做的更改以及为什么它会成为答案。
    猜你喜欢
    • 2010-10-16
    • 1970-01-01
    • 2018-06-18
    • 2011-02-04
    • 2011-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多