URL: https://www.opennet.ru/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 7577
[ Назад ]

Исходное сообщение
"Ошибки в отладке простенького ftp клиента."

Отправлено my_way , 01-Сен-08 17:02 
Добрый день. Относительно недавно начал программировать под unix, опыта пока малова-то, поэтому, требуется помощь. Пишу простенький ftp клиент и пока что запнулся на следующем. Когда программа просто прогоняется всё выполняется корректно. Под отладчиком (работаю в SUSE 10.3 KDevelop) он после строчки fdopen и fclose иногда писал Cannot access memory 0x0
а иногда 0xfffffff причём появляются ошибки нерегулярно=(( Под suse 11 которая у меня дома такого вообще не происходит и никакие ошибки не вылазят.

Если что-то где-то можно написать красивее и грамотнее было бы здорово услышать предложения=)

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <cctype>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>

#include <unistd.h>
#include <iostream>
using namespace std;

#define MAX_SLEEP_CONNECT 128

bool             connect_retry(int &sock, const struct sockaddr *addr, socklen_t len);
struct addrinfo*     GetFtpAddr(const char *host);
int             ReturnSimpleRequest(int &sock, string* text);


bool connect_retry(int &sock, const struct sockaddr *addr, socklen_t len)
{
    if ((sock = socket(addr->sa_family, SOCK_STREAM, 0)) < 0)
        return false;

    int nsec = 0;
    /*
     *    Пытаемся установить соединение с экспоненциальной задержкой
     */
    for (nsec = 1; nsec <= MAX_SLEEP_CONNECT; nsec <<=1)
    {
        if (connect(sock, addr, len) == 0)
        {
            return true;
        }
        if (nsec <= MAX_SLEEP_CONNECT/2)
            sleep(nsec);
    }
    return false;
}

struct addrinfo* GetFtpAddr(const char *host)
{
    /*
      Функция возвращает адрес хоста по имени.(Адрес структуру addrinfo)
      Причём первый из его адресов.
    */
    int err = 0;
    
    struct addrinfo    address, hint, *spisok, *next;
    struct addrinfo *sinp = NULL;    

    address.ai_flags = AI_CANONNAME;
    address.ai_family = 0;
    address.ai_socktype = 0;
    address.ai_protocol = 0;
    address.ai_addrlen = 0;
    address.ai_canonname = NULL;
    address.ai_addr = NULL;
    address.ai_next = NULL;


    if ((err = getaddrinfo("ftp.freebsd.org", "ftp", &address, &spisok)) !=0 )
        printf("Err getaddrinfo: %s\n\n", gai_strerror(err));
    
    // Мы возвращаем первый же адресс который встречаем.
    
    for (next = spisok; next != NULL; next=next->ai_next)
    {
        if (next->ai_family == AF_INET)
        {
            sinp = next;
            break;
        }
    }
    
    /*
      По идее здесь должнен освобождать память выделенную под адреса
      ,тоесть freeaddrinfo(spisok); но почему-то я не знаю как это и где удобнее сделать.    
    */

    return sinp;
}

int main(int argc, char *argv[])
{
    int socket_ftp;
    
    int err = 0;
    
    const char *ad;
    char abuf[INET_ADDRSTRLEN];

    struct addrinfo *address = GetFtpAddr(argv[1]);
    
    if (address!=NULL)
    {
        struct sockaddr_in *sinp = (struct sockaddr_in *)address->ai_addr;
        ad = inet_ntop(AF_INET, &sinp->sin_addr, abuf, INET_ADDRSTRLEN);
        printf("Адрес %s  ", ad?ad:"не известен");
        printf("Порт %d", ntohs(sinp->sin_port));
        printf("\n");
    }
    if (!connect_retry(socket_ftp, address->ai_addr, address->ai_addrlen))
    {
        printf("\nНе успешно");
        exit(1);
    }

    string str = "error";
    int n = ReturnSimpleRequest(socket_ftp, &str);
    printf("Code: %d\n", n);
    
    close(socket_ftp);
    exit(0);
}
int ReturnSimpleRequest(int &sock, string *text)
{
  #define BUFLEN 100
  if (sock >0)
  {
    const char c = 'r';
    char buf[BUFLEN];
    char ccode[3];
    int code;
    
    FILE* socket_fd = NULL;

    if ((socket_fd = fdopen(sock, &c)) != NULL);
    {
        fgets(buf, 100, socket_fd);
        if (text!=NULL)        
        {
            text->clear();
            text->append(buf);
            printf("%s",text->c_str());
        }
        else
            printf("%s", buf);

        fclose(socket_fd);
        if (isdigit(buf[0]) && isdigit(buf[1]) && isdigit(buf[2]))
            return atoi(strncpy (ccode,buf,5));
        
    }
  }
  return -1;
}


Содержание

Сообщения в этом обсуждении
"Ошибки в отладке простенького ftp клиента."
Отправлено NuINu , 02-Сен-08 09:47 
>[оверквотинг удален]
>
> string str = "error";
> int n = ReturnSimpleRequest(socket_ftp, &str);
> printf("Code: %d\n", n);
>
> close(socket_ftp);
> exit(0);
>}
>int ReturnSimpleRequest(int &sock, string *text)
>{

Не скажу где у вас неправильная работа с памятью, а где то она явно есть, но вот это надо СРОЧНО переделать!!!
замените
bool    connect_retry(int &sock, const struct sockaddr *addr, socklen_t len);
на
bool    connect_retry(int* sock, const struct sockaddr *addr, socklen_t len);

и измените соответсвующие вызовы.
и так далее по тексту.


"Ошибки в отладке простенького ftp клиента."
Отправлено my_way , 02-Сен-08 21:54 
Спасибо, правда не совсем понимаю криминал использования ссылки, а не указателя. Вы не могли бы пояснить?

Заметил странное..
1)Я соединяюсь с сервером. Тоесть дескриптор сокета вначале получаю от socket а потом connect.
2)Я открываю поток для чтения из сокета в функции через функции fdopen и там же закрываю его через fclose

Проблема в том что если я открываю поток вначале на чтение например в одной функции и после отработки закрываю, а потом пытаюсь открыть его на запись в другой функции то указатель на потом имеет значение NULL. А вот если я открываю сразу два потока на сокет один на чтение а другой на запись то всё работает нормально.

Почему я не могу открыть поток на сокет, потом закрыть, а потом открыть снова уже другой поток?



"Ошибки в отладке простенького ftp клиента."
Отправлено NuINu , 03-Сен-08 09:38 
>Спасибо, правда не совсем понимаю криминал использования ссылки, а не указателя. Вы
>не могли бы пояснить?

криминал? ммм... почитай Алена Голуба, там очень хорошо все расписано. самый главный недостаток, это сюрприз который сразу невидно. для читающего(или сопровождающего) твой код программиста это будет головной болью.
область применения ссылок это конструкторы классов, переопределения операторов, во всех остальных случаях лучше использовать указатель.

>[оверквотинг удален]
>
>Проблема в том что если я открываю поток вначале на чтение например
>в одной функции и после отработки закрываю, а потом пытаюсь открыть
>его на запись в другой функции то указатель на потом имеет
>значение NULL. А вот если я открываю сразу два потока на
>сокет один на чтение а другой на запись то всё работает
>нормально.
>
>Почему я не могу открыть поток на сокет, потом закрыть, а потом
>открыть снова уже другой поток?

"секрет" в функции fclose: will flush the stream pointed to by fp (writing any buffered output data using fflush(3)) and close the underlying file descriptor

если хочешь так делать, сделай от открытого дескриптора dup, и уже к нему(новому дескриптору) применяй fdopen.


"Ошибки в отладке простенького ftp клиента."
Отправлено my_way , 03-Сен-08 10:32 
Большое вам спасибо! =)

"Ошибки в отладке простенького ftp клиента."
Отправлено vic , 03-Сен-08 14:18 
0. Вы на С или С++ пишите? Либо, либо, иначе получается что-то страшное.

>bool    connect_retry(int &sock, const struct sockaddr *addr, socklen_t len);

1. Ссылке тут не место, т.к. пока не увидишь прототип думаешь что первый параметр не меняется.

>[оверквотинг удален]
> {
>  if (connect(sock, addr, len) == 0)
>  {
>   return true;
>  }
>  if (nsec <= MAX_SLEEP_CONNECT/2)
>   sleep(nsec);
> }
> return false;
>}

2. вот как узнать почему не соединились? информация об ошибке игнорируется, а должна выводится как минимум в лог файл.
3. зачем этот хитрый цикл? Функция connect() реализует начало TCP-соединения, читайте матчасть, попытка соединится несколько раз с увеличением таймаута там уже реализована. Нужнее в данном случае механизм прерывания долго зависающего коннекта к неотвечающему хосту =)
4. для комментов в тексте функции лучше // и не размазывать на три строки, но этохоливар уже =)

>[оверквотинг удален]
>
> /*
>   По идее здесь должнен освобождать память выделенную под адреса
>
>   ,тоесть freeaddrinfo(spisok); но почему-то я не знаю как это
>и где удобнее сделать.
> */
>
> return sinp;
>}

5. Коммент описывающий функцию располагают перед функцией по правилам и с тегами позволяющими натравить на код doxygen и получить автоматическую документацию.
6. короче так:
struct addrinfo address = { 0 };
address.ai_flags = AI_CANONNAME;
7. не должен освобождать, а освободить. делаем копию sinp хоть посредством malloc()+memcpy() и удаляем spisok.

>[оверквотинг удален]
>  if (sock >0)
>  {
> const char c = 'r';
> char buf[BUFLEN];
> char ccode[3];
> int code;
>
> FILE* socket_fd = NULL;
>
> if ((socket_fd = fdopen(sock, &c)) != NULL); // <-------- СЮРПРИЗ =))))

8. точка с запятой после в конце строки с if это явно ошибка =)
9. fdopen/fopen ожидает в параметре mode нечто типа "r" или "w" т.е. строку в терминах С, я вот хз что будет если строка будет нетерминированной как у вас (неопределенное поведение).

>[оверквотинг удален]
>   printf("%s", buf);
>
>  fclose(socket_fd);
>  if (isdigit(buf[0]) && isdigit(buf[1]) && isdigit(buf[2]))
>   return atoi(strncpy (ccode,buf,5));
>
> }
>  }
>  return -1;
>}

и т.д.

еще следует почитать про C++ и C, осознать разницу подходов, почитать инет на тему серверов и поисследовать уже существующие исходники.


"Ошибки в отладке простенького ftp клиента."
Отправлено timur , 03-Сен-08 16:28 
Спасибо за подробный разбор, поправил=) Проштудировать нормально Страуструпа всё никак не доходят руки.. вот и получаю на выходе поверхностные знания=(
А вы не могли бы посоветовать что-нибудь по сетевым приложениям?  Одна из книг которую собираюсь купить это UNIX: разработка сетевых приложений Стивенсона, но похоже там и так есть многое из того что есть в Стивенс UNIX. Профессиональное программирование, которая мне сейчас помогает=)

"Ошибки в отладке простенького ftp клиента."
Отправлено my_way , 03-Сен-08 16:30 
написал случайно под ником своего друга



"Ошибки в отладке простенького ftp клиента."
Отправлено vic , 04-Сен-08 13:41 
>А вы не могли бы посоветовать что-нибудь по сетевым приложениям?  Одна
>из книг которую собираюсь купить это UNIX: разработка сетевых приложений Стивенсона

ее и посоветую :)


"Ошибки в отладке простенького ftp клиента."
Отправлено my_way , 09-Сен-08 22:53 
В процессе возник следующий вопрос.
При взаимодействии с ftp сервером окончательный ответ сервера можно считать только тогда когда получаем 3 цифры и пробел в строке. Проблема в том что если по каким-то причинам нам не передали всю информацию которую мы рассчитывали считать функциями recv или read то процесс надолго зависает. Как можно защититься от этого? И какие ещё ситуации необходимо учитывать? Небольшие примеры кода очень бы помогли..

Кстати.. при обрыве связи вызывается сигнал SIGPIPE который как я понимаю следует игнорировать и обрабатывать в программе как мне эт необходимо? Какие ещё сетевые проблемы надо учитывать и как?



"Ошибки в отладке простенького ftp клиента."
Отправлено vic , 10-Сен-08 14:23 
>[оверквотинг удален]
>При взаимодействии с ftp сервером окончательный ответ сервера можно считать только тогда
>когда получаем 3 цифры и пробел в строке. Проблема в том
>что если по каким-то причинам нам не передали всю информацию которую
>мы рассчитывали считать функциями recv или read то процесс надолго зависает.
>Как можно защититься от этого? И какие ещё ситуации необходимо учитывать?
>Небольшие примеры кода очень бы помогли..
>
>Кстати.. при обрыве связи вызывается сигнал SIGPIPE который как я понимаю следует
>игнорировать и обрабатывать в программе как мне эт необходимо? Какие ещё
>сетевые проблемы надо учитывать и как?

Работать на чтение и запись через select(), тогда будете иметь таймаут на операцию, а следовательно избавитесь от зависаний, а также сможет обрабатывать ошибки на сокете и сигналы. По подробней читайте man sendto и man recv. Ну и в вышеуказанной книге это все описано.


"Ошибки в отладке простенького ftp клиента."
Отправлено my_way , 14-Сен-08 23:55 
Спасибо)

"Ошибки в отладке простенького ftp клиента."
Отправлено Allex , 17-Сен-08 11:07 
Когда передо мной встала похожая задача, я просто взял готовый опенсурс класс. Поверьте, не стоит тратить время на изобретение велосипеда, ну разве что только в ознакомительных целях.