![]() |
|
Модераторы: feodorv |
![]()
|
|
| Luchi |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 18 Регистрация: 21.8.2012 Репутация: нет Всего: нет |
Доброго времени суток!
Пишу на C++ версия компилятора 2.95.2 Как грамотно проверять код на ошибки? В SMTP клиенте с самого начала инициализации сокета и до конца программы SOCKET_ERROR==-1? Получается на протяжении всей программы в SOCKET_ERROR возвращается ошибка? 1. Или проверять нужно не SOCKET_ERROR, а send или recv? Но компилятор выдает ошибку. 2. Может кто-либо объясните простыми словами на одной части кода send и recv как проверять на ошибки, если можно конкретно на моем коде. 3. В каких переменных какие ошибки? 4. И есть ли разница что ставить в return -1, 0, 1 при обработке ошибки в IF() {return -1, 0, 1}?
|
|||
|
||||
| feodorv |
|
||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 10 Всего: 45 |
SOCKET_ERROR есть константа, в неё возвращаться ничего не может. Как раз на неё проверяются возвращаемые значения функций - произошла ли ошибка или нет. Собственно код ошибки можно узнать из WSAGetLastError(). Поэтому условие всегда ложно. Проверять нужно возвращаемые значения функций send или recv:
Если Ваша программа не выполняется из под скрипта или другой программы, в которых проверяется это значение, то не имеет. Но в духе "хорошего стиля программирования" в случае ошибки лучше возвращать из main() не 0, в случае успеха - 0. Можно даже последовать рекомендациям Microsoft, и возвращать значения специально определённых кодов. Добавлено через 10 минут и 58 секунд Хочу также заметить, что SMTP сервер не обязан возвращать строки, заканчивающиеся символом '\0', поэтому такой код:
может выполниться с непредсказуемым результатом. Лучше написать так:
-------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||||||||
|
|||||||||||
| Luchi |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 18 Регистрация: 21.8.2012 Репутация: нет Всего: нет |
feodorv
Спасибо объяснил доступно, код который получился ниже. 1. ф-и send и recv возвращают много значений, нужно проверять каждое? Как узнать какое значение чему соотвествует? 2. Почему WSAGetLastError() постоянно показывает 0? 3. Если в PASSWORD if ((er = recv(s, text, sizeof(text), 0)) == SOCKET_ERROR ), при вводе неправильного пароля if не срабатывает и программа не завершается, хотя ведь с сервера приходит ошибка? er в этом случае = больше 1, тогда зачем в случае ошибки сравнивать er<0? 4. Помню в справочниках указывалось, что при проверках на ошибки всего 2-3 значения: 1-все правильно, 0-нет данных, -1-ошибка, или без 0? 5. Нужно ли проверять на ошибки заголовки письма?
Это сообщение отредактировал(а) Luchi - 12.9.2012, 20:04 |
|||
|
||||
| feodorv |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 10 Всего: 45 |
Функции send и recv возвращают либо ошибку (ту самую константу SOCKET_ERROR), либо число байт, посланных или прочитанных. Так что возвращаемое значение всего одно.
Не знаю, что за справочники Вы читали. Самое главное упущено - "число байт, посланных или прочитанных" (если не ошибка). При синхронных (а у Вас везде синхронные) операциях recv, да, возвращает 0, если соединение входит в стадию разрыва, ожидать при этом прихода новых данных бессмысленно. send может вернуть 0, если Вы попросили послать 0 байт. В остальном - "число байт, посланных или прочитанных" (если не ошибка). Просто, абстрактно вызывать WSAGetLastError() смысла нет, этот вызов всё равно вернёт код последней возникшей ошибки (или могущей возникнуть в зависимости от использовавшегося вызова WinSock API). Если ошибок не возникало, WSAGetLastError() будет постоянно возвращать 0. То есть контекст вызова WSAGetLastError() такой:
Идёт проверка ошибки вызова recv на Вашей стороне. Мало ли что могло случиться. Сервер мог разорвать соединение, кто-то мог передёрнуть маршрутизатор или вообще выдернуть сетевой кабель. ОС на всё это реагирует, возвращая Вам SOCKET_ERROR в recv() и устанавливая соответствующий код ошибки в WSAGetLastError(). И send совершенно не знает, что Вы отправляете в сеть, пароль ли, признание в любви или ещё что. Он/оно просто отправляет в сеть то, что попросили. Если ли же на стороне сервера пароль не прошёл проверку, то SMTP сервер пришлёт обратно сообщение вида "Password incorrect" или ещё что-то в этом роде. При этом recv отработает штатно, без ошибок, и прочтёт из сети это сообщение. И уже Ваша задача (на прикладном уровне!) обработать это сообщение корректно. recv здесь вообще не причём... -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||||
|
|||||||
| Luchi |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 18 Регистрация: 21.8.2012 Репутация: нет Всего: нет |
@feodorv
Спасибо, понятно. Единственное, когда отправляю логин и пароль, идет простой обмен данными, чтобы корректно на прикладном уровне обработать сообщение, при неправильном логин или пароль (в этой ситуации приходит 67 байт) нужно:
Но ведь ответы разных серверов могут отличаться, поэтому кол-во байт тоже будет различаться. Если не сложно, как универсально обработать ошибку логина или пароля? Во всех найденных материалах касаемых сокетов именно этот момент не расписан. Это сообщение отредактировал(а) Luchi - 13.9.2012, 22:59 |
|||
|
||||
| feodorv |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 10 Всего: 45 |
Нет-нет. Не нужно строго ориентироваться на число присланных байт. При правильном логине/пароле сервер пришлёт подтверждение. Если такое подтверждение не получено, можно считать, что регистрация не прошла. Сам ответ от сервера содержится в text (в конце концов, Вы же читаете из сети именно в text). Вот содержимое этого буфера и нужно анализировать. Например:
Подтверждение регистрации стандартизировано (нужно смотреть спецификацию протокола SMTP, это какой-то RFC). Честно, сам я не помню, какой именно ответ должен быть от сервера в случае успеха, но он всегда одинаков и не зависит от сервера. Отрицательный ответ может варьироваться... Это сообщение отредактировал(а) feodorv - 14.9.2012, 21:47 -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
|||
|
||||
| Luchi |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 18 Регистрация: 21.8.2012 Репутация: нет Всего: нет |
feodorv
Вот это я и искал. Решил эту задачу с помощью ф-и atoi():
Но за ваш вариант спасибо, буду иметь ввиду. |
|||
|
||||
| feodorv |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 10 Всего: 45 |
По сути, мой ответ ничем не отличается от Вашего решения Но! Опять-таки: Вы не завершили данные буфера text символом конца строки '\0'. В результате функция atoi может вернуть всё, что угодно (есть такая вероятность). Посмотрите, пожалуйста, на этот код внимательно. Что не так? -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
|||
|
||||
| Luchi |
|
||||||||
|
Новичок Профиль Группа: Участник Сообщений: 18 Регистрация: 21.8.2012 Репутация: нет Всего: нет |
Согласен, но для того чтобы разобраться и научиться, лучше иметь несколько вариантов решения задачи.
У меня \0 стоял после фразы и я его не включил, но как сейчас понимаю, что он вообще не на своем месте стоял, т.к. его вставка должна произойти до выполнения ф-и.
Думаю, что этот код не универсален, т.к. обрабатываются только два варианта ответа. Переработанный код: с использованием atoi
с использованием strncmp
Это сообщение отредактировал(а) Luchi - 17.9.2012, 19:38 |
||||||||
|
|||||||||
| feodorv |
|
||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 10 Всего: 45 |
Функция atoi реагирует на '\0', поэтому присвоение text[er] = '\0'; нужно делать ДО вызова atoi. Соответственно, при чтении из сокета необходимо зарезервировать один байт под этот самый '\0', поэтому в recv нужно указывать sizeof(text)-1.
Дело не в этом. Конструкция воспринимается компилятором совсем не так, как Вы задумали. Вы думали про что-то такое:
А у Вас получилось так:
Условие всегда истинно, не понятно, как у Вас программа работала...
Сам по себе код text[strlen(text)] = '\0'; совершенно бессмысленнен (и даже опасен) и говорит о том, что Вы не понимаете, что такое си-строка, как работает strlen и прочая... Если вы закрыли строку, прочитанную из сети, завершающим символом '\0', то уже всё равно, что atoi, что strncmp. Если же нет, тогда только strncmp Ээээ. Пожалуйста, поподробнее. Покажите, пожалуйста, что Вы шлёте, и чем отвечает сервер (это всё можно сделать через printf'ы). Добавлено через 4 минуты и 9 секунд И всё-таки, неверный подход к функции recv. Она возвращает число прочитанных байт, поэтому стилистически правильнее было бы использование переменной с именем bytes:
А Вы ожидаете ошибку, которая возникает крайне редко -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||||||||
|
|||||||||||
![]()
|
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | C/C++: Сети | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |