#c #sockets
#c #сокеты
Вопрос:
Я пытаюсь отправить изображение в формате jpg из клиентского процесса на сервер через sockets
. Изображение содержит двоичные данные, поэтому я хочу сделать это на основе низкоуровневого программирования, используя reads
и writes
. Я также отправляю данные изображения итерациями по 100 байт.
Это код, который я сделал, который не отправляет изображение идентично тому, что я хочу:
КЛИЕНТ
void send_image(char *path, char *filename, int socket) {
int fd = open(path, O_RDONLY); //I open the file of the image.jpg
int n = 1;
while (n > 0) {
char img_data[100];
n = read(fd, img_data, 100); //sending 100 bytes of image each iteration till n=0 (end of file)
if (!n) break;
int sending = 1;
write(socket, amp;sending, sizeof(int)); //Tell the client the image still has data to send
write(socket, img_data, strlen(img_data));
usleep(250);
}
sending = 0; //Tell the server the image has been fully sent
write(socket, amp;sending, sizeof(int));
close(fd);
}
СЕРВЕР
void receiving_image(char *path) {
int receiving = 0;
int j=0;
char *image_data = NULL; //Variable to store all the image data
read(socket, amp;receiving, sizeof(int)); //Reads that the client is going to send an image
while (receiving) {
char data[100]; //Variable that stores partial data (100 bytes) of an image on each iteration
read(socket, data, 100);
image_data = realloc(image_data, (j strlen(data)) * sizeof(char)); //Readjust the size of the main image data.
for (int i=0; i<(int) strlen(data); i ) {
image_data[j] = data[i]; //copy the partial data of the image to the main variable of the image
j ;
}
j = (int) strlen(image_data);
read(socket, amp;receiving, sizeof(int)); //Read if the image is still sending
}
image_to_directory(path, image_data); //Copy image to directory
}
Это компилируется и работает нормально, но когда я проверяю каталог на стороне сервера, где было сохранено изображение, я вижу, что это не то же самое изображение, которое отправил клиент (я подтвердил через md5sum, и хэши не равны).
Есть ли что-то, чего мне не хватает?
Комментарии:
1.
strlen(img_data)
иstrlen(data)
ошибочны. Строковые функции могут использоваться только для строк, а не для двоичных данных. Используйте возвращаемое значениеread
вместо, чтобы получить количество прочитанных / полученных байтов.2.
read()
возвращает-1
значение в случае ошибки, а не 0. Вы должны проверить<=0
.3. Это не ваша проблема, поскольку я предполагаю, что сервер и клиент работают на одинаковых машинах, но
int
могут иметь разный размер на сервере и клиенте. Лучше использоватьuint32_t
или что-то в этом роде.4. @12431234123412341234123 на какую строку вы ссылаетесь? Не
while (n > 0)
выполняет этого?5.
while (n > 0)
слишком поздно. Еслиread
возвращает-1
, код продолжается и использует результатread
, прежде чем он получит шанс выйти из цикла. Фактическая неправильная проверка, на которую ссылаютсяif (!n)
Ответ №1:
Вы не должны использовать strlen
для своих вычислений длины двоичных данных. Он предназначен только для завершенных строк (отсюда и название). У вас также есть крайне опрометчивые открытые вызовы для чтения / записи, что является рецептом катастрофы при отправке данных через сокеты.
Кажется, вы никогда не отправляете более 100 байт за раз, что в данном случае полезно для разработки более надежного протокола. Рассмотрим это:
- Первый октет — это количество
uint8_t
байтов N, и он будет в 0 .. 100. - После подсчета байтов передается N байтов.
- Повторяйте 1-2, пока не останется больше байтов.
- Уведомить сервер о EOF, отправив один нулевой октет
Пример этого кода отправителя показан здесь.
void send_image(const char *path, int socket)
{
int fd = open(path, O_RDONLY); //I open the file of the image.jpg
if (fd == -1)
return;
ssize_t n = 0;
do
{
// note the first octet will prefix the length
uint8_t img_data[101];
n = read(fd, img_data 1, 100);
if (n > 0)
{
// you never know just how many bytes are going to
// be sent, so setup the frame, but then ensure even
// piecewise deliver can succeed.
img_data[0] = (uint8_t)n;
ssize_t sent = 0;
size_t pos = 0;
do
{
sent = write(socket, img_data pos, (n 1)-pos);
if (sent < 0)
break;
pos = sent;
} while ( pos < (n 1) amp;amp; sent > 0);
}
} while (n > 0);
uint8_t done = 0;
write(socket, amp;done, sizeof done); // not much we can do if this fails
close(fd);
}
Я не утверждаю, что приведенный выше код будет даже компилироваться, но концепция должна быть достаточно очевидной. Однако это все. Очевидно, что можно / нужно сделать больше (контрольные суммы, параметры перезапуска и т. Д.), Но это основная предпосылка.
Серверная сторона может сделать что-то подобное, что я оставляю в качестве упражнения для вас. Смысл всего этого в том, чтобы использовать возвращаемые значения из ваших вызовов чтения / записи. Они существуют по какой-то причине. Если вы обнаружите, что кодируете «голое» чтение или запись (где вы не собираете результат функции и не используете его каким-либо образом), скорее всего, вы сделали что-то ужасно неправильное.