#c #sockets
#c #сокеты
Вопрос:
Я изучаю сокеты и Im в процессе создания сервера сокетов, который может обслуживать файлы html, а также изображения в jpeg
формате. Основываясь на других подобных сообщениях, которые я нашел, я не могу понять, что я делаю неправильно. Я использую fseek
и ftell
, чтобы получить размер изображения, а затем я использую fread
для чтения этих байтов в malloc
массив символов ‘ed, который я затем отправляю обратно. Что я делаю не так?
код для работы с jpeg
файлами:
else if (!strcmp(getFileExtension(fname), "jpg") || !strcmp(getFileExtension(fname), "jpeg"))
{
printf("got a jpeg requestn");
FILE *fp = fopen(fname, "rb");
if (fp == NULL)
{
perror("Error opening .jpg/.jpeg file");
status = 500;
ret = toStr(status);
statusMessage = " Internal Server Errorn";
body = "<h1>Server encountered error trying to open jpeg file</h1>";
mode = html;
goto shutdown;
}
// seek to end of file and get
// length of the file to read
fseek(fp, 0, SEEK_END);
bodySize = ftell(fp);
printf("file is %d bytesn", bodySize);
// seek back to beginning of file
fseek(fp, 0, SEEK_SET);
// allocate and read len amt of bytes
char *data = malloc(bodySize);
fread(data, sizeof(char), bodySize, fp);
fclose(fp);
status = 200;
ret = toStr(status);
statusMessage = " OKn";
body = data;
mode = jpeg;
goto shutdown;
}
мой shutdown
код, который просто устанавливает заголовки, коды состояния и т. Д. Данные файла jpeg должны находиться в пределах body
переменной
// cleanup and closing
shutdown:
// send just the headers first
size = strlen(header) strlen(ret) strlen(statusMessage) strlen(mode) 1;
reply = (char *)malloc(size);
reply[size - 1] = '';
snprintf(reply, size, "%s%s%s%s", header, ret, statusMessage, mode);
printf("n%snn", reply);
send(new_sd, reply, size, 0);
// send just body
if (!strcmp(mode, jpeg)) // jpeg uses bodySize
{
printf("body size (%d):n%sn", bodySize, body);
send(new_sd, body, bodySize, 0);
}
else // non jpeg uses strlen
{
printf("body size (%d):n%sn", (int)strlen(body), body);
send(new_sd, body, strlen(body), 0);
}
shutdown(new_sd, SHUT_RDWR);
close(new_sd);
exit(0);
Вот мой ответ сервера браузеру (клиенту):
Комментарии:
1. Вероятно, не следует обрабатывать данные jpeg так, как будто это строка…
2. Согласовано — проверьте, что
strlen(body)
на самом деле оценивается в вашемshutdown
коде. Я предполагаю, что 4, учитывая ответ сервера. Приstrlen
попадании на a0
он вернется — ваши данные в формате JPEG могут содержать много0
символов. Вместо этого вы могли бы использовать своюlen
переменную из ранее для этой цели.3. Или вы могли бы просто сначала отправить заголовки, а затем отправить тело, избегая копирования данных изображения
4. Я принял предложение @vmt и теперь отправляю body и headesr отдельно. Я также перестал использовать strlen для двоичных файлов jpeg. Я обновил свой пост, чтобы отразить этот код. Файл jpeg по-прежнему отображается неправильно
Ответ №1:
Ничто не выделяется для меня с точки зрения кода сокета. Вместо того, чтобы быть проблемой (только) сокета, может быть, это (также) проблема протокола? Возможно, проблема с несоответствием HTTP / 1.1 (RFC 7230 )? не указаны ни длина содержимого, ни кодировка передачи? плохие окончания строк? (строка состояния обычно заканчивается на OK r n, а не OK n n)
Ответ №2:
не следует использовать snprintf
with %s
для двоичных данных, таких как байты jpeg, я считаю %s
, что завершается нулевым байтом (0x00), что приводит к повреждению двоичного файла.
Также strlen(body)
не будет работать, поскольку strlen также завершается нулевым байтом (0x00), что дает неверную длину для данных jpeg.
Попробуйте memcpy
вместо этого.
Откройте файл в этом режиме "rb "
Простой псевдокод (может не компилироваться)
int jpegDataSize = len;
char* jpegdata = data;
reply_size = strlen(HEADER) strlen(ret) strlen(statusMessage) strlen(mode) jpegDataSize 1; //can't use strlen for jpeg because it's binary
reply = (char *)malloc(reply_size);
memcpy(amp;reply[0], HEADER, strlen(HEADER));
memcpy(amp;reply[strlen(HEADER)], ret, strlen(ret));
memcpy(amp;reply[strlen(ret)], statusMessage, strlen(statusMessage));
memcpy(amp;reply[strlen(statusMessage)], mode, strlen(mode));
memcpy(amp;reply[strlen(mode)], jpegdata, jpegDataSize); //avoid strlen(jpegdata)
reply[reply_size - 1] = '';
printf("n-------------------------------------nresponse is (%d): n%snn", reply_size, reply); //can't use strlen (because jpegdata is binary)
send(new_sd, reply, reply_size, 0); //can't use strlen because jpegdata is binary.
shutdown(new_sd, SHUT_RDWR);
close(new_sd);
exit(0);
Комментарии:
1. Я воспользовался предложением @vmt из другого комментария отправить заголовок и тело отдельно, поэтому использование
snprintf
должно быть в порядке, поскольку оно предназначено только для HTTP-заголовка / кода состояния / сообщения / типа содержимого.
Ответ №3:
Оказывается, в коде моего отредактированного сообщения нет никаких логистических проблем. Проблема заключалась в том, что в коде завершения работы, когда я отправляю ответ в заголовке HTTP, я использовал size в качестве длины ответа, когда я должен был использовать strlen(reply)
или size - 1
для send(new_sd, reply, size, 0)
.