Правильно отменяющий вызов Socket.XXXAsyn c - PullRequest
0 голосов
/ 25 апреля 2020

Я работаю над основанной на задачах оболочкой для 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 код как можно лучше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...