Closed. This question needs to be more focused。它当前不接受答案。
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            想改善这个问题吗?更新问题,使其仅通过editing this post专注于一个问题。
                        
                        2年前关闭。
                                                                                            
                
        
对于一个学校项目,我正在编写一个UDP侦听器(使用C#),它将接收数千个UDP数据包并将数据保存在数据库中(模拟)。现在,它将数据保存在远程数据库中。

我想知道什么是侦听数据包并尽可能少丢失的最佳方法。

因此,我有一个仅侦听数据包的线程,当接收到数据包时,我创建了一个新线程来解析并将接收到的消息保存在远程数据库中,后者将提供一个显示数据的网页。

1-我应该创建线程来处理每个数据包还是有更好的方法?应该允许的最大线程数是多少?

2-我正在考虑使用本地数据库而不是远程数据库供UDP侦听器存储数据。我认为每个线程将以这种方式更快结束。还有一个网页将使用相同的数据库显示客户端信息(该数据库位于php服务器本地)。该网页还会查询很多数据库,我不确定哪个是最好的...数据库对于UDP侦听器来说是本地的,对于创建该网页的php服务器而言是本地的。

3-db.query()是否应该有锁?我对此发表了评论,它似乎运作良好。该功能是对数据库的插入。

4-有人告诉我我应该在每次插入之前/之后open()/ close()数据库连接...现在我在启动侦听器时将其打开,并且仅在插入之前检查它是否关闭。

5-在这种情况下,异步/等待可以帮助我的代码吗?也许使用Async插入数据库将使线程结束更快?

6-我在DataGridView上显示数据,但这似乎很慢,是否有更好的方法在Main Form上显示数据?

我将实现一种方法,以确认接收到的每个数据包,并且如果确认失败,客户端将重新发送,但是当每分钟接收数千个数据包时,我仍然希望它尽可能地失败。
任何有关如何使此过程更好的意见将不胜感激。

class UdpListener
{
    UdpClient listener;
    int port;
    byte[] receivedBytes;
    byte[] sendData;
    IPEndPoint ipep;
    IPEndPoint sender;
    Thread packetWorkerThread;
    DataBase db;
    MainForm mainForm;
    int threadCount, queryCount;

    public UdpListener(MainForm mainForm)
    {
        try
        {
            port = 1988;
            ipep = new IPEndPoint(IPAddress.Any, port);
            listener = new UdpClient(ipep);
            sender = new IPEndPoint(IPAddress.Any, 0);
            db = new DataBase();
            this.mainForm = mainForm;
            threadCount = queryCount = 0;
        }
        catch (Exception e) { }
    }

    public void start()
    {
        // Open db connection.
        //-- Maybe I should open/close before/after each insert?!
        if (db.connect())
        {
            while (true)
            {
                try
                {
                    receivedBytes = listener.Receive(ref sender);
                    Packet packetData = new Packet(sender, ipep, receivedBytes);

                    if(threadCount<10)
                    {
                        //Launch Thread to process received data and save it to Database
                        packetWorkerThread = new Thread(p =>
                        {
                            workerThread_ProcessPacket(packetData);
                        });
                        packetWorkerThread.Start();
                    }
                }
                catch (Exception e) { }
            }
        }
    }

    private void workerThread_ProcessPacket(Packet packetData)
    {
        try
        {
            lock (this)
            {
                threadCount++;
                queryCount++;
            }
            //lock (db)
            //{
                db.sqlQuery("SOME INSERT SQL");
            //}
            string data = GetHexStringFrom(packetData.ReceivedBytes);

            string[] row = new string[] { DateTime.Now.ToString(), packetData.FIP, packetData.FPort, packetData.TIP, packetData.TPort, data, threadCount.ToString(), queryCount.ToString() };
            mainForm.dataGridViewPackets.Invoke((MethodInvoker)delegate { mainForm.dataGridViewPackets.Rows.Add(row); });

            if (mainForm.dataGridViewPackets.RowCount >= 100)
            {
                mainForm.dataGridViewPackets.Invoke((MethodInvoker)delegate { mainForm.dataGridViewPackets.Rows.RemoveAt(0); });
            }

            lock (this)
            {
                threadCount--;
            }
        }
        catch (Exception e) { }
    }

最佳答案

1-我应该创建线程来处理每个数据包还是有更好的方法?应该允许的最大线程数是多少?


如果是我,我将只使用两个线程;一个监听UDP,一个监听数据库。他们将通过ConcurrentQueue进行通信。

private ConcurrentQueue<Packet> _queue = new ConcurrentQueue<Packet>();
private volatile bool _cancel;

void Listener() {
    while (!_cancel) {
        var packet = GetNextPacket(out packet);
        _queue.Enqueue(packet);
    }

void Processor() {
    while (!_cancel) {
        Packet packet;
        var ok = _queue.TryDequeue(out packet);
        if (ok) {
            SaveToDatabase(packet);
        }
        else {
            Sleep(100);
        }
    }
}


如果您确实想利用多个线程和连接,则可以启动Processor的多个实例。 ConcurrentQueue逻辑保证队列中的每个数据包仅被处理一次。

我的开始不超过处理器的核心数量。请注意,数据库仍然会成为瓶颈,因为所有连接通常都将尝试写入数据库中的同一数据页,并且会短暂地将彼此锁定,但是您仍然可以获得一些好处。


  2-我正在考虑使用本地数据库而不是远程数据库供UDP侦听器存储数据。我认为每个线程将以这种方式更快结束。还有一个网页将使用相同的数据库显示客户端信息(该数据库位于php服务器本地)。该网页还会查询很多数据库,我不确定哪个是最好的...数据库对于UDP侦听器来说是本地的,对于创建该网页的php服务器而言是本地的。


本地数据库不一定更好,因为该数据库随后将与您的程序共享资源,我认为这很忙。我个人将设计该软件以在任何位置使用数据库,然后以各种配置对其进行测试。


  3-db.query()是否应该有锁?我对此发表了评论,它似乎运作良好。该功能是对数据库的插入。


除非您的数据库很奇怪,否则默认锁定语义应该很好。通常,数据库是为多个客户端设计的。


  4-有人告诉我我应该在每次插入之前/之后open()/ close()数据库连接...现在我在启动侦听器时将其打开,并且仅在插入之前检查它是否关闭。


通常您会为每个操作打开和关闭。开销将是最小的,因为“关闭”连接实际上并没有关闭它-它只是将其发送回连接池。 “开放”只是抢回来。

话虽如此,在您的特定情况下,保持打开状态可能是一个更好的主意,因为您只是一遍又一遍地做一件事,并且您需要使其尽快运行。另外,您不需要连接池-始终需要一个连接(每个线程)。


  5-在这种情况下,异步/等待可以帮助我的代码吗?也许使用Async插入数据库将使线程结束更快?


如果您使用我建议的双线程方法,则等待和异步将无济于事。如果您有专用的工作线程,则在继续操作之前需要先await任何先前的async,因此您仍然无法一次处理一个数据包。


  6-我在DataGridView上显示数据,但这似乎很慢,是否有更好的方法在Main Form上显示数据?


如果您的数据量很大,那么任何表单控件都将变得乏味。如果您使用绑定控件并每次刷新整个数据集,那就更糟了。尝试一次使用未绑定的DataGridView和add the data manually,一次一行。

09-18 14:05