异步客户端服务设计模式
本文关键字:设计模式 服务 客户端 异步 | 更新日期: 2023-09-27 18:08:24
我试图设计一个邮件客户端服务,它允许使用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);
}
}
这是我迄今为止成功使用的代码的一般格式。
它不阻塞调用线程(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);
}