C# 异步客户端服务设计模式

C# 异步客户端服务设计模式,c#,service,.net-4.0,tcpclient,C#,Service,.net 4.0,Tcpclient,我正在尝试设计一个邮件客户端服务,它允许使用TcpClient类连接和发送/接收命令。我还想在回调每个函数时自动调用调用线程,这样调用者就不需要这样做了 我发现,为了实现这一点,我需要为每个函数编写至少三到四倍于同步编写的代码。最大的问题是,我必须为每个回调编写一个单独的try/catch 我将发布我的连接功能,希望有人能提出更好的方法: public virtual void Connect(Action<Exception> callback, string hostname,

我正在尝试设计一个邮件客户端服务,它允许使用TcpClient类连接和发送/接收命令。我还想在回调每个函数时自动调用调用线程,这样调用者就不需要这样做了

我发现,为了实现这一点,我需要为每个函数编写至少三到四倍于同步编写的代码。最大的问题是,我必须为每个回调编写一个单独的try/catch

我将发布我的连接功能,希望有人能提出更好的方法:

public virtual void Connect(Action<Exception> callback, string hostname, int port, bool ssl, RemoteCertificateValidationCallback validateCertificate)
{
    if (State != ConnectionState.Disconnected)
        throw new InvalidOperationException(AlreadyConnectedString);

    Host = hostname;
    Port = port;
    Ssl = ssl;

    var context = SynchronizationContext.Current;

    // Callback on the caller's thread
    Action<Exception> onCallback = (Exception ex) =>
        {
            context.Post(_ =>
                {
                    callback(ex);
                }, null);
        };

    // Called on any raised exceptions
    Action<Exception> onFail = (Exception ex) =>
        {
            State = ConnectionState.Disconnected;
            Cleanup();
            onCallback(ex);
        };

    // Check for a valid response
    Action<string, Exception> onConnectResponse = (string response, Exception ex) =>
        {
            if (ex != null)
                onFail(ex);

            try
            {
                OnConnected(response);
                onCallback(ex);
            }
            catch (Exception responseException)
            {
                onFail(responseException);
            }
        };

    // Callback after SSL authentication
    AsyncCallback onAuthenticated = (IAsyncResult result) =>
        {
            try
            {
                var sslStream = (SslStream)result.AsyncState;
                sslStream.EndAuthenticateAsClient(result);

                State = ConnectionState.Authorization;

                GetResponse(onConnectResponse);
            }
            catch (Exception authenticateException)
            {
                onFail(authenticateException);
            }
        };

    // Callback after TcpClient connect
    AsyncCallback onConnect = (IAsyncResult result) =>
        {
            try
            {
                _Connection.EndConnect(result);

                _Stream = _Connection.GetStream();

                if (ssl)
                {
                    SslStream sslStream;

                    if (validateCertificate != null)
                        sslStream = new SslStream(_Stream, false, validateCertificate);
                    else
                        sslStream = new SslStream(_Stream, false);

                    _Stream = sslStream;

                    sslStream.BeginAuthenticateAsClient(hostname, onAuthenticated, sslStream);
                }
                else
                {
                    State = ConnectionState.Authorization;

                    GetResponse(onConnectResponse);
                }
            }
            catch (Exception connectException)
            {
                onFail(connectException);
            }
        };

    try
    {
        _Connection = new TcpClient();
        _Connection.BeginConnect(hostname, port, onConnect, null);
    }
    catch (Exception ex)
    {
        onFail(ex);
    }
}
公共虚拟void Connect(操作回调、字符串主机名、int端口、bool ssl、RemoteCertificateValidationCallback validateCertificate) { 如果(状态!=连接状态已断开) 抛出新的InvalidOperationException(AlreadyConnectedString); 主机=主机名; 端口=端口; Ssl=Ssl; var context=SynchronizationContext.Current; //调用方线程上的回调 操作onCallback=(异常示例)=> { context.Post(=> { 回调(ex); },空); }; //呼吁任何提出的例外情况 操作失败=(异常示例)=> { 状态=连接状态。已断开连接; 清理(); onCallback(ex); }; //检查是否有有效的响应 操作onConnectResponse=(字符串响应,异常示例)=> { 如果(ex!=null) onFail(ex); 尝试 { 未连接(响应); onCallback(ex); } 捕获(异常响应异常) { onFail(responseException); } }; //SSL身份验证后的回调 AsyncCallback onAuthenticated=(IAsyncResult结果)=> { 尝试 { var sslStream=(sslStream)result.AsyncState; sslStream.endClient(结果); 状态=连接状态。授权; GetResponse(OnConnectionResponse); } catch(异常authenticateException) { onFail(authenticateException); } }; //TcpClient连接后的回调 AsyncCallback onConnect=(IAsyncResult结果)=> { 尝试 { _连接。结束连接(结果); _Stream=_Connection.GetStream(); 如果(ssl) { SSL流SSL流; if(validateCertificate!=null) sslStream=新的sslStream(_Stream,false,validateCertificate); 其他的 sslStream=新的sslStream(_-Stream,false); _流=SSL流; beginauthenticatesclient(主机名,onAuthenticated,sslStream); } 其他的 { 状态=连接状态。授权; GetResponse(OnConnectionResponse); } } 捕获(异常连接异常) { onFail(连接异常); } }; 尝试 { _连接=新的TcpClient(); _Connection.BeginConnect(主机名、端口、onConnect、null); } 捕获(例外情况除外) { onFail(ex); } }
以下是我迄今为止成功使用的代码的通用格式

它不会阻止调用线程(UI),并且总是在调用线程上调用其回调,但它在后台线程中完成每个任务的大部分工作

如果线程需要独占访问共享资源(如开放网络/SSL流),则可以使用锁定器封送此资源的使用,以便一次只有一个线程使用它

public void Connect(Action<Exception> callback, string hostname, int port, bool ssl, RemoteCertificateValidationCallback validateCertificate)
{
    if (State != ConnectionState.Disconnected)
        throw new InvalidOperationException(AlreadyConnectedString);

    Host = hostname;
    Port = port;
    Ssl = ssl;
    State = ConnectionState.Connecting;

    var callingThread = TaskScheduler.FromCurrentSynchronizationContext();

    Action connectAction = () =>
    {
        // Connect asynchronously in order to specify a timeout
        TcpClient connection = new TcpClient();
        connection.SendTimeout = SendTimeout;
        connection.ReceiveTimeout = ReadTimeout;

        IAsyncResult ar = connection.BeginConnect(hostname, port, null, null);
        WaitHandle waitHandle = ar.AsyncWaitHandle;

        try
        {
            if (!ar.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(ConnectTimeout), false))
                throw new TimeoutException();

            connection.EndConnect(ar);
        }
        finally
        {
            waitHandle.Close();
        }

        Stream stream = connection.GetStream();

        if (ssl)
        {
            SslStream sslStream;

            if (validateCertificate != null)
                sslStream = new SslStream(stream, false, validateCertificate);
            else
                sslStream = new SslStream(stream, false);

            sslStream.AuthenticateAsClient(hostname);

            stream = sslStream;
        }

        lock (_locker) // Perform thread unsafe operations here
        {
            _connection = connection;
            _stream = stream;
        }

        OnConnected(GetResponse());
    };

    Action<Task> completeAction = (Task task) =>
    {
        Exception ex = (task.Exception != null) ? task.Exception.InnerException : task.Exception;

        if (task.Exception != null)
        {
            Cleanup();
        }
        else
        {
            State = ConnectionState.Authorization;
        }

        if (callback != null)
            callback(ex);
    };

    Task.Factory.StartNew(connectAction, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
                .ContinueWith(completeAction, callingThread);
}
public void Connect(操作回调、字符串主机名、int端口、bool ssl、RemoteCertificateValidationCallback validateCertificate)
{
如果(状态!=连接状态已断开)
抛出新的InvalidOperationException(AlreadyConnectedString);
主机=主机名;
端口=端口;
Ssl=Ssl;
状态=连接状态。正在连接;
var callingThread=TaskScheduler.FromCurrentSynchronizationContext();
动作连接动作=()=>
{
//异步连接以指定超时
TcpClient连接=新的TcpClient();
connection.SendTimeout=SendTimeout;
connection.ReceiveTimeout=ReadTimeout;
IAsyncResult ar=connection.BeginConnect(主机名、端口、null、null);
WaitHandle WaitHandle=ar.AsyncWaitHandle;
尝试
{
if(!ar.AsyncWaitHandle.WaitOne(TimeSpan.FromMillicles(ConnectTimeout),false))
抛出新的TimeoutException();
连接。端连接(ar);
}
最后
{
waitHandle.Close();
}
Stream=connection.GetStream();
如果(ssl)
{
SSL流SSL流;
if(validateCertificate!=null)
sslStream=新sslStream(流、假、validateCertificate);
其他的
SSL流=新SSL流(流,假);
sslStream.authenticatesClient(主机名);
流=SSL流;
}
lock(\u locker)//在此处执行线程不安全的操作
{
_连接=连接;
_溪流=溪流;
}
OnConnected(GetResponse());
};
操作完成操作=(任务)=>
{
异常ex=(task.Exception!=null)?task.Exception.InnerException:task.Exception;
if(task.Exception!=null)
{
清理();
}
其他的
{
状态=连接状态。授权;
}
如果(回拨=