Почему эта функция C падает при fclose? - PullRequest
2 голосов
/ 15 мая 2009

Пожалуйста, помогите мне понять, почему эта функция выдает исключение, когда достигает fclose:


void receive_file(int socket, char *save_to, int file_size) {
    FILE *handle = fopen(save_to,"wb");
    if(handle != NULL) {
        int SIZE = 1024;
        char buffer[SIZE];
        memset(buffer,0,SIZE);
        int read_so_far = 0;
        int read_now = 0;
        int reading_unit = (file_size < SIZE) ? file_size : SIZE;
        do {
            read_now = read(socket,buffer,reading_unit);
            fwrite(buffer,read_now,1,handle);
            read_so_far += read_now;
            if(read_so_far >= file_size) {
                break;
            }
            memset(buffer, 0, sizeof(buffer));
        } while (1);
        read_now = 0;
        fclose(handle);
    }
    else {
        extern int errno;
        printf("error creating file");
        printf(" error code : %d",errno);
        exit(-1);
    }
}

Eclipse CDT существует со следующей ошибкой:

Single stepping until exit from function __kernel_vsyscall, 
which has no line number information.

Цель этой функции - получить файл через сокет.

РЕДАКТИРОВАТЬ: я использую CentOS 5.3. Дело в том, что файл создан и записан. Даже MD5 правильный. Я не понимаю, почему это не удается в fclose.

EDIT2: вот трассировка стека, которую мне удалось получить:

*** glibc detected *** /home/linuser/workspace/proj/Debug/proj: free(): invalid next size (normal): 0x096a0068 ***
======= Backtrace: =========
/lib/libc.so.6[0x4fb0f1]
/lib/libc.so.6(cfree+0x90)[0x4febc0]
/lib/libc.so.6(fclose+0x136)[0x4e9c56]
/home/linuser/workspace/proj/Debug/proj[0x8048cd8]
/home/linuser/workspace/proj/Debug/proj[0x80492d6]
/home/linuser/workspace/proj/Debug/proj[0x804963d]
/lib/libc.so.6(__libc_start_main+0xdc)[0x4a7e8c]
/home/linuser/workspace/proj/Debug/proj[0x8048901]
======= Memory map: ========
001ff000-00200000 r-xp 001ff000 00:00 0          [vdso]
0046f000-00489000 r-xp 00000000 08:06 1280361    /lib/ld-2.5.so
00489000-0048a000 r-xp 00019000 08:06 1280361    /lib/ld-2.5.so
0048a000-0048b000 rwxp 0001a000 08:06 1280361    /lib/ld-2.5.so
00492000-005d0000 r-xp 00000000 08:06 1280362    /lib/libc-2.5.so
005d0000-005d2000 r-xp 0013e000 08:06 1280362    /lib/libc-2.5.so
005d2000-005d3000 rwxp 00140000 08:06 1280362    /lib/libc-2.5.so
005d3000-005d6000 rwxp 005d3000 00:00 0 
005d8000-005fd000 r-xp 00000000 08:06 1280369    /lib/libm-2.5.so
005fd000-005fe000 r-xp 00024000 08:06 1280369    /lib/libm-2.5.so
005fe000-005ff000 rwxp 00025000 08:06 1280369    /lib/libm-2.5.so
009b2000-009bd000 r-xp 00000000 08:06 1280372    /lib/libgcc_s-4.1.2-20080825.so.1
009bd000-009be000 rwxp 0000a000 08:06 1280372    /lib/libgcc_s-4.1.2-20080825.so.1
009c5000-00aa5000 r-xp 00000000 08:06 5465873    /usr/lib/libstdc++.so.6.0.8
00aa5000-00aa9000 r-xp 000df000 08:06 5465873    /usr/lib/libstdc++.so.6.0.8
00aa9000-00aaa000 rwxp 000e3000 08:06 5465873    /usr/lib/libstdc++.so.6.0.8
00aaa000-00ab0000 rwxp 00aaa000 00:00 0 
08048000-0804a000 r-xp 00000000 08:06 4884214    /home/linuser/workspace/proj/Debug/proj
0804a000-0804b000 rw-p 00001000 08:06 4884214    /home/linuser/workspace/proj/Debug/proj
096a0000-096c1000 rw-p 096a0000 00:00 0          [heap]
b7e00000-b7e21000 rw-p b7e00000 00:00 0 
b7e21000-b7f00000 ---p b7e21000 00:00 0 
b7f99000-b7f9a000 rw-p b7f99000 00:00 0 
b7faa000-b7fac000 rw-p b7faa000 00:00 0 
bfa82000-bfa97000 rw-p bffea000 00:00 0          [stack]

и вот «обновленная» версия функции:


void rec_file(int socket,char *path,int size)
{
    FILE *handle = fopen(path,"wb");
    char buffer[4096];
    int total_read = 0;
    if(handle != NULL)
    {
        while(1)
        {
            int bytes_read = recv(socket,buffer,4096,0);
            total_read    += bytes_read;
            if(bytes_read != -1)
            {
                fwrite(buffer,bytes_read,1,handle);
            }
            else
            {
                printf("read error ");
                exit(-1);
            }
            if(total_read >= size)
            {
                break;
            }
        }
        fclose(handle);
    }
    else
    {
        printf("error receiving file");
        exit(-1);
    }
}

Может быть, это чище? Тем не менее, я все еще получаю то же самое fclose исключение.

EDIT3: Я закомментировал все и оставил только следующий код, который, к сожалению, по-прежнему выдает исключение в fclose:


void nrec(int sock,char* path,int size)
{
    FILE *handle = fopen(path,"wb");
    if(handle == NULL)
    {
        printf ("error opening file");
        return;
    }
    fclose(handle);
}

Ответы [ 10 ]

6 голосов
/ 15 мая 2009
  • fclose () возвращает значение: 0, если файл успешно закрыт, EOF, если нет. Вы можете изменить свой код на ...

    if (fclose (handle)) {printf ("ошибка закрытия файла."); Выход (-1); }

  • Как и все остальные, проверьте read () на наличие ошибки.

  • Если происходит сбой после , fclose возвращает 0 (успех), и все операции read () были успешными, тогда, возможно, проблема в другом месте. Может быть, это VLA. Попробуйте изменить код буфера на файл статический или malloc / free.

  • Если вызывающий объект ошибся по поводу размера файла, скажем, и заявил, что размер файла составляет 500 байт, а на самом деле его всего 480, будет ли ваш код продолжать читать сокет вечно?

2 голосов
/ 15 мая 2009

Вы не проверяли, получили ли вы сообщение об ошибке при вызове read(). Если вы получаете -1 из read(), вы затем передаете его, не проверяя, в fwrite(), что принимает size_t в качестве аргумента. Когда ваше значение int преобразуется в size_t (или рассматривается как size_t), оно становится довольно большим числом (4 ГБ - 1 в 32-разрядной системе). Итак, fwrite() выполняет по вашей команде - пытается записать много данных из тех мест, для которых это не предусмотрено.

2 голосов
/ 15 мая 2009

Не уверен конкретно, но вы знаете, что read () может вернуть отрицательный результат при ошибке, верно?

2 голосов
/ 15 мая 2009

Этот код кажется очень ... неуверенным в себе. Множество ненужных заданий, memset() звонки, сложная логика.

Нет смысла ограничивать количество, которое вы пытаетесь прочитать. Всегда читайте столько, сколько у вас есть буферное пространство для; если данных будет меньше, вы получите меньше.

Он также неправильно обрабатывает ошибки. Если read() терпит неудачу, он может вернуть -1, и в этом случае плохие вещи (TM) будут происходить повсюду.

Мой совет - очистить его, подумать о возвращаемых значениях из функций ввода-вывода, проверить их лучше, а затем повторить попытку.

В противном случае, если вы в Linux, запустите его через Valgrind , чтобы получить помощь по отслеживанию сбоя.

1 голос
/ 20 мая 2009

Если ваша версия кода «EDIT3» все еще не работает, проблема кроется в другом месте вашего кода. Если вы допустили ошибку в другой части вашей программы (например, дважды освободив что-то, разыменовав указатель, который не указывал туда, где вы думали), то полученное повреждение может остаться незамеченным позже (например, когда вы достигнете fclose ()).

1 голос
/ 15 мая 2009

Вы уверены, что не повредили кучу в другом месте, прежде чем перейти к этой функции? Попробуйте запустить вашу программу под valgrind.

1 голос
/ 15 мая 2009

Код в edit3 выглядит разумно. Я подозреваю, что вы, вероятно, перегружены памятью где-то еще и пожинаете проблему в этот момент в вашем приложении.

Есть ли у вас средства проверки границ, очистки и т. Д., Которые вы можете запустить для проверки переполнения буфера, используя блок свободной памяти и т. Д.

1 голос
/ 15 мая 2009

Вам необходимо проверить возвращаемое значение read () - оно может вернуть код ошибки, например -1, а также количество прочитанных байтов.

Редактировать: В вашем новом коде вы должны проверить, что возвращаемое значение из recv ()>> 0, а не -1 Вы должны также прервать, если он вернет ноль.

0 голосов
/ 15 мая 2009

Я не уверен, как компилятор интерпретирует эти две строки

int SIZE = 1024;
char buffer[SIZE];

Вы используете переменный размер буфера, а не константу. Я не уверен, будет ли это вести себя так, как вы ожидаете. Не могли бы вы попробовать следующее.

#define SIZE (1024)
char buffer[SIZE];
0 голосов
/ 15 мая 2009

Может быть потому, что вы записываете в файл больше байтов, чем file_size? Используя текущую логику, если для file_size задано значение 1025, в результате вы получите 2048 байтов в файл. Я полагаю, вам следует пересчитать read_unit внутри цикла while.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...