根据我对您的问题的理解:您打算像阻塞流一样使用StreamReader,但您使用的是异步套接字......即使我的理解是错误的,请仍然查看以下建议,因为它们还涵盖了您真正打算使用 StreamReader 进行异步读取的情况。
至少能得到完整的一行数据,并且
是和否:只要底层缓冲区有一个新行,它就会读取一整行数据,但是,这是一个异步事件,所以一旦 StreamReader 用完数据(即它到达最后一个新行)它即使您收到另一个异步回调,也将不再有任何可阅读的内容。基本上,您将接收缓冲区绑定到SocketAsyncEventArgs,当您收到回调时,您的缓冲区已被填满。之后你对缓冲区的处理取决于你(你可以将其输入StreamReader 以读取行输入)。
它不会以某种方式阻塞。
它不会阻塞,但它不会获取您正在寻找的所有数据,具体取决于您实现异步回调的方式。这让我想到了下一点……
我的下一点:我不知道这是一个连续的数据流,还是只是一些行分隔的“批次”数据,但是,您有几个选择:
- 使用 1 个内存流和一个与该内存流关联的读/写流。每当您收到回调时,您使用
StreamWriter 写入缓冲区并使用 StreamReader 读取行,直到您无法再读取任何新行。
- 继续将缓冲区写入流,直到您不再从套接字接收到字节(即收到的字节为 0),然后使用该流初始化您的
StreamReader,然后读取所有行。这假设您有固定大小的传入数据,而不是连续的“输入”流。
- 创建一个阻塞读取器并让它在单独的线程上运行,它仅在异步读取引发某种标志时读取(想想
ManualResetEvent 或类似的东西)。它有点违背了异步套接字的目的,但它仍然可以让您利用这些好处。它比纯阻塞套接字好一点,因为阻塞完全由你控制。
我会假设您的最佳选择是 #1,但其他 2 个可能不是不可能的,具体取决于您要做什么。
附带说明:我有 created an async HTTP client,所以请随意查看并清除您认为有用的任何内容。我认为这两个功能对你来说是最重要的:
开始接收:
private void BeginReceive()
{
if ( _clientState == EClientState.Receiving)
{
if (_asyncTask.BytesReceived != 0 && _asyncTask.TotalBytesReceived <= _maxPageSize)
{
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.SetBuffer(_asyncTask.ReceiveBuffer, 0, _asyncTask.ReceiveBuffer.Length);
e.Completed += new EventHandler<SocketAsyncEventArgs>(ReceiveCallback);
e.UserToken = _asyncTask.Host;
bool comletedAsync = false;
try
{
comletedAsync = _socket.ReceiveAsync(e);
}
catch (SocketException se)
{
Console.WriteLine("Error receiving data from: " + _asyncTask.Host);
Console.WriteLine("SocketException: {0} Error Code: {1}", se.Message, se.NativeErrorCode);
ChangeState(EClientState.Failed);
}
if (!comletedAsync)
{
// The call completed synchronously so invoke the callback ourselves
ReceiveCallback(this, e);
}
}
else
{
//Console.WriteLine("Num bytes received: " + _asyncTask.TotalBytesReceived);
ChangeState(EClientState.ReceiveDone);
}
}
}
还有 ReceiveCallback:
private void ReceiveCallback(object sender, SocketAsyncEventArgs args)
{
lock (_sync) // re-entrant lock
{
// Fast fail: should not be receiving data if the client
// is not in a receiving state.
if (_clientState == EClientState.Receiving)
{
String host = (String)args.UserToken;
if (_asyncTask.Host == host && args.SocketError == SocketError.Success)
{
try
{
Encoding encoding = Encoding.ASCII;
_asyncTask.BytesReceived = args.BytesTransferred;
_asyncTask.TotalBytesReceived += _asyncTask.BytesReceived;
// ********************************************
// You will need to replace the following line:
_asyncTask.DocSource += encoding.GetString(_asyncTask.ReceiveBuffer, 0, _asyncTask.BytesReceived);
// Write the contents of the ReceiveBuffer to a Stream using a StreamWriter
// Use the StreamReader associated with the same Stream to read the next line(s)
// ********************************************
BeginReceive();
}
catch (SocketException e)
{
Console.WriteLine("Error receiving data from: " + host);
Console.WriteLine("SocketException: {0} Error Code: {1}", e.Message, e.NativeErrorCode);
ChangeState(EClientState.Failed);
}
}
else if (_asyncTask.Host != host)
{
Console.WriteLine("Warning: received a callback for {0}, but the client is currently working on {1}.",
host, _asyncTask.Host);
}
else
{
Console.WriteLine("Socket Error: {0} when receiving from {1}",
args.SocketError,
_asyncTask.Host);
ChangeState(EClientState.Failed);
}
}
}
}