2013-12-17 50 views
1

我正試圖捕獲特定端口上的所有傳入事件。它工作正常,但我認爲,我的Socket被阻塞5秒,而stream.Read()正在執行。 stream.Read()忙時,如果有包裹進入,會發生什麼情況?我怎麼能避免可能的數據丟失?C#在沒有busyTimes的情況下在tcp網絡中偵聽?

public static void Main(string[] args) 
    { 

     System.Console.WriteLine("Testprogramm Connection\n"); 
     //ThreadListener tl = new ThreadListener(); 
     DateTime startTime3 = DateTime.Now; 
     DateTime startTime4 = DateTime.Now; 

     TimeSpan duration3; 
     TimeSpan duration4; 

     while (true) 
     { 
      DataPackage dp = Connection.readEvent(4002, 3000); 

      DateTime EndZeit = DateTime.Now; 
      if (dp.Ip.Equals("192.168.101.3")) 
      { 
       duration3 = EndZeit - startTime3; 
       System.Console.WriteLine("Duration to last receive: " + duration3); 
       startTime3 = DateTime.Now; 
      } 
      if (dp.Ip.Equals("192.168.101.4")) 
      { 
       duration4 = EndZeit - startTime4; 
       System.Console.WriteLine("Duration to last receive: " + duration4); 
       startTime4 = DateTime.Now; 
      } 
      System.Console.WriteLine(); 
     } 
    } 

    ...Connection.cs 
    public static DataPackage readEvent(int port, int size) 
    { 
     DataPackage dp = new DataPackage(); 
     byte[] bytes = new byte[size]; 
     String ip_client = ""; 
     IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName()); 
     IPAddress ipAddress = ipHostInfo.AddressList[1]; 
     try 
     { 
      System.Console.Write("Wait...:"); 
      listener = new TcpListener(ipAddress, port); 
      listener.Start(); 
      tcpClient = listener.AcceptTcpClient(); 
      tcpClient.NoDelay = true; 
      tcpClient.Client.NoDelay = true; 

      ip_client = tcpClient.Client.RemoteEndPoint.ToString(); 

      stream = tcpClient.GetStream(); 
      DateTime startTime = DateTime.Now; 
      stream.Read(bytes, 0, bytes.Length); 
      DateTime endTime = DateTime.Now; 

      stream.Close(); 
      tcpClient.Close(); 
      listener.Stop(); 
      dp.Data = bytes; 
      dp.setIP(ip_client); 

      System.Console.Write("...received from " + ip_client + " busy:"); 
      TimeSpan duration = endTime - startTime; 
      System.Console.WriteLine(duration.Seconds + "s"); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e.Message); 
      closeAllConnections(); 
      return null; 
     } 
     return dp; 
    } 

enter image description here

回答

1

我並不感到驚訝,你看到事件間的延遲。在您的readEvent方法中,您正在創建套接字,然後偵聽傳入連接,然後執行read(),然後關閉所有套接字。需要一點時間來設置您的套接字。如果您創建連接類的套接字成員,執行初始化(創建,偵聽和連接),然後在readEvent方法中檢索消息,那麼這是更好的方法。更奇特的做法可能是排隊所有的事件,以便您的連接的客戶端可以去拿起它們,但這涉及到一些異步編程。

 public class Connection : IDisposable { 
    private readonly Queue<DataPackage> messages = new Queue<DataPackage>(); 

    public object syncLock = new Object(); 
    public List<DataPackage> GetCurrentMessages() { 
     Monitor.Enter(syncLock); 
     var result = new List<DataPackage>(); 
     try { 
      foreach (var item in messages) { 
       result.Add(item); 
      } 
      messages.Clear(); 
     } finally { 
      Monitor.Exit(syncLock); 
     } 
     return result; 
    } 

    private TcpListener listener; 
    private TcpClient tcpClient; 
    private int bufferSize; 
    private bool running; 
    public void Init(int port, int size) { 
     // not real thread safe here 
     if (running) return; 
     running = true; 
     bufferSize = size; 
     IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName()); 
     IPAddress ipAddress = ipHostInfo.AddressList[1]; 
     listener = new TcpListener(ipAddress, port); 
     listener.Start(); 
     listener.BeginAcceptSocket(ar => { 
      tcpClient = listener.AcceptTcpClient(); 
      tcpClient.NoDelay = true; 
      listener.EndAcceptTcpClient(ar); 
      var t = new Thread(readEvent); 
      t.Start(); 
     }, null); 
    } 

    private bool shouldRead = true; 
    private void readEvent() { 

     while (shouldRead) { 
      byte[] bytes = new byte[bufferSize]; 

      try { 
       Console.Write("Wait...:"); 

       var ip_client = tcpClient.Client.RemoteEndPoint.ToString(); 

       var stream = tcpClient.GetStream(); 
       DateTime startTime = DateTime.Now; 
       stream.Read(bytes, 0, bytes.Length); 
       DateTime endTime = DateTime.Now; 
       var package = new DataPackage(); // add your stuff in here 

       Monitor.Enter(syncLock); 
       try { 
        messages.Enqueue(package); 
       } finally { 
        Monitor.Exit(syncLock); 
       } 

       System.Console.Write("...received from " + ip_client + " busy:"); 
       TimeSpan duration = endTime - startTime; 
       System.Console.WriteLine(duration.Seconds + "s"); 
      } catch (Exception e) { 
       Console.WriteLine(e.Message); 
      } 
     } 

    } 

    public class DataPackage {} 

    #region IDisposable Members 
    private bool disposed; 
    void Dispose(bool disposing) { 
     if (!disposed) { 
      if (disposing) { 
       shouldRead = false; 
       tcpClient.Close(); 
       listener.Stop(); 
      } 
      //Clean up unmanaged resources 
     } 
     disposed = true; 
    } 
    public void Dispose() { 
     Dispose(true); 
    } 
    #endregion 
} 
+0

你能舉個例子嗎? –

+0

我曾嘗試儘可能提前啓動。但沒有注意到一些改進。我曾考慮過線程,但我認爲使用套接字多次或連接到同一端口的多個套接字可能會有問題... –

+1

我添加了一個可能遵循的示例。我在VS中很快就把它掀起來了,所以它應該可以編譯,但它沒有完全測試。你應該從中得到想法。 –

0

你的TCP監聽器可能正在等待緩衝區填滿。

  • 減小緩衝區大小;
  • PUSH bit集發送數據包;或
  • 您的客戶正在等待建立一個重要的數據包大小。

要設置緩衝區大小,請使用TcpClient.ReceiveBufferSize屬性。

爲了避免拖延而等待緩衝區填滿時,TcpClient.NoDelay屬性設置爲true

+0

我無法編輯客戶端發送程序,我也需要這個緩衝區大小,以獲得整個包。 TcpClient.NoDelay沒有改變任何東西 –

+0

你可以增加你的載荷;除非有特定的原因,否則不需要將緩衝區設置爲與您計劃的有效負載一樣大。例如,假設您的最終有效負載爲1MB。使緩衝區大小爲64KB。你會閱讀()20次;將每個收到的部分有效載荷添加到本地緩衝區 – OnoSendai

+0

現在,我獲得10秒的忙時間而不是5秒 –

1

我估計沒有任何數據丟失。在你的大小寫塊線程中使用Read方法,直到第一筆數據量到來(甚至1字節)。理論上,如果客戶會保持「沉默」,您可以等待無限的時間。如果你想限制的等待時間,使用tcpClient.ReceiveTimeout,像這樣:

tcpClient.ReceiveTimeout = 3000; 

於是,就有了一切完全正確的代碼,問題是客戶保持沉默5秒。

PS:你爲什麼不使用IPAddress.Any?在某些情況下,此Dns.GetHostEntry(Dns.GetHostName())可能需要很長時間。

var listener = new TcpListener(IPAddress.Any, port); 
+0

感謝您的提示。 –

相關問題