1. Убрать все "lock" - никакой пользы от них здесь нет.
2. Убрать все "try {...} catch (Exception e) { Debug.Print(e.Message); }
Никакой пользы от них тоже нет.
3. Убрать IsCorrect. Читаем длину сообщения, затем читаем нужное количество байт. А то, что вы называете "Фактическая длина" - плод вашей фантазии.
4. Читать надо ни до тех пор, пока DataAvailable, а до тех пор, пока не будет получено нужное количество байт. Синхронно, либо асинхронно вы это будете делать - без разницы.
5. А теперь собственно ответ на ваш вопрос:
Механизм чтения сообщений умирает, когда читатель читает быстрее, чем писатель пишет (либо сеть передает). В этом случае DataAvailable вернет false; ваш алгоритм, вместо того, чтобы дождаться оставшейся части сообщения, прерывает чтение на середине сообщения, говорит, что IsCorrect() == false, и зависает.
6. Формат сообщения имеет смысл упростить: первые 4 байта - длина тела в байтах; затем само тело (например, в UTF-8, если вам нужен текст). Соответственно, читаем сначала 4 байта, а потом еще столько, сколько там указано.
P.S.
7. Вызывать Encoding.GetString() следует только на целом сообщении; если вы делаете это на части сообщения - результат непредсказуем.
8. Сетевые исключения обрабатывать, конечно же, надо. Однако Debug.Print - это не решение проблемы. Сокет имеет смысл закрыть. А дальше - либо уведомить об этой проблеме своего клиента (кто обрабатывает полученные сообщения), либо, по-хорошему, - попытаться открыть другое соединение и повторить попытку продолжить работу так, чтобы клиент ничего не заметил :)