因此,似乎阻塞的Read()可以在接收到发送给它的所有数据之前返回。反过来,我们用一个循环来包装Read(),该循环由相关流中的DataAvailable值控制。问题是你可以在这个while循环中接收更多数据,但是没有幕后处理让系统知道这一点。我在网上找到的大部分解决方案都不适用于我。
我最终做的是作为循环的最后一步,在从流中读取每个块之后,我做了一个简单的Thread.Sleep(1)。这似乎给系统提供了更新的时间,而且我没有得到准确的结果,但这对于解决方案而言似乎有些过时且非常“间接”。
以下列出了我正在处理的情况:IIS应用程序和独立应用程序之间的单个TCP连接,都是用C#编写的,用于发送/接收通信。它发送请求然后等待响应。这个请求是由HTTP请求发起的,但是我没有从HTTP请求中读取数据这个问题,它是事后的。
以下是处理传入连接的基本代码
protected void OnClientCommunication(TcpClient oClient)
{
NetworkStream stream = oClient.GetStream();
MemoryStream msIn = new MemoryStream();
byte[] aMessage = new byte[4096];
int iBytesRead = 0;
while ( stream.DataAvailable )
{
int iRead = stream.Read(aMessage, 0, aMessage.Length);
iBytesRead += iRead;
msIn.Write(aMessage, 0, iRead);
Thread.Sleep(1);
}
MemoryStream msOut = new MemoryStream();
// .. Do some processing adding data to the msOut stream
msOut.WriteTo(stream);
stream.Flush();
oClient.Close();
}
所有反馈都欢迎提供更好的解决方案,或者只是赞一下需要让Sleep(1)在我们检查DataAvailable值之前允许正确更新。
猜猜我希望在2年之后this question的答案不是现状仍然如此:)
答案 0 :(得分:9)
我看到了这个问题
您期望通信速度比while()
循环快,这是不太可能的
只要没有更多数据,while()
循环就会完成,这可能不是退出后几毫秒的情况。
你期待一定数量的字节吗?
OnClientCommunication()
多久被解雇一次?是谁触发了它?
在while()
循环后,您如何处理数据?你是否继续追加以前的数据?
DataAvailable
将返回false,因为您的阅读速度比通信速度快,所以只有在您不断回到此代码块处理更多数据时才会这样做。
答案 1 :(得分:8)
您必须知道需要阅读多少数据;你不能简单地循环读取数据,直到没有更多的数据,因为你永远不能确定不会再有了。
这就是HTTP GET结果在HTTP标头中有字节数的原因:因此客户端将知道它何时收到了所有数据。
根据您是否可以控制对方发送的内容,以下是两种解决方案:
使用“成帧”字符:(SB)数据(EB),其中SB和EB是起始块和结束块字符(根据您的选择),但不能在数据内部出现。当你“看到”EB时,你知道你已经完成了。
在每条消息前面实现一个长度字段,以指示后面有多少数据:(len)数据。读(len),然后读(len)字节;必要时重复。
这与读取零长度读取意味着数据结束的文件不同(这意味着另一方已断开连接,但这是另一个故事)。
第三种(不推荐)解决方案是您可以实现计时器。 一旦开始获取数据,请设置计时器。如果接收循环空闲一段时间(比如几秒钟,如果数据不常出现),您可能会假设没有更多的数据即将到来。最后一种方法是最后的手段......它不是很可靠,很难调整,而且很脆弱。
答案 2 :(得分:2)
我试图在从网络流中读取数据之前检查DataAvailable并且它将返回false,尽管在读取单个字节后它将返回true。所以我检查了MSDN文档,他们也在检查前阅读。我会将while循环重新安排到do while循环以遵循这种模式。
http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.dataavailable.aspx
// Check to see if this NetworkStream is readable.
if(myNetworkStream.CanRead){
byte[] myReadBuffer = new byte[1024];
StringBuilder myCompleteMessage = new StringBuilder();
int numberOfBytesRead = 0;
// Incoming message may be larger than the buffer size.
do{
numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));
}
while(myNetworkStream.DataAvailable);
// Print out the received message to the console.
Console.WriteLine("You received the following message : " +
myCompleteMessage);
}
else{
Console.WriteLine("Sorry. You cannot read from this NetworkStream.");
}
答案 3 :(得分:2)
当我有这段代码时:
var readBuffer = new byte[1024];
using (var memoryStream = new MemoryStream())
{
do
{
int numberOfBytesRead = networkStream.Read(readBuffer, 0, readBuffer.Length);
memoryStream.Write(readBuffer, 0, numberOfBytesRead);
}
while (networkStream.DataAvailable);
}
从我可以观察到:
是的,这就是这些图书馆工作的方式。他们需要有时间运行以完全验证传入的数据。 - James Apr 20' 16 at 5:24
当我有这段代码时:
var readBuffer = new byte[1024];
using (var memoryStream = new MemoryStream())
{
do
{
int numberOfBytesRead = networkStream.Read(readBuffer, 0, readBuffer.Length);
memoryStream.Write(readBuffer, 0, numberOfBytesRead);
if (!networkStream.DataAvailable)
System.Threading.Thread.Sleep(1); //Or 50 for non-believers ;)
}
while (networkStream.DataAvailable);
}
然后NetworkStream有足够的时间来正确设置.DataAvailable,这个方法应该能正常运行。
有趣的事实......这似乎是某种程度上依赖于操作系统版本。因为没有睡眠的第一个功能在Win XP和Win 10上为我工作,但是在Win 7上没有收到整整1000个字节。不要问我为什么,但我对它进行了彻底的测试,并且它很容易重现。< / p>
答案 4 :(得分:0)
使用TcpClient.Available将允许此代码精确地读取每次可用的内容。当要读取的剩余数据量大于或等于TcpClient.ReceiveBufferSize时,TcpClient.Available将自动设置为TcpClient.ReceiveBufferSize。否则,将其设置为剩余数据的大小。 因此,您可以通过设置TcpClient.ReceiveBufferSize(例如oClient.ReceiveBufferSize = 4096;)来指示每次读取可用的最大数据量。
protected void OnClientCommunication(TcpClient oClient)
{
NetworkStream stream = oClient.GetStream();
MemoryStream msIn = new MemoryStream();
byte[] aMessage;
oClient.ReceiveBufferSize = 4096;
int iBytesRead = 0;
while (stream.DataAvailable)
{
int myBufferSize = (oClient.Available < 1) ? 1 : oClient.Available;
aMessage = new byte[oClient.Available];
int iRead = stream.Read(aMessage, 0, aMessage.Length);
iBytesRead += iRead;
msIn.Write(aMessage, 0, iRead);
}
MemoryStream msOut = new MemoryStream();
// .. Do some processing adding data to the msOut stream
msOut.WriteTo(stream);
stream.Flush();
oClient.Close();
}