2014-03-26 32 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!"); } 
相关问题