Не думаю, что вы включили достаточно кода, чтобы получить полную картину, но эта строка выглядит подозрительно:
switch( QCS * (BytesTransfered > 0) * Client->OpCode() ){
Это проблематично по нескольким причинам.Во-первых, GetQueuedCompletionStatus
не гарантирует возврата 1 в случае успеха.MSDN обещает только то, что он вернет ненулевое значение.Поэтому полагаться на конкретное значение для примера успеха рискованно.Во-вторых, у вас нет возможности различить неудавшийся вызов и успешный вызов, который возвращает 0 байтов.Вы действительно должны отделить свою логику для управления очередью и для отправки определенных событий ввода / вывода.Это облегчит понимание и поддержку вашего кода.
Вы также должны помнить, что у каждого сокета есть две стороны.Существуют структуры и дескриптор сокета, которые вы связываете с ним в пространстве пользователя, а затем есть объекты ядра, которые управляют деталями низкого уровня.То, что вы закрываете свой дескриптор на стороне пользователя, не означает, что объекты ядра исчезают.На объекты ядра ссылаются, они подсчитываются и, как правило, задерживаются до тех пор, пока не завершится весь ввод-вывод, связанный с этими объектами.
Вот почему вы все еще можете получать уведомления ввода-вывода для сокета после того, как он был «уничтожен» с точки зрения вашей программы.В частности, для сокетов последовательность выключения будет иметь место после того, как вы закроете свой дескриптор (потому что ранее вы не отключили сокет явно).
Вместо уничтожения объекта Clientв ответ на конкретное сообщение просто закройте дескриптор сокета и очистите другие структуры в ответ на уведомление об отмене.Вы также можете подумать о том, чтобы сделать изящное отключение, а не прерывать соединение.