在使用异步套接字的MSDN example中,套接字中的接收数据是通过从BeginReceive调用的回调处理程序重复调用异步BeginReceive来完成的:
private static void ReceiveCallback( IAsyncResult ar ) {
//...Skipped...
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
new AsyncCallback(ReceiveCallback), state);
} else {
// ...Skipped...
}
http://msdn.microsoft.com/en-us/library/bbx2eya8(v=vs.110).aspx
是否有必要再次从已经在单独线程中执行的处理程序进行异步调用?可以在这个处理程序中循环使用Receive吗?类似的东西:
while (bytesRead) {
bytesRead = client.Receive(state.buffer, 0, client.Available,
SocketFlags.None);
// Etc...
}
答案 0 :(得分:3)
APM pattern的主要目标是避免在等待异步操作的结果时阻塞调用线程,从而提高服务器应用程序的可伸缩性。
如果在AsyncCallback
中继续同步循环调用Receive
,您仍然会阻止初始BeginReceive
已完成的IOCP线程。这对于客户端UI应用程序可能没问题,您可能不关心ThreadPool
饥饿,但对于服务器端应用程序来说这当然不是一个好主意,否则被阻止的线程可能会服务于其他传入客户要求。
请注意,在C#5.0 / .NET 4.5及更高版本中,APM被认为是遗留的。您可以使用async/await
和new Task-based Asynchronous Pattern (TAP)
模式,这极大地简化了异步代码开发,例如:
async Task<string> ReadAllAsync()
{
var sb = new StringBuffer();
using (var tcp = new TcpClient())
{
await tcp.ConnectAsync(IPAddress.Parse("localhost"), 8080).ConfigureAwait(false);
var buffer = new byte[1024];
using (var stream = tcp.GetStream())
{
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (0 == bytesRead)
break;
sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
}
}
return sb.ToString();
}
如果由于某种原因您不想使用NetworkStream.ReadAsync
,您可以使用Task.FromAsync
将APM样式的套接字API包装为TAP:
public static class SocketsExt
{
static public Task ReceiveDataAsync(
this TcpClient tcpClient,
byte[] buffer)
{
return Task.Factory.FromAsync(
(asyncCallback, state) =>
tcpClient.Client.BeginReceive(buffer, 0, buffer.Length,
SocketFlags.None, asyncCallback, state),
(asyncResult) =>
tcpClient.Client.EndReceive(asyncResult),
null);
}
static public async Task<Int32> ReceiveInt32Async(
this TcpClient tcpClient)
{
var data = new byte[sizeof(Int32)];
await tcpClient.ReceiveDataAsync(data).ConfigureAwait(false);
return BitConverter.ToInt32(data, 0);
}
}