Domanda

Si consideri il seguente codice:

writer.c

mkfifo("/tmp/myfifo", 0660);

int fd = open("/tmp/myfifo", O_WRONLY);

char *foo, *bar;

...

write(fd, foo, strlen(foo)*sizeof(char));
write(fd, bar, strlen(bar)*sizeof(char));

reader.c

int fd = open("/tmp/myfifo", O_RDONLY);

char buf[100];
read(fd, buf, ??);

La mia domanda è:

Poiché non è sapere di prima mano quanti byte si foo e bar hanno, come faccio a sapere quanti byte da leggere dal reader.c?
Perché se io, ad esempio, leggere 10 byte in lettore e foo e bar sono insieme meno di 10 byte, io li hanno entrambi nella stessa variabile e che io non voglio.
Idealmente avrei una funzione di lettura per ogni variabile, ma ancora una volta non so prima di mano il numero di byte saranno i dati di avere.
Ho pensato di aggiungere un'altra istruzione di scrittura nel writer.c tra la scrittura per foo e bar con un separatore e quindi avrei alcun problema decodifica da reader.c. È questo il modo per andare a questo proposito?

Grazie.

È stato utile?

Soluzione

Un separatore è un modo per andare su di esso, e questo lavoro volere bene, fino a quando si conosce l'ordine dei dati, e si utilizza il separatore come solo un separatore, e mai come parte dei dati.

Un altro modo è quello di precedere ogni scrittura al tubo con il numero di byte da seguire, in una larghezza fissa. In questo modo, si saprà la quantità di dati sta per venire giù per il tubo. Utilizzare una larghezza fissa, in modo da sapere esattamente quanto tempo il campo di larghezza sarà, in modo da sapere sia quando per avviare e interrompere la lettura di ogni blocco di dati.

Altri suggerimenti

Molte delle altre risposte menzione utilizzando una sorta di protocollo per i dati e credo che questo sia l'approccio corretto. Questo protocollo può essere semplice o complesso come necessario. Ho fornito un paio di esempi che si possono trovare utili 1 .


In un caso semplice, si può avere solo un byte di lunghezza seguito dal byte di dati (s) (cioè C stringa).

+--------------+
| length byte  |
+--------------+
| data byte(s) |
+--------------+

Writer:

uint8_t foo[UCHAR_MAX+1];
uint8_t len;
int fd;

mkfifo("/tmp/myfifo", 0660);
fd = open("/tmp/myfifo", O_WRONLY);

memset(foo, UCHAR_MAX+1, 0);
len = (uint8_t)snprintf((char *)foo, UCHAR_MAX, "Hello World!");

/* The length byte is written first followed by the data. */
write(fd, len, 1);
write(fd, foo, strlen(foo));

Reader:

uint8_t buf[UCHAR_MAX+1];
uint8_t len;
int fd;

fd = open("/tmp/myfifo", O_RDONLY);

memset(buf, UCHAR_MAX+1, 0);

/* The length byte is read first followed by a read 
 * for the specified number of data bytes.
 */
read(fd, len, 1);
read(fd, buf, len);

In un caso più complesso, si può avere una lunghezza di byte seguito dai dati byte contenente più di una semplice stringa C.

+----------------+
|  length byte   |
+----------------+
| data type byte |
+----------------+
|  data byte(s)  |
+----------------+

Intestazione Comune:

#define FOO_TYPE 100
#define BAR_TYPE 200

typedef struct {
    uint8_t type;
    uint32_t flags;
    int8_t msg[20];
} __attribute__((aligned, packed)) foo_t;

typedef struct {
    uint8_t type;
    uint16_t flags;
    int32_t value;
} __attribute__((aligned, packed)) bar_t;

Writer:

foo_t foo;
unsigned char len;
int fd;

mkfifo("/tmp/myfifo", 0660);
fd = open("/tmp/myfifo", O_WRONLY);

memset(&foo, sizeof(foo), 0);
foo.type = FOO_TYPE;
foo.flags = 0xDEADBEEF;
snprintf(foo.msg, 20-1, "Hello World!");

/* The length byte is written first followed by the data. */
len = sizeof(foo);
write(fd, len, 1);
write(fd, foo, sizeof(foo));

Reader:

uint8_t buf[UCHAR_MAX+1];
uint8_t len;
uint16_t type;
union data {
    foo_t * foo;
    bar_t * bar;
}
int fd;

fd = open("/tmp/myfifo", O_RDONLY);

memset(buf, UCHAR_MAX+1, 0);

/* The length byte is read first followed by a read 
 * for the specified number of data bytes.
 */
read(fd, len, 1);
read(fd, buf, len);

/* Retrieve the message type from the beginning of the buffer. */
memcpy(&type, buf, sizeof(type));

/* Process the data depending on the type. */
switch(type) {
    case FOO_TYPE:
        data.foo = (foo_t)buf;
        printf("0x%08X: %s\n", data.foo.flags, data.foo.msg); 
        break;
    case BAR_TYPE:
        data.bar = (bar_t)buf;
        printf("0x%04X: %d\n", data.bar.flags, data.bar.value); 
        break;
    default:
        printf("unrecognized type\n");
}

1 -. Questo codice è stato scritto dalla memoria e non è testato

Un separatore è infatti un modo per fare questo - e abbastanza comodamente, stringhe C sono dotati di una tale separazione -. Il nul-terminator alla fine della stringa

Se si modificano le chiamate write() in modo che anche loro scrivono il nul-terminatore (nota che sizeof(char) è definito come 1, in modo che possa essere lasciato fuori):

write(fd, foo, strlen(foo) + 1);
write(fd, bar, strlen(bar) + 1);

Si può quindi scegliere a parte le corde dopo la lettura in (sarà ancora bisogno di leggerli in un buffer e poi rompere loro parte, a meno di leggere loro un carattere alla volta).

Per generalizzare la risposta di tromba d'aria un po ', hai avuto modo di stabilire un protocollo di una certa varietà. Ci deve essere fine a quello che si sta inviando, altrimenti non si sa in alto dal basso, come fai notare.

Entrambi i suggerimenti del Whirlwind funzionerà. Si può anche andare al punto di implementare una particolare (o standard) protocollo sulla cima di un tubo o FIFO per fare il porting del codice per un ambiente più distribuito con diversi sistemi e facile compito più tardi. Il nocciolo della questione, però, è che hai avuto modo di regole stabilite per la comunicazione prima che tu sia in grado di comunicare in realtà.

Si dovrà definire una sorta di protocollo filo o formato di serializzazione / deserializzazione in modo che il lettore sa come interpretare i dati che è la lettura dal FIFO. Utilizzando un separatore è il modo più semplice per andare su questo, ma si incorrerà in problemi se il separatore mai appare come parte della produzione di dati del vostro scrittore.

Un po 'più in là lungo la scala di complessità, il protocollo potrebbe definire sia un separatore e un modo di indicare la lunghezza di ogni "pezzo" o "messaggio" di dati che si sta inviando.

Infine, il problema è più completamente risolto con la scrittura di messaggi serializzati che il vostro scrittore sarà poi deserializzare dopo la ricezione. Si può essere interessati a utilizzare qualcosa come buffer protocollo o Thrift per raggiungere questo obiettivo (con il valore aggiunto che è possibile implementare il lettore o lo scrittore in un certo numero di diversi linguaggi di programmazione senza modificare il protocollo) .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top