我正在尝试设计一个邮件客户端服务,它允许使用TcpClient类连接和发送/接收命令。我还希望在回调每个函数时自动调用调用线程,这样调用者就不需要了。
我发现我需要为每个函数编写至少三到四倍的代码来实现它,而不是我要同步编写它。最大的问题是我必须为每个回调编写一个单独的try / catch。
我会发布我的Connect功能,希望有人能提出更好的建议:
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);
}
}
答案 0 :(得分:0)
这是我迄今为止成功使用的代码的一般格式。
它不会阻塞调用线程(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);
}