C#套接字:异步那些内同步调用 [英] C# Sockets: synchronous calls within asynchronous ones
问题描述
在MSDN的例如使用异步插座,插座的接收数据是通过反复调用异步BeginReceive从由BeginReceive调用的回调处理程序完成的:
私有静态无效ReceiveCallback(IAsyncResult的AR){
// ... ...跳过
如果(读取动作大于0){
//可能有更多的数据,所以存储接收到的数据为止。
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,读取动作));
//获取数据的其余部分。
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
新的AsyncCallback(ReceiveCallback),状态);
}其他{
// ... ...跳过
}
http://msdn.microsoft。 COM / EN-US /库/ bbx2eya8(v = vs.110)的.aspx
有没有必要再做出从中已经在一个单独的线程中执行的处理程序的异步调用?我们可以简单地使用在此处理循环接收?是这样的:
而(读取动作){读取动作= client.Receive(state.buffer,0,client.Available,
SocketFlags.None);
//等等...
}
的 APM 是为了避免阻塞调用线程在等待异步操作的结果,从而拉动了服务器应用程序的可扩展性的模式。
如果你的的AsyncCallback
你继续调用接收
在循环同步,你仍然会阻止该IOCP线程在其上初始 BeginReceive
已完成。这可以为客户端UI的应用程序,你可能不关心线程池
饥饿是好的,但是这肯定不是一个服务器端的应用程序,其中一个好主意阻塞的线程本来可以服务于其他客户请求。
请注意,与C#5.0 / .NET 4.5和,APM被视为遗产。您可以使用 异步/的await
和的 新的基于任务的异步模式(TAP)
模式,从而大大简化了异步code开发,例如:
异步任务<串GT; ReadAllAsync()
{
VAR SB =新的StringBuffer(); 使用(VAR TCP =新的TcpClient())
{
等待tcp.ConnectAsync(IPAddress.Parse(localhost的),8080).ConfigureAwait(假);
VAR缓冲=新的字节[1024];
使用(VAR流= tcp.GetStream())
{
变种读取动作=等待stream.ReadAsync(缓冲液,0,buffer.Length);
如果(0 ==读取动作)
打破;
sb.Append(Encoding.ASCII.GetString(state.buffer,0,读取动作));
}
} 返回sb.ToString();
}
如果您不希望使用 NetworkStream.ReadAsync
某些原因,你可以用APM式插座API,作为TAP与任务。 FromAsync
:
公共静态类SocketsExt
{
静态公共任务ReceiveDataAsync(
该TcpClient的TcpClient的,
字节[]缓冲区)
{
返回Task.Factory.FromAsync(
(AsyncCallback的,状态)= GT;
tcpClient.Client.BeginReceive(缓冲液,0,buffer.Length,
SocketFlags.None,的AsyncCallback,州),
(asyncResult)= GT;
tcpClient.Client.EndReceive(asyncResult)
空值);
} 静态公共异步任务<&的Int32 GT; ReceiveInt32Async(
这个的TcpClient的TcpClient)
{
VAR数据=新的字节[的sizeof(的Int32)];
等待tcpClient.ReceiveDataAsync(数据).ConfigureAwait(假);
返回BitConverter.ToInt32(数据,0);
}
}
In MSDN example of using asynchronous sockets, the receiving data in socket is done by repeatedly calling asynchronous BeginReceive from the callback handler which is called by 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
Is there a necessity to make again an asynchronous call from a handler which is already executing in a separate thread? Can one simply use Receive in a loop in this handler? Something like:
while (bytesRead) {
bytesRead = client.Receive(state.buffer, 0, client.Available,
SocketFlags.None);
// Etc...
}
The major goal of the APM pattern was to avoid blocking the calling thread while waiting for the result of asynchronous operation, and thus boost the scalability of the server applications.
If in your AsyncCallback
you continue calling Receive
in the loop synchronously, you'll still be blocking the IOCP thread on which the initial BeginReceive
has completed. This may be OK for a client-side UI app, where you might not care about ThreadPool
starvation, but this is certainly not a good idea for a server-side app, where the blocked thread could otherwise be serving other incoming client requests.
Note that with C# 5.0 / .NET 4.5 and on, APM is considered legacy. You can use async/await
and new Task-based Asynchronous Pattern (TAP)
pattern, which greatly simplifies the asynchronous code development, e.g.:
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();
}
If for some reason you don't want to use NetworkStream.ReadAsync
, you can wrap APM-style socket APIs as TAP with Task.FromAsync
:
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);
}
}
这篇关于C#套接字:异步那些内同步调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!