2011-05-25 12 views
6

我正在用C#編寫客戶端/服務器應用程序,它很棒。目前,一切正常,而且都非常強大。我的問題是,在連接上發送數據包時遇到了一些延遲。使用TcpClient進行通信的更快捷方式?

在客戶端,我這樣做:

NetworkStream ns = tcpClient.GetStream(); 

// Send packet 

byte[] sizePacket = BitConverter.GetBytes(request.Length); 
byte[] requestWithHeader = new byte[sizePacket.Length + request.Length]; 
sizePacket.CopyTo(requestWithHeader, 0); 
request.CopyTo(requestWithHeader, sizePacket.Length); 

ns.Write(requestWithHeader, 0, requestWithHeader.Length); 

// Receive response 

ns.Read(sizePacket, 0, sizePacket.Length); 
int responseLength = BitConverter.ToInt32(sizePacket, 0); 
byte[] response = new byte[responseLength]; 

int bytesReceived = 0; 
while (bytesReceived < responseLength) 
{ 
    int bytesRead = ns.Read(response, bytesReceived, responseLength - bytesReceived); 
    bytesReceived += bytesRead; 
} 

(左了一些異常捕獲等),服務器則正好相反,即在NetworkStream.Read(),直到它有一個整個IT塊請求,然後處理它並使用Write()發送響應。

Write()/ Read()的原始速度不是問題(即發送大數據包很快),但是一個接一個地發送幾個小數據包而不關閉連接可能非常慢(延遲50-100毫秒)。奇怪的是,這些延遲出現在局域網連接上,典型的ping時間爲012毫秒,但是它們在本地主機上運行時不會發生,即使ping時間實際上是相同的(至少差異不應該是大約100毫秒)。如果我重新打開每個數據包的連接,這會對我有意義,導致很多握手,但我不是。就好像進入等待狀態的服務器拋出它與客戶端不同步,然後它重新建立了本質上是丟失的連接的一個絆腳石。

那麼,我做錯了嗎?有沒有辦法讓TcpServer和TcpClient之間的連接保持同步,以便服務器隨時可以接收數據? (反之亦然:有時處理來自客戶端的請求需要幾ms,然後客戶端似乎沒有準備好接收來自服務器的響應,直到在Read()被阻塞之後有一段時間纔會喚醒爲止。 )

+0

只是出於好奇你是否嘗試使用StreamReader? – 2011-05-25 16:13:09

+1

您爲什麼不使用[WCF](http://msdn.microsoft.com/zh-cn/netframework/aa663324)的任何特定原因? 這個框架包含了所有你想要做的和更多內置的東西。 – Jay 2011-05-25 16:04:06

+2

與此相反IMO; WCF非常臃腫,大多數人只使用它的一小部分。原始套接字通常很好,通常效率更高。 – 2011-05-25 17:48:50

回答

7

原來我的服務器和客戶端不是完全對稱的。我注意到了,但我認爲它根本不重要。顯然這是一筆鉅額交易。具體而言,服務器這樣做:

ns.Write(sizePacket, 0, sizePacket.Length); 
ns.Write(response, 0, response.Length); 

其中我變爲這樣:

// ... concatenate sizePacket and response into one array, same as client code above 
ns.Write(responseWithHeader, 0, responseWithHeader.Length); 

而現在的延遲完全消失,或至少它不再以毫秒爲單位衡量的。所以這就像是一個100倍的加速。 \ o/

它仍然很奇怪,因爲它和以前一樣寫入與套接字完全相同的數據,所以我猜測套接字在寫操作期間會收到一些祕密元數據,然後以某種方式傳遞給遠程套接字,這是一個小睡的機會。不管是這樣,還是第一次寫入都會將套接字置於接收模式,導致它在接收到任何東西之前被要求再次發送時跳起來。

我想這意味着你會發現所有這些示例代碼都在其周圍,它展示瞭如何在固定大小的塊中寫入和讀取套接字(通常在前面有一個描述數據包大小的int跟我的第一個版本一樣),沒有提到這樣做會有非常嚴重的性能損失。

+9

只是爲了解釋延遲,我相信你在Windows套接字中擊中了Nagles算法:小包將被放入緩衝區以提高網絡整體性能。每個數據包在到達網絡對等體之前可能會看到200毫秒的延遲:http://support.microsoft.com/kb/214397/en-us http://msdn.microsoft.com/zh-cn/library/system.net .sockets.tcpclient.nodelay.aspx – 2011-05-26 11:08:05

+1

恩,謝謝你,這是相當豐富的。我希望微軟能夠更好地連接到MSDN那樣的優秀文章。或者,也許我只需要知道去哪裏看。無論哪種方式,我現在覺得更聰明。仍然有點奇怪,我測量了〜100毫秒的延遲,沒有像Winsock緩衝區的200毫秒超時,但也許我測量它是錯誤的。 – ReturningTarzan 2011-05-26 14:05:56

+4

這對微軟來說並不特定(除了他們自己的算法選擇外)。任何生產TCP協議堆棧都將有利於通過小型協議發送大型數據包以減少開銷所以,當你向TCP流寫入少量數據時,它會被緩存,希望其他一些寫操作會發生。一個好的TCP堆棧算法努力平衡低延遲(通過立即發送)和高帶寬(通過使用盡可能大的包大小)。大多數堆棧都提供了一種手動刷新緩衝區的方法,或者爲了獲得更好的性能,在特定的套接字上指定沒有延遲。 – David 2011-05-26 14:29:20