The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

форумы  помощь  поиск  регистрация  майллист  вход/выход  слежка  RSS
"Ошибки в отладке простенького ftp клиента."
Вариант для распечатки  
Пред. тема | След. тема 
Форумы Программирование под UNIX (Public)
Изначальное сообщение [ Отслеживать ]

"Ошибки в отладке простенького ftp клиента."  
Сообщение от my_way on 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;
}

Высказать мнение | Ответить | Правка | Cообщить модератору

 Оглавление

Сообщения по теме [Сортировка по времени | RSS]


1. "Ошибки в отладке простенького ftp клиента."  
Сообщение от NuINu (??) on 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);

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

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

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

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

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

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


Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

3. "Ошибки в отладке простенького ftp клиента."  
Сообщение от NuINu (??) on 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.

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

4. "Ошибки в отладке простенького ftp клиента."  
Сообщение от my_way on 03-Сен-08, 10:32 
Большое вам спасибо! =)
Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

5. "Ошибки в отладке простенького ftp клиента."  
Сообщение от vic (??) on 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, осознать разницу подходов, почитать инет на тему серверов и поисследовать уже существующие исходники.

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

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

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


Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

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

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

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

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

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


Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

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

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

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

11. "Ошибки в отладке простенького ftp клиента."  
Сообщение от my_way on 14-Сен-08, 23:55 
Спасибо)
Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

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

Архив | Удалить

Индекс форумов | Темы | Пред. тема | След. тема
Оцените тред (1=ужас, 5=супер)? [ 1 | 2 | 3 | 4 | 5 ] [Рекомендовать для помещения в FAQ]




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру