C# 处理数千个UDP数据包并用线程将其保存在远程数据库上的最佳方法是什么?

C# 处理数千个UDP数据包并用线程将其保存在远程数据库上的最佳方法是什么?,c#,mysql,multithreading,sockets,C#,Mysql,Multithreading,Sockets,对于一个学校项目,我正在编写一个UDP侦听器(使用C#),它将接收数千个UDP数据包,并将数据保存在数据库(模拟)中。现在它正在将数据保存到远程数据库中 我想知道什么是听信息包并尽可能少地丢失信息的最好方法 因此,我有一个线程,只监听数据包,当收到数据包时,我创建一个新线程来解析并将收到的消息保存在远程数据库上,后者将有一个网页显示数据 1-我应该创建线程来处理每个数据包,还是有更好的方法?允许的最大线程数是多少 2-我正在考虑使用本地数据库而不是远程数据库来为UDP侦听器存储数据。我认为这样每

对于一个学校项目,我正在编写一个UDP侦听器(使用C#),它将接收数千个UDP数据包,并将数据保存在数据库(模拟)中。现在它正在将数据保存到远程数据库中

我想知道什么是听信息包并尽可能少地丢失信息的最好方法

因此,我有一个线程,只监听数据包,当收到数据包时,我创建一个新线程来解析并将收到的消息保存在远程数据库上,后者将有一个网页显示数据

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

2-我正在考虑使用本地数据库而不是远程数据库来为UDP侦听器存储数据。我认为这样每个线程都会结束得更快。还有一个网页将使用相同的数据库来显示客户端的信息(该数据库是php服务器的本地数据库)。该网页还将查询数据库很多,我不知道这是最好的。。。对于UDP侦听器,数据库是本地的;对于创建网页的php服务器,数据库是本地的

3-是否应该为db.query()设置锁?我有它的评论,它似乎工作良好。此函数是对数据库的插入

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

5-在这种情况下,Async/Wait能帮助我编写代码吗?也许使用Async在数据库上插入会使线程结束得更快

6-我在DataGridView上显示数据,但这似乎非常慢,是否有更好的方法在主窗体上显示数据

我将实现一种方法,以便确认接收到的每个数据包,如果ack失败,客户端将重新发送,但我仍然希望在每分钟接收数千个数据包时,尽可能使其失败。
如果您对如何改进此流程有任何意见,我们将不胜感激

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) { }
    }
UdpListener类
{
UdpClient侦听器;
国际港口;
字节[]接收字节;
字节[]发送数据;
IPEndPoint-ipep;
i端点发送器;
线程包workerThread;
数据库数据库;
主窗体主窗体;
int threadCount,queryCount;
公共UdpListener(主表单主表单)
{
尝试
{
港口=1988年;
ipep=新的IPEndPoint(IPAddress.Any,端口);
侦听器=新的UdpClient(ipep);
发送方=新的IPEndPoint(IPAddress.Any,0);
db=新数据库();
this.mainForm=mainForm;
threadCount=queryCount=0;
}
捕获(例外e){}
}
公开作废开始()
{
//打开数据库连接。
//--也许我应该在每次插入之前/之后打开/关闭?!
if(db.connect())
{
while(true)
{
尝试
{
receivedBytes=listener.Receive(ref-sender);
packetpacketdata=新数据包(发送方、ipep、接收字节);
如果(线程数)
{
workerThread_ProcessPackage(packetData);
});
packetWorkerThread.Start();
}
}
捕获(例外e){}
}
}   
}
私有无效workerThread_ProcessPacket(数据包包数据)
{
尝试
{
锁(这个)
{
threadCount++;
queryCount++;
}
//锁(db)
//{
sqlQuery(“一些插入SQL”);
//}
string data=GetHexStringFrom(packetData.ReceivedBytes);
string[]行=新字符串[]{DateTime.Now.ToString(),packetData.FIP,packetData.FPort,packetData.TIP,packetData.TPort,data,threadCount.ToString(),queryCount.ToString()};
调用((MethodInvoker)委托{mainForm.dataGridViewPackets.Rows.Add(row);});
如果(mainForm.dataGridViewPackets.RowCount>=100)
{
调用((MethodInvoker)委托{mainForm.dataGridViewPackets.Rows.RemoveAt(0);});
}
锁(这个)
{
线程数--;
}
}
捕获(例外e){}
}
1-我应该创建线程来处理每个数据包,还是有更好的方法?允许的最大线程数是多少

如果是我,我只会用两条线;一个用于监听UDP,一个用于与数据库通信。他们将通过网络进行交流

private ConcurrentQueue _queue=new ConcurrentQueue();
私人易变bool\u取消;
void侦听器(){
while(!\u取消){
var数据包=GetNextPackage(输出数据包);
_排队(分组);
}
空处理器(){
while(!\u取消){
数据包;
var ok=\u queue.TryDequeue(输出数据包);
如果(确定){
SaveToDatabase(数据包);
}
否则{
睡眠(100);
}
}
}
如果您确实想利用多个线程和连接,可以启动多个
处理器的实例;队列中的每个数据包都由
ConcurrentQueue
逻辑保证只处理一次

我的起点不会超过处理器的内核数。请注意,数据库仍然是一个瓶颈,因为所有连接通常都会尝试写入数据库中的同一数据页,并会短暂地相互锁定,但您可能仍然会获得一些收益

2-我正在考虑使用本地数据库而不是远程数据库,以便UDP侦听器存储数据
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);
        }
    }
}