sprintf необработанные байты в строку в C? - PullRequest
2 голосов
/ 24 января 2020

Я посылаю несколько необработанных байтов по сети в C (используя HTTP). В настоящее время я делаю это так:

// response is a large buffer
int n = 0; // response length
int x = 42; // want client to read x
int y = 43; // and y 

// write a simple HTTP response containing a 200 status code then x and y in binary format
strcpy(response, "HTTP/1.1 200\r\n\r\n");
n += 16; // status line we just wrote is 16 bytes long
memcpy(response + n, &x, sizeof(x));
n += sizeof(x);
memcpy(response + n, &y, sizeof(y));
n += sizeof(y);
write(client, response, n);

В JavaScript я затем читаю эти данные, используя такой код:

request = new XMLHttpRequest();
request.responseType = "arraybuffer";
request.open("GET", "/test");
request.onreadystatechange = function() { if (this.readyState === XMLHttpRequest.DONE) { console.log(new Int32Array(this.response)) } }
request.send();

, который печатает [42, 43] как следует .

Мне интересно, есть ли более элегантный способ сделать это на стороне сервера, например,

n += sprintf(response, "HTTP/1.1 200\r\n\r\n%4b%4b", &x, &y);

Где %4b - это спецификатор выдуманного формата, который просто говорит: скопируйте 4 байта с этого адреса в строку (которая будет "* \ 0 \ 0 \ 0"). Есть ли спецификатор формата, такой как вымышленный %4b, который делает что-то подобное?

Ответы [ 2 ]

1 голос
/ 25 января 2020

Это проблема XY, вы спрашиваете, как использовать sprintf() для решения вашей проблемы, а не просто спрашиваете, как решить вашу проблему. Ваша настоящая проблема заключается в том, как сделать этот код более «элегантным».

Нет особой причины отправлять данные в одной операции записи - буферизация сетевого стека обеспечит эффективную упаковку данных:

static const char header[] = "HTTP/1.1 200\r\n\r\n" ;
write( client, header, sizeof(header) - 1 ) ;
write( client, &x, sizeof(x) ) ;
write( client, &y, sizeof(y) ) ; 

Обратите внимание, что X и Y будут записаны в машинном порядке байтов, который может быть неправильным в приемнике. Тогда более обобщенно:

static const char header[] = "HTTP/1.1 200\r\n\r\n" ;
write( client, header, sizeof(header) - 1 ) ;

uint32_t nl = htonl( x ) ;
write( client, &nl, sizeof(nl) ) ;

nl = htonl( y ) ;
write( client, &nl, sizeof(nl) ) ; 
0 голосов
/ 25 января 2020

Есть ли спецификатор формата, такой как вымышленный% 4b?

Нет, нет, и ваш метод в порядке. Я бы предложил использовать snprintf и некоторые проверки, чтобы избежать переполнения буфера, добавив ex. static_assert(sizeof(int) == 4, "") проверка того, что платформа использует порядковый номер с прямым порядком байтов и аналогичной средой и обработку ошибок, и предотвращение неопределенных проверок поведения.

Тем не менее, вы можете использовать %c спецификатор printf несколько раз, например, "%c%c%c%c", ((char*)&x)[3], ((char*)&x)[2], ((char*)&x)[1], ((char*)&x)[0], чтобы напечатать 4 байта , Вы можете обернуть его в макрос и сделать:

#include <stdio.h>

#define PRI_BYTES_4  "%c%c%c%c"
#define ARG_BYTES_BE_4(var) \
    ((const char*)&(var))[3], \
    ((const char*)&(var))[2], \
    ((const char*)&(var))[1], \
    ((const char*)&(var))[0]

int main() {
    int var = 
        'l' << 24 |
        'a' << 16 | 
        'm' << 8 |
        'e';
    printf("Hello, I am " PRI_BYTES_4 ".\n",
        ARG_BYTES_BE_4(var));
    // will print `Hello, I am lame.`
}
...