Quote:
Originariamente inviato da aeroxr1
Ma anche la prima potrebbe avere questo problema giusto ? ( per capire se ho capito Ihih)
Pensavo che TCP del sender segmentasse i pezzetti, ma poi il receiver ricomponesse il tutto prima di darli alla socket . Non è così ? :/
Inviato dal mio Galaxy Nexus utilizzando Tapatalk
|
Come ha detto WarDuck, il protocollo TCP ti assicura che i dati arrivino tutti (a meno che non cada la connessione) e nell'ordine corretto, ma non garantisce nulla riguardo alla loro atomicità. La parte più piccola che sei sicuro di ricevere è il byte, inviare anche solo due byte in sequenza (anche utilizzando un'unica send) non garantisce che con un'unica recv li riceverai entrambi.
Nota a margine: di solito il sistema operativo fa automaticamente il flush del buffer anche se non è pieno se dopo un po' di tempo non gli arrivano dati, per cui se il protocollo applicativo è basato su una comunicazione messaggio/risposta (quindi la parte non manda mai due messaggi di seguito senza prima aver ricevuto risposta)
e la lunghezza del messaggio non supera la dimensione del pacchetto, allora il messaggio arriverà in un unico pacchetto e senza dati eccedenti, e sarà possibile leggerlo con un'unica recv. Questo approccio naturalmente si basa sulla validità delle condizioni elencate, se anche una sola di esse non è soddisfatta si ottengono comportamenti inaspettati. Per quanto riguarda l'autoflush non bisogna commettere l'errore di pensare di utilizzare fflush, perché questo comando agisce solo sui buffer delle librerie del c e non su quelli di sistema. Anche impostare TCP_NODELAY non è sufficiente, perché c'è un ulteriore meccanismo di caching dei buffer legato all'acknowledgement... insomma, l'unico modo certo per far funzionare le cose sempre e comunque è quello di scrivere uno strato software che accumuli i dati fino alla lunghezza desiderata prima di leggerli.
Codice:
struct message_header
{
uint32_t length;
char variable_message[0]; // nn ricordo ma dovrebbe funzionare su C99
};
In pratica inserisci nella struttura un campo che il compilatore interpreta come un array di char (attenzione, un array in-place, non un puntatore ad un array posizionato chissà dove), ma non ne definisci la lunghezza, poi a runtime allochi la struttura con dello spazio eccedente ed accedi al campo sforando oltre la dimensione teorica (0).
Se da qualche errore di compilazione si può tranquillamente definire l'array di lunghezza 1 e poi ricordarsi di allocare un byte in meno (ma anche se ci dimentichiamo non satureremo la memoria per un solo byte).
Il problema di questo metodo è che mandando quella struct sul socket non consideri eventuali differenze di endianess tra client e server. Personalmente opterei per le due send, non ci vedo nulla di male.
Quote:
L'unico problema sarebbe se il buffer non è stato riempito per quanti byte mi aspettavo a quel punto la mia recv mi riporterà i byte letti che saranno in numero minore a quelli che mi aspettavo.
|
Esatto, perché recv legge
fino a n byte, ma anche se ce n'è uno solo lo legge lo stesso.
Quote:
Devo capire come inviare una struttura
|
La variabile che rappresenta la tua struttura non è altro che il puntatore restituito da malloc, basta che ne fai il cast a void* quando lo passi a send, tuttavia, come ho scritto prima, questo metodo non è l'ideale (a meno che non sei sicuro che tutte le piattaforme coinvolte abbiano la stessa endianess).