Передача файлов через сокеты, окончательный размер с меньшим количеством байтов
-
22-08-2019 - |
Вопрос
Я пытаюсь получить файл через сокеты в C.Но сервер отправляет мне, например, 64-байтовые пакеты для файла размером 1000000 байт, и я получаю примерно 999902 байта в файле назначения.
while ((n = read(sd, buffer_in, BUFSIZE ))) // BUFSIZE = 64
{
if(n<0)
{
printf("Fail.\n");
fclose(archivo);
return -1;
}
if(fwrite(buffer_in, n, 1, f) !=1 )
{
printf("fwrite error.\n");
fclose(archivo);
return -1;
}
bytes+=n;
}
printf("We received %d bytes", bytes);
При использовании через локальный сокет TCP/IP он работает, но не при медленном соединении.В ходе отладки я вижу, что получаю много 64-байтовых фрагментов и 30-байтовый фрагмент рядом с EOF.Я знаю, что вы можете получить меньше байтов при read(), поскольку вызов возвращается, когда доступны какие-либо данные (> 1 байт).Но это условие не должно быть уловлено вовремя?Должен вернуться, когда n == 0, то есть больше нет данных (EOF).
Спасибо за вашу помощь.
(РЕДАКТИРОВАТЬ)
Отправка кода следующим образом:
while (n=read(file_fd, buffer, BUFSIZE))
{
write (sdaccept, buffer, n)
}
Я знаю, что и read(), и write() могут возвращать N < BUFSIZE, но разве этот цикл не должен обрабатывать это соответствующим образом?Я суммировал и возвращает 1000000, точный размер.
(РЕДАКТИРОВАТЬ II)
Протестировано с исходным кодом C с 10673 байтами, получает 10575 без повреждений, за исключением того, что в целевом файле НЕТ первых 98 байтов!!!
Решение
Предоставленный код отправки игнорирует тот факт, что write() (или send()) в сокете не обязан записывать весь буфер.
write()/send() может решить записать его частично или не записывать вообще, если базовая подсистема отказывается получать больше данных (например, сетевая подсистема может иметь очередь для отправки данных, и эта очередь уже заполнена).Это весьма вероятно при медленном соединении.
Отправляющая сторона должна проверить возвращаемое значение write(), чтобы определить, сколько данных было фактически записано, и соответствующим образом скорректировать его.
Запись должна быть сделана как-то так:
int readAmount;
while( readAmount = read(file_fd, buffer, BUFSIZE) > 0 )
{
int totalWritten = 0;
do {
int actualWritten;
actualWritten = write (sdaccept, buffer + totalWritten, readAmount - totalWritten);
if( actualWritten == - 1 ) {
//some error occured - quit;
}
totalWritten += actualWritten;
} while( totalWritten < readAmount );
}