编写客户端/服务器程序,以前使用过这样的调用:
TcpListener.AcceptTcpClient()
NetworkStream.Read()
NetworkStream.Write()
TcpListener.AcceptTcpClient()
工作正常,因为只有在建立连接后才会调用它的回调函数。
但是,BeginRead()
效果不佳。由于某种原因,即使没有数据发送,它也会执行并继续通过它。不确定发生了什么。 NetworkStream.Read()
阻止了,但事实并非如此......
以下是建立连接时创建的非工作线程:
private void HandleClientCommunication(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
try
{
byte[] header = new byte[4];
clientStream.BeginRead(header, 0, header.Length, Read, new StateData(clientStream, header)); // Runs even if no data is sent
int dataLength = BitConverter.ToInt32(header, 0); // Continues to this, even if no packets are sent..
byte[] data = new byte[dataLength]; // dataLength is 0 still of course because none of the code is blocking
clientStream.BeginRead(data, 0, dataLength, Read, new StateData(clientStream, data));
}
catch (IOException e)
{
Console.WriteLine(e.InnerException.Message);
}
// not relevant past here
}
这也是Read方法。不确定是否需要找出问题,但无论如何我都会把它包括在内:
private void Read(IAsyncResult async)
{
StateData stateData = (StateData)async.AsyncState;
stateData.BytesRead += stateData.Stream.EndRead(async);
if (stateData.BytesRead < stateData.Bytes.Length)
{
stateData.Stream.BeginRead(stateData.Bytes, 0, stateData.Bytes.Length, Read, stateData);
}
else
{
string message = Encoding.ASCII.GetString(stateData.Bytes);
Console.WriteLine(message);
}
}
所以是的,我只是尝试与异步调用建立客户端服务器交互,并让每个“数据包”包含一个数据长度为int的头,然后是数据本身。不幸的是,事情似乎不起作用,我不确定问题是什么。
似乎我必须包含一些手动形式的阻止,但这不会破坏使用异步调用的目的吗?那么为什么不回到那时的常规同步阻止调用,如Read()
和AcceptTcpClient()
呢?
我认为我不完全理解这里的事情。
答案 0 :(得分:3)
但是,BeginRead()也不能正常工作。由于某种原因,即使没有数据发送,它也会执行并继续通过它。不确定发生了什么。 NetworkStream.Read()阻塞就好了,但这不是..
我认为你误解了BeginRead
的意图。阻止它不是意味着 - 它发出异步读取,在读取完成时执行给定的回调。 意味着立即返回 - 所以难怪你的标题数据是空的。
基本上,任何需要数据的工作都应该在回调中 - 而不是在调用BeginRead
的方法中。您的第一个回调应该转换标头数据,然后使用不同的回调再次BeginRead
来读取消息数据本身...尽管您还要记住任何Read
/ BeginRead
来电可能无法读取您要求的所有数据。你在回调中尝试来解释这个问题,但你并不在那里 - 因为你总是试图从一开始就填充缓冲区 - 这将是覆盖任何现有数据。如果您知道整体上有多少数据并且缓冲区很长,那么您应该从开始读取缓冲区已经读取了多少字节。
请注意,C#5将使所有这一切变得更加简单 - 但是如果你不能等到那时,你可能会发现更容易回到多个线程上的阻塞调用(每个客户端一个)。