Добрый день. Относительно недавно начал программировать под 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;
}
>[оверквотинг удален]
>
> 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);и измените соответсвующие вызовы.
и так далее по тексту.
Спасибо, правда не совсем понимаю криминал использования ссылки, а не указателя. Вы не могли бы пояснить?Заметил странное..
1)Я соединяюсь с сервером. Тоесть дескриптор сокета вначале получаю от socket а потом connect.
2)Я открываю поток для чтения из сокета в функции через функции fdopen и там же закрываю его через fcloseПроблема в том что если я открываю поток вначале на чтение например в одной функции и после отработки закрываю, а потом пытаюсь открыть его на запись в другой функции то указатель на потом имеет значение NULL. А вот если я открываю сразу два потока на сокет один на чтение а другой на запись то всё работает нормально.
Почему я не могу открыть поток на сокет, потом закрыть, а потом открыть снова уже другой поток?
>Спасибо, правда не совсем понимаю криминал использования ссылки, а не указателя. Вы
>не могли бы пояснить?криминал? ммм... почитай Алена Голуба, там очень хорошо все расписано. самый главный недостаток, это сюрприз который сразу невидно. для читающего(или сопровождающего) твой код программиста это будет головной болью.
область применения ссылок это конструкторы классов, переопределения операторов, во всех остальных случаях лучше использовать указатель.>[оверквотинг удален]
>
>Проблема в том что если я открываю поток вначале на чтение например
>в одной функции и после отработки закрываю, а потом пытаюсь открыть
>его на запись в другой функции то указатель на потом имеет
>значение 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.
Большое вам спасибо! =)
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, осознать разницу подходов, почитать инет на тему серверов и поисследовать уже существующие исходники.
Спасибо за подробный разбор, поправил=) Проштудировать нормально Страуструпа всё никак не доходят руки.. вот и получаю на выходе поверхностные знания=(
А вы не могли бы посоветовать что-нибудь по сетевым приложениям? Одна из книг которую собираюсь купить это UNIX: разработка сетевых приложений Стивенсона, но похоже там и так есть многое из того что есть в Стивенс UNIX. Профессиональное программирование, которая мне сейчас помогает=)
написал случайно под ником своего друга
>А вы не могли бы посоветовать что-нибудь по сетевым приложениям? Одна
>из книг которую собираюсь купить это UNIX: разработка сетевых приложений Стивенсонаее и посоветую :)
В процессе возник следующий вопрос.
При взаимодействии с ftp сервером окончательный ответ сервера можно считать только тогда когда получаем 3 цифры и пробел в строке. Проблема в том что если по каким-то причинам нам не передали всю информацию которую мы рассчитывали считать функциями recv или read то процесс надолго зависает. Как можно защититься от этого? И какие ещё ситуации необходимо учитывать? Небольшие примеры кода очень бы помогли..Кстати.. при обрыве связи вызывается сигнал SIGPIPE который как я понимаю следует игнорировать и обрабатывать в программе как мне эт необходимо? Какие ещё сетевые проблемы надо учитывать и как?
>[оверквотинг удален]
>При взаимодействии с ftp сервером окончательный ответ сервера можно считать только тогда
>когда получаем 3 цифры и пробел в строке. Проблема в том
>что если по каким-то причинам нам не передали всю информацию которую
>мы рассчитывали считать функциями recv или read то процесс надолго зависает.
>Как можно защититься от этого? И какие ещё ситуации необходимо учитывать?
>Небольшие примеры кода очень бы помогли..
>
>Кстати.. при обрыве связи вызывается сигнал SIGPIPE который как я понимаю следует
>игнорировать и обрабатывать в программе как мне эт необходимо? Какие ещё
>сетевые проблемы надо учитывать и как?Работать на чтение и запись через select(), тогда будете иметь таймаут на операцию, а следовательно избавитесь от зависаний, а также сможет обрабатывать ошибки на сокете и сигналы. По подробней читайте man sendto и man recv. Ну и в вышеуказанной книге это все описано.
Спасибо)
Когда передо мной встала похожая задача, я просто взял готовый опенсурс класс. Поверьте, не стоит тратить время на изобретение велосипеда, ну разве что только в ознакомительных целях.