C#套接字:异步那些内同步调用 [英] C# Sockets: synchronous calls within asynchronous ones

查看:167
本文介绍了C#套接字:异步那些内同步调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆