2017-06-20 18 views
-1

對於一個學校項目,我正在編寫一個UDP偵聽器(使用C#),它將接收數千個UDP數據包並將數據保存到數據庫(模擬)中。現在它將數據保存在遠程數據庫中。什麼是最好的方法來處理數千個UDP數據包並將它們保存到使用線程的遠程數據庫中?

我想知道什麼是最好的方式來聽包,失去儘可能少。

所以,我有一個線程,只偵聽數據包,當收到一個數據包時,我創建一個新線程來解析並將接收到的消息保存到遠程數據庫,後者將會有一個網頁顯示數據。

1 - 我應該創建線程來處理每個數據包還是有更好的方法?最多允許多少個線程?

2 - 我正在考慮使用本地數據庫而不是遠程數據庫來爲UDP偵聽器存儲數據。我認爲每條線索都將以這種方式更快結束。還有一個網頁將使用相同的數據庫來顯示客戶端的信息(該數據庫是php服務器的本地數據庫)。該網頁也會查詢數據庫很多,我不知道哪個是最好的...對於數據庫是UDP偵聽器的本地數據庫,或者是創建網頁的PHP服務器的本地數據庫。

3 - 應該有db.query()的鎖嗎?我有它的評論,它似乎工作正常。這個函數是對數據庫的插入。

4 - 有些人告訴我,我應該在每次插入之前/之後打開()/關閉()數據庫連接...現在,當我啓動偵聽器並僅檢查它是否關閉之前打開它插入。

5 - 在這種情況下Async/Await可以幫助我的代碼嗎?也許使用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) { } 
    } 
+2

不要啓動顯式線程來處理UDP連接,更不用說每個數據包,因爲它不會擴展,您無疑會耗盡操作系統。而是使用IOCP,如通過**異步/等待提供** – MickyD

回答

0

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 - 有些人告訴我,我應該在每次插入之前/之後打開()/ close()數據庫連接...現在,當我啓動偵聽器並僅檢查它是否是在插入之前關閉。

通常你會打開和關閉每個操作。開銷會很小,因爲「關閉」連接並不會真正關閉它 - 它只是將它發送回連接池。 「開幕」只是抓住了它。

話雖這麼說,在你的具體情況,它可以一個更好的主意,以保持它打開,因爲你只是在做一件事一遍又一遍,你需要它來儘可能快地運行。此外,您不需要連接池 - 您始終需要一個連接(每個線程)。

5 - 在這種情況下Async/Await可以幫助我的代碼嗎?也許使用Async插入數據庫會使線程更快結束?

如果您使用我推薦的雙線程方法,await和async將無助於任何事情。如果你有一個專用的工作線程,在繼續之前需要await以前的任何async,所以你仍然會一直處理一個數據包。

6 - 我在DataGridView上顯示數據,但這似乎很慢,有沒有更好的方法在主窗體上顯示數據?

如果你有一個非常高的數據量,那麼任何形式的控制將是平淡無奇。如果您使用綁定控件並且每次都刷新整個數據集,則會更糟糕。嘗試一次使用未綁定的DataGridView和add the data manually

+0

非常感謝您的所有意見!我會嘗試你的兩個線程方法。 – Renato

相關問題