2009-05-15 3 views
2

Пожалуйста, помогите мне понять, почему эта функция генерирует исключение, когда он достигает fclose:Почему эта функция C сбой при 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.

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

EDIT: Я использую CentOS 5.3. Дело в том, что файл создан и написан. Даже MD5 правильный. Я не понимаю, почему он терпит неудачу.

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); 
} 
+0

Не пишите 'extern int errno;' - использовать #include ; потому что «errno» является изменяемым целочисленным значением l, но не обязательно int. В многопоточном программировании обычно #define errno * (errno_func()), где extern int * errno_func (void); –

+0

Вы исключили, что save_to (имя файла, который вы хотите записать), не мусор? Кроме того, какая операционная система? –

+0

Операции memset() действительно не нужны. –

ответ

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

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

  • Как все остальные указывают, проверьте, прочитано() для ошибки.

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

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

+1

У меня нет возможности выполнить эту проверку. Функция срабатывает, когда она достигает fclose. – Geo

+0

Он взорвется, когда вы установите read_now на 0? –

+1

Он взрывается, когда он входит в fclose. fclose не получает возможности закончить и вернуться. – Geo

2

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

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

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

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

Если это не так, если вы находитесь в Linux, запустите его через Valgrind, чтобы получить справку по отслеживанию сбоя.

2

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

2

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

1

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

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

+0

Этого не бывает.Я запускал код в режиме отладки. Я никогда не получаю -1. – Geo

+0

@Geo: еще не произошло - может случиться в будущем. Предположим, что все системные вызовы могут и будут терпеть неудачу в неудобное время. Исключение: getpid() не подводит - есть и другие. Но такие вызовы, как read(), которые имеют отказы, определенные * будут * терпеть неудачу, скорее, чем позже. –

0

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

+0

То же самое происходит. – Geo

0

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

int SIZE = 1024; 
char buffer[SIZE]; 

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

#define SIZE (1024) 
char buffer[SIZE]; 
+0

Либо компилятор должен принять его (если он находится в режиме C99), либо отклонить его как синтаксическую ошибку. – 2009-05-15 14:52:19

+0

Компилятор может принять его, а затем реализовать его с «сбоем при выходе». Новые возможности, новые возможности обучения. –

1

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

Есть ли у вас Bounds шашек, очистить и т.д., которые вы могли бы работать, чтобы проверить переполнение буфера, используя free'd блок памяти и т.д.

1

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

1

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

Смежные вопросы