Я работаю над основанной на задачах оболочкой для SocketAsyncEventArgs для личного проекта. Пока у меня все работает, кроме отмены задач. При его реализации я столкнулся с проблемой в виде непонимания того, как правильно осуществить отмену.
Скажите, что у меня есть Task-returning ReceiveAsyn c и ReceiveFromAsyn c методы, которые принимают CancellationToken со следующими сигнатурами:
public Task<int> ReceiveAsync(SocketAsyncEventArgs e, CancellationToken cancellationToken) {
cancellationToken.Register(state => {
// do something to cancel the operation when the cancellationToken is signalled
}, someState);
bool completedAsync = socket.ReceiveAsync(e);
// ...
}
public Task<int> ReceiveFromAsync(SocketAsyncEventArgs e, CancellationToken cancellationToken) {
cancellationToken.Register(state => {
// do something to cancel the operation when the cancellationToken is signalled
}, someState);
bool completedAsync = socket.ReceiveFromAsync(e);
// ...
}
Кроме того, скажем, что у меня есть методы SendAsyn c и SendToAsyn c, также с CancellationToken со следующими сигнатурами:
public Task<int> SendAsync(SocketAsyncEventArgs e, CancellationToken cancellationToken) {
cancellationToken.Register(state => {
// do something to cancel the operation when the cancellationToken is signalled
}, someState);
bool completedAsync = socket.SendAsync(e);
// ...
}
public Task<int> SendToAsync(SocketAsyncEventArgs e, CancellationToken cancellationToken) {
cancellationToken.Register(state => {
// do something to cancel the operation when the cancellationToken is signalled
}, someState);
bool completedAsync = socket.SendToAsync(e);
// ...
}
Обработчик событий SocketAsyncEventArgs.Completed выглядит так. Предположим, что до сих пор правильная реализация обработчиков SocketAsyncEventArgs. Единственное, чего не хватает, это отмены:
public void HandleIoCompleted(object sender, SocketAsyncEventArgs args) {
switch (args.LastOperation) {
case SocketAsyncOperation.Receive:
switch (args.SocketError) {
case SocketError.Success:
// ...
break;
case SocketError.OperationAborted:
Debug.WriteLine("CompleteReceive experienced SocketError.OperationAborted!");
break;
default:
// ...
break;
}
break;
case SocketAsyncOperation.ReceiveFrom:
switch (args.SocketError) {
case SocketError.Success:
// ...
break;
case SocketError.OperationAborted:
Debug.WriteLine("CompleteReceiveFrom experienced SocketError.OperationAborted!");
break;
default:
// ...
break;
}
break;
case SocketAsyncOperation.Send:
switch (args.SocketError) {
case SocketError.Success:
// ...
break;
case SocketError.OperationAborted:
Debug.WriteLine("CompleteSend experienced SocketError.OperationAborted!");
break;
default:
// ...
break;
}
break;
case SocketAsyncOperation.SendTo:
switch (args.SocketError) {
case SocketError.Success:
// ...
break;
case SocketError.OperationAborted:
Debug.WriteLine("CompleteSendTo experienced SocketError.OperationAborted!");
break;
default:
// ...
break;
}
break;
default:
throw new NotSupportedException($"{nameof(HandleIoCompleted)} doesn't support {args.LastOperation}");
}
}
Из других вопросов по этой теме c я прочитал, что для отмены операции Socket.ReceiveAsyn c или Socket.ReceiveFromAsn c я необходимо утилизировать нижележащий сокет. Это, однако, помешало бы любым асинхронным операциям отправки, которые были в полете, которые заканчивались бы SocketError.OperationAborted, когда сокет был удален. Аналогично, отмена любых операций Socket.SendAsyn c или Socket.SendToAsyn c может помешать текущим операциям приема в полете, которые также будут испытывать SocketError.OperationAborted при удалении сокета.
Что такое правильный способ безопасного отмены операции, чтобы трафик UDP и TCP c мог возобновиться после потери пакета (где без отмены вызовы receiveFromAsyn c и receiveAsyn c никогда не вызовут e.Completed) ?
Если требуется дополнительный код, просто добавьте комментарий с указанием как можно больше, и я выложу sh код как можно лучше.