2014-03-26 33 views
0

我在寫一個C#客戶端應用程序,它將連接到用python編寫的服務器。我的問題是關於循環接收數據。應用程序結構全部關於客戶端請求服務器 - >服務器響應客戶端。當消息低於實際緩衝區大小(在服務器中設置)時,一切正常。例如:服務器端緩衝區:1024,客戶端緩衝區大小:256,數據長度< 1kb。運行我用下面的代碼應用:在循環中接收網絡數據的正確方法

int datacounter = 0; 
byte[] recived = new byte[256]; 
StringBuilder stb = new StringBuilder(); 
serverStream.ReadTimeout = 1500; 
try 
{ 
    while ((datacounter = serverStream.Read(recived, 0, 256)) > 0) 
    { 
     Console.WriteLine("RECIVED: " + datacounter.ToString()); 
     stb.append(System.Text.Encoding.UTF8.GetString(recived, 0, datacounter)); 
    } 
} 
catch { Console.WriteLine("Timeout!"); } 

然後應用程序接收數據,在4個循環(每個256個字節):

RECIVED: 256 
RECIVED: 256 
RECIVED: 256 
RECIVED: 96 

然後超時蜱,即結束該傳輸並通過完整數據以後分析(來自stb對象)。我不認爲使用超時是正確的,但我不知道有任何其他方式來做到這一點。 但是,這種方式起作用。下面我們以示例爲例,它不包含: 服務器端緩衝區:1024,客戶端緩衝區:256,數據長度〜8kbytes(python端在循環中發送數據)。

RECIVED: 256 
RECIVED: 256 
RECIVED: 256 
RECIVED: 256 

然後超時滴答(顯然數據不完整 - 得到8kb的1kb)。有時循環甚至在1次運行後結束,28個recived字節,並且在超時之前。 Python表示數據已經正確發送。下面是我創建套接字和serverStream對象的方式:

TcpClient clientSocket = new TcpClient(); 
clientSocket.Connect("x.y.z.x", 1234); 
NetworkStream serverStream = clientSocket.GetStream(); 

它不是TcpClient故障。嘗試與清除套接字一樣,創建像:

​​

但是,它的工作原理類似。有沒有辦法,讓我的循環無超時工作,接收所有數據?如果可能,我想保持套接字同步。

回答

0

爲什麼不拋出異常讓你的代碼進入catch分支並找出? :)

catch (Exception ex) { Console.WriteLine("Timeout because of... " + ex.Message); } 

--edit 對不起,我沒看到timout。你問的問題是,如果有一種方法可以不超時。是的,不要設置任何超時並檢查接收到的字節數是否小於緩衝區大小。

即:

while ((datacounter = serverStream.Read(recived, 0, 256)) > 0) 
    { 
     Console.WriteLine("RECIVED: " + datacounter.ToString()); 
     stb.append(System.Text.Encoding.UTF8.GetString(recived, 0, datacounter)); 
     if(datacounter < 256) //you're good to go 
      break; 
    } 
+0

這是套接字讀取超時異常。 –

+0

好的抱歉,我沒有趕上serverStream.Timeout屬性。你完全確定這不是客戶問題嗎? 你可以使用Wireshark來嗅探,看看是否確實有超過1.5秒的超時。 – CharlieBrown

+0

Wireshark聽起來像一個好主意,我稍後會嘗試。我很確定這是一個客戶端問題,使用python編寫的類似客戶端根本沒有任何問題。今天還重新安裝了.NET,以檢查它是否沒有損壞 - 事實並非如此。關於你的編輯 - 如果最後一個數據包偶然會長256? :] –

0

我不認爲有什麼不對您收到的代碼功能。我把一個測試放在一起,接收器可以發送儘可能多的數據(例如8 MB),只要你在發送超時前暫停發送1.5秒。

所以它看起來像你的服務器根本沒有發送「快」足夠。

要回答您的問題,計時並不是知道何時收到完整消息的典型方法。確定收到完整消息時的一個常見而簡單的方法是在發送端填充完整消息的長度(例如,4字節int)。然後在接收端,首先讀取4個字節,解碼爲長度,然後讀取更多的字節。

您也可以考慮將消息終止字符串(例如Environment.NewLine)附加到消息的末尾。這具有可以調用StreamReader.ReadLine()的優點,它將阻塞,直到收到完整的消息。這隻有在消息本身不能包含終止時纔有效。

如果您無法更改服務器協議,是否有任何其他方式知道您已收到完整的消息? (例如,在消息結尾處檢查NewLine,XML結束標記或其他某種模式)。如果沒有,也許您可​​以等待服務器斷開連接,否則看起來您將被迫找到正確的定時平衡。

我在下面的測試代碼,以防萬一你想玩它。

服務器/發送方:

 IPAddress localAddr = IPAddress.Parse("127.0.0.1"); 
     TcpListener server = new TcpListener(localAddr, 13579); 
     server.Start(); 
     TcpClient clientSocket = server.AcceptTcpClient(); 
     NetworkStream stream = clientSocket.GetStream(); 

     int bytesSent = 0; 
     int bytesToSend = 1 << 25; 
     int bufferSize = 1024; 
     string testMessage = new string('X', bufferSize); 
     byte[] buffer = UTF8Encoding.UTF8.GetBytes(testMessage); 

     while (bytesSent < bytesToSend) 
     { 
      int byteToSendThisRound = Math.Min(bufferSize, bytesToSend - bytesSent); 
      stream.Write(buffer, 0, byteToSendThisRound); 
      bytesSent += byteToSendThisRound; 
     } 

客戶端/接收端:

 TcpClient client = new TcpClient("127.0.0.1", 13579); 
     NetworkStream serverStream = client.GetStream(); 

     int totalBytesReceived = 0; 
     int datacounter = 0; 
     byte[] recived = new byte[256]; 
     StringBuilder stb = new StringBuilder(); 
     serverStream.ReadTimeout = 1500; 
     try 
     { 
      while ((datacounter = serverStream.Read(recived, 0, 256)) > 0) 
      { 
       totalBytesReceived += 256; 
       Console.WriteLine("RECIVED: {0}, {1}", datacounter, totalBytesReceived); 
       stb.Append(System.Text.Encoding.UTF8.GetString(recived, 0, datacounter)); 
      } 
     } 
     catch { Console.WriteLine("Timeout!"); } 
相關問題