Как отправить текст и двоичный файл через сокет в одном сообщении в C?

Я знаю, как отправить текстовое сообщение через сокет в C, и я знаю, как отправить двоичные данные (файл изображения) через сокет.

Как создать одно сообщение, включающее текстовую часть, а затем двоичные данные? Например:

"messageId: 12, messageLength: 34

{image binary data}"

Вы уже знаете, как отправлять текст, и как отправлять двоичные файлы, так что же вам мешает совместить их? Сначала отправьте текст, а затем отправьте двоичный файл. TCP не имеет понятия о сообщениях, поэтому ему все равно, сколько вы отправляете. Ваш протокол диктует, что содержит сообщение. Взгляните, например, на протокол HTTP. Он использует смешанные текстовые + двоичные сообщения.

Remy Lebeau 09.04.2023 10:12

@RemyLebeau — HTTP может отправлять текст и изображение, но вы отправляете изображение в формате base64 (что составляет + 33% к размеру сообщения).

Manifestor 09.04.2023 10:18

Если вам нужен HTTP, укажите это в заголовке. Вы можете отправить через сокет много вещей, которые нарушат протокол HTTP.

n. m. 09.04.2023 11:55

@н.м. Мне нужен чистый сокет TCP

Manifestor 09.04.2023 12:54

Зачем тогда поднимать HTTP? Кстати, HTTP не заставляет вас использовать base64 для отправки изображений.

n. m. 09.04.2023 13:43

@н.м. в заголовке ничего про HTTP нет, это TCP. Я писал о HTTP, когда отвечал RemyLebeau.

Manifestor 09.04.2023 14:04

Я упомянул HTTP просто как пример протокола, который может отправлять текст и двоичные файлы в одном сообщении.

Remy Lebeau 09.04.2023 18:40

Выберите протокол, который работает.

Martin James 09.04.2023 19:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

TLDR: memcpy и специальный символ-разделитель между текстовым заголовком и двоичными данными. Разделителем может быть символ конца строки (\n).

char* CreateMessage(int messageId, size_t messageLength, char* binaryData, /*out*/ size_t* total)
{
    char szHeader[100] = {0};
    size_t headerLen = 0;

    sprintf(szHeader, "messageId: %d, messageLength: %d\n", messageId, messageLength);

    headerLen = strlen(szHeader);

    total = headerLen + messageLength;

    char* data = malloc(total);
    memcpy(data, header, headerLen);
    memcpy(data+headerLen, binaryData, messageLength);

    return data;
}

И затем, когда вы хотите отправить некоторые данные:

size_t total = 0;
char* message = CreateMessage(12, 34, binaryData, &total);
send(s, message, total, 0);
free(message);

В качестве альтернативы, если вы используете TCP, нет причин, по которым вы не можете использовать два вызова отправки. Один для заголовка (с символом '\n'), а другой для двоичных данных. Получатель в любом случае должен обрабатывать сообщения, которые сегментируются и фрагментируются.

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

Martin James 09.04.2023 19:31

@MartinJames - Правильно. Я хочу сказать, что он может легко комбинировать свой «текстовый» заголовок со своими «двоичными» данными, если использует соответствующий разделитель для обозначения конца текстовой строки.

selbie 09.04.2023 20:22
Ответ принят как подходящий

TCP не знает, что такое «текст» или «сообщение». Он имеет дело с потоками байтов. Если у вас есть массив байтов, вы можете отправить его по TCP. Если у вас есть более одного массива, вы можете отправлять их по TCP один за другим, и результат будет неотличим от объединения всех этих массивов в один и отправки их в виде одного большого куска байтов.

Отправка байтов --- любых байтов --- по TCP не проблема. Проблема заключается в извлечении значимых частей («сообщений») из потока байтов. Это не имеет ничего общего с TCP, и все, что связано с тем, как вы определяете «сообщение» с точки зрения содержимого вашего потока, то есть вашего протокола.

Вы можете определить свой протокол, сказав, что «сообщение — это строка, заканчивающаяся символом новой строки», и это подходит для отправки текстовых сообщений, в которых нет встроенной новой строки. Это не будет работать для двоичных сообщений, потому что двоичный BLOB-объект, скорее всего, будет иметь встроенный символ новой строки.

Вы можете определить сообщение, сказав, что «сообщение представляет собой 4-байтовую величину, интерпретируемую как беззнаковое целое число сетевого порядка, за которым следует такое количество байтов полезной нагрузки». Это нормально как для текстовых, так и для бинарных сообщений, но немного неудобно для текста, потому что вам нужно вычислить и отправить длину каждой строки.

Вы можете сказать: «Мой поток содержит строго чередующиеся текстовые и бинарные сообщения, текст завершается символом новой строки, а бинарному предшествует его длина», и это тоже будет работать, хотя вы не можете отправлять два текстовых или два бинарных сообщения подряд.

Вы можете сказать: «Мой поток содержит ровно два сообщения, текст, заканчивающийся новой строкой, а затем двоичный файл». Длина не передается явно, конец потока — это конец двоичного сообщения.

Видишь, это все есть в твоем протоколе. TCP не волнует. Все, что он знает, это байты.

Я скучал по чтению от вас. +1

Soner from The Ottoman Empire 09.04.2023 12:34

Почти идеально :)

Martin James 09.04.2023 19:33

Другие вопросы по теме