|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#41 |
|
Senior Member
Iscritto dal: May 2001
Messaggi: 12861
|
Ah ecco, effettivamente bisognava essere molto pedantici
A questo punto la cosa migliore è come dice van: Codice:
struct message_h
{
...
char message[];
};
|
|
|
|
|
|
#42 | |
|
Senior Member
Iscritto dal: Jan 2014
Messaggi: 852
|
Quote:
In altre parole "char message[];" è esattamente equivalente a "char *message;", ed è assolutamente diverso da "char message[0];". Ma ribadisco che non mi piace in alcun caso. |
|
|
|
|
|
|
#43 |
|
Senior Member
Iscritto dal: Mar 2006
Messaggi: 2056
|
Nuovo dubbietto
Sto facendo la recv modificata per gestire la situazione recv==0 cioè la chiusura di una connessione aspettata o inaspettata , e recv<length . In questo caso pensavo di segnare il ritorno della recv in una variabile per riniziare da lì quando arrivano i rimanenti dati, una cosa del genere : il client fa send(strlen(dati)); il server : char *buffer; buffer=malloc(length*sizeof(char)); recvmod() { ret=recv(...) alla recv dopo inizio a scrivere da buffer[ret] } e questo con i dati che arrivano sotto forma di stringa. Quando deve arrivare la lunghezza dei dati però li mi nasce il problema, perchè non mi viene in mente un metodo per ricevere un campo int bufferizzandolo.. lo dovrei fare shiftando i dati della quantità ricevuta e facendo poi dopo la or ? (ora per ora faccio in maniera che la lunghezza length non sia mai superiore ad un byte) |
|
|
|
|
|
#44 |
|
Member
Iscritto dal: Jul 2011
Messaggi: 246
|
Mi inserisco tardi in questa discussione, per cui non sono sicuro di aver capito il tuo dubbio...
Per inviare un intero a dimensione fissa (esempio un intero senza segno a 32 bit) puoi fare qualcosa del genere: Codice:
uint32_t size = 123465; // l'intero che vuoi inviare size = htonl(size); // conversione in formato network send (sock, &size, sizeof(size), 0); Codice:
uint32_t size; recv(sock, &size, sizeof(size), 0); // ricevo esattamente quattro bytes size = ntohl (size); // conversione
__________________
Non c'è cosa peggiore nella vita di un programmatore di un errore che si presenta solo ogni tanto. CONCLUSO POSITIVAMENTE CON: oldfield Ultima modifica di Mettiu_ : 06-07-2014 alle 12:45. |
|
|
|
|
|
#45 | |
|
Senior Member
Iscritto dal: Mar 2006
Messaggi: 2056
|
Quote:
Il mio dubbio viene dal fatto che la recv ci sta non riceva tutti i byte insieme. Quindi considerando i 32 bit dell'int , se ricevo solo i primi 8 bit , cioè solo il primo byte avrei un errore no ? Quindi non mi servirebbe un sistema di bufferizzazione anche per l'int ? (prima di iniziare questa discussione non pensavo ci fossero di questi problemi e la ricezione la facevo anche io ricevendo la size dell'int Questa è la struttura che ho creato : Codice:
struct elemento
{
char *username; //Username del giocatore , crerò dinamicamente lo spazio in maniera di non sprecare memoria
short int UDPports;
unsigned long IPaddress;
int socket; //id del socket
char ready; //indica se il client ha effettuato il login o è sempre in attesa di login e quindi non ancora connesso
char impegnato; //mi indicherà Se il client è impegnato in un'altra partita
char cmd ; //il comando che stiamo eseguendo. Inizialmente in waiting !
int stato; //variabile di stato che mi dice in che parte della macchina a stati si trova il client
int length; //lunghezza campo dati
char *buffer; //buffer dove vengono ricevuti i dati temporaneamente prima di essere ricevuti completamente, buffer dinamico
int last; //variabile che indica fino a che punto abbiamo riempito il buffer in caso di lettura non terminata
struct elemento *next; //puntatore all'elemento successivo della lista
struct elemento *enemy; //puntatore alla struttura dell'avversario
};
typedef struct elemento player;
Codice:
int recvTCP(int sd, void* buf, int len) // possibile implementazione
{
int ret;
ret = recv(sd,buf,len,0);
if (ret<len)
{
if(ret==0) //chiusura connessione da parte di un client oppure errore
{
//chiusura connessione da parte di un client oppure errore
close(sd); //chiudo la socket
FD_CLR(sd,&master); //rimuovo la socket dal master set
printf("%s si è disconnesso dal server",client->username);
if(client->impegnato==YES) //si è chiuso durante una partita quindi è un problema da notificare all'avversario
{
//notifica(client->enemy->socket);
}
elem_remove(client,&connessi); //rimuove l'elemento dalla lista connessi
return -1; //non ritorna nessun byte c'è solo stata una chiusura
}
//qui ci vorrebbe un buffer da riempire fino a quando sono disponibili tutti i dati, cosa faccio ?
//chiedere al professore
return ret; //ritorna il numero di byte letti
}
return 0; // tutto ok
}
void riceviLength() //creato una funzione separata per la futura gestione del buffer della lunghezza
{
recvTCP(client->socket,&(client->length),sizeof(int)); //dato che ricevo un solo byte forse //è meglio metta 1 al posto di sizeof(int)
}
int riceviMsg()
{
int ret;
ret = recvTCP(client->socket,&(client->buffer[client->last]),client->length);
if(ret<0) return -1; //c'è stata una chiusura della connessione
if(ret==0)
{
client->last=0; //rimetto a 0 la variabile che mi indica dove sono rimasto a leggere
return 0; //dato completamente ricevuto
}
client->last=ret;
client->length=(client->length)-(client->last); //aggiorno la lunghezza da leggere all'interno della struttura client
return ret;
}
Ultima modifica di aeroxr1 : 06-07-2014 alle 13:19. |
|
|
|
|
|
|
#46 | |
|
Senior Member
Iscritto dal: Jan 2014
Messaggi: 852
|
Quote:
Codice:
char buf[4];
int n = 0;
while (n < 4) {
n += recv(sockfd, &buf[n], 4 - n, 0);
}
Codice:
char buf[4];
while (recv(sockfd, buf, 4, MSG_PEEK) < 4) {}
recv(sockfd, buf, 4, 0);
Ovviamente sul server il ciclo va gestito diversamente con la select, inoltre vanno gestiti gli errori. Dei due metodi il primo è da preferire, soprattutto nel caso di dati corposi. |
|
|
|
|
|
|
#47 | |
|
Senior Member
Iscritto dal: Mar 2006
Messaggi: 2056
|
Quote:
Quei cicli li posso fare sul client , sul server li dovrò spezzettare tutti Certo era tanto semplice se la select segnalava solo i socket pronti con il numero di byte voluti Per il passaggio da int a string itoa() ha problemi di portabilità che voi sappiate ? p.s : ora provo il codice che ho postato ieri, però lo provo in locale mettendo server e client sulla stessa macchina quindi sarà difficile vedere se ho strutturato bene i cicli per il buffer della recv |
|
|
|
|
|
|
#48 |
|
Senior Member
Iscritto dal: Jan 2014
Messaggi: 852
|
Itoa converte un int in una stringa in formato decimale, di lunghezza variabile. Ti conviene utilizzare sprintf se vuoi trasmettere in formato decimale di lunghezza fissa, oppure utilizzare shift e and per prendere i byte uno alla volta.
|
|
|
|
|
|
#49 | |
|
Senior Member
Iscritto dal: Mar 2006
Messaggi: 2056
|
Quote:
|
|
|
|
|
|
|
#50 |
|
Senior Member
Iscritto dal: Mar 2006
Messaggi: 2056
|
Ho deciso di implementare da subito il buffer anche per la lunghezza in maniera da non avere limiti sul buffer del dato
Ecco qua come l'ho scritto, potete dargli un occhiata per vedere se secondo voi è corretto ? Va bè capisco se non ne avrete voglia Codice:
int recvTCP(int sd, void* buf, int len) //DUBBIO : Per ora in caso di ritorno < lenght non faccio la chiusura, ma gestisco il buffer !
{
int ret;
ret = recv(sd,buf,len,0);
if (ret<len)
{
if(ret==0) //chiusura connessione da parte di un client oppure errore
{
//chiusura connessione da parte di un client oppure errore
close(sd); //chiudo la socket
FD_CLR(sd,&master); //rimuovo la socket dal master set
printf("%s si è disconnesso dal server",client->username);
if(client->impegnato==YES) //si è chiuso durante una partita quindi è un problema da notificare all'avversario
{
//notifica(client->enemy->socket);
}
elem_remove(client,&connessi); //rimuove l'elemento dalla lista connessi
return -1; //non ritorna nessun byte c'è solo stata una chiusura
}
//qui ci vorrebbe un buffer da riempire fino a quando sono disponibili tutti i dati, cosa faccio ?
//chiedere al professore
return ret; //ritorna il numero di byte letti
}
return 0; // tutto ok
}
int riceviLength() //creato una funzione separata per la futura gestione del buffer della lunghezza
{
//in realtà inviando un int per la dimensione invio ben 4byte, devo gestire un buffer per la lunghezza
int ret;
int temp; //ci viene salvato il dato letto
ret = recvTCP(client->socket,&temp,sizeof(int));
temp=(int)ntohl(temp);
client->last=(client->last)|temp;
if(ret<0) return -1; //c'è stata una chiusura della connessione già gestita dalla recvTCP
if (ret==0)
{
client->length=client->last;
client->last=0;
return 0;
}
client->last=(client->last)<<(8*(ret));
return ret;
}
p.s : nelle dispense da cui studio è scritto che anche la send può inviare meno dati di quelli previsti e i dati inviati vengono ritornati dalla funzione. Ora il dubbio è : seguendo quanto dice la dispensa dovrei fare un buffer anche di quella Sotto TCP se la send() mi ritorna un valore < di quello previsto posso direttamente gestire la chiusura di connessione in quanto c'è stato un errore di connessione ? e sotto UDP ? Ultima modifica di aeroxr1 : 07-07-2014 alle 15:00. |
|
|
|
|
|
#51 | |
|
Member
Iscritto dal: Nov 2012
Messaggi: 126
|
Quote:
|
|
|
|
|
|
|
#52 | |
|
Member
Iscritto dal: Jul 2011
Messaggi: 246
|
Quote:
- la lunghezza non va inviata come stringa altrimenti diventa essa stessa variabile. Ad esempio se la lunghezza è 10 io invio la stringa "10" che è lunga due bytes mentre se la lunghezza è 100, invio la stringa "100" che è lunga tre bytes. Fare in modo che questa STRINGA abbia una lunghezza fissa mi sembra un lavoro inutile; - fare gli shift e gli OR che dite voi (inviando byte per byte) è anche peggio in quanto uno non può semplicemente leggere la memoria byte per byte e inviarne il contenuto perchè poi il ricevente, appena prova a interpretare quei byte come intero, non sa come fare a causa dell'endianess (e il risultato può essere errato). Un modo possibile (che eviti questi problemi) l'ho già scritto qualche post fa... In aggiunta a quel codice, si può dire che uno può aggiungere un controllo per verificare che recv riceva il numero esatto di bytes (cioè 4 per un uint32). Se l'altro end-point è implementato bene questa condizione deve essere rispettata (l'intero deve essere inviato con una singola chiamata send chiaramente). Altrimenti io chiuderei pure la socket perchè se c'è un protocollo e una delle parti non lo rispetta allora puoi benissimo chiudergli la porta in faccia.
__________________
Non c'è cosa peggiore nella vita di un programmatore di un errore che si presenta solo ogni tanto. CONCLUSO POSITIVAMENTE CON: oldfield |
|
|
|
|
|
|
#53 | ||
|
Senior Member
Iscritto dal: Mar 2006
Messaggi: 2056
|
Quote:
Ma in questa maniera quale sarebbe l'errore ? Codice:
int riceviLength() //creato una funzione separata per la futura gestione del buffer della lunghezza
{
//in realtà inviando un int per la dimensione invio ben 4byte, devo gestire un buffer per la lunghezza
int ret;
int temp; //ci viene salvato il dato letto
ret = recvTCP(client->socket,&temp,sizeof(int));
temp=(int)ntohl(temp);
client->last=(client->last)|temp;
if(ret<0) return -1; //c'è stata una chiusura della connessione già gestita dalla recvTCP
if (ret==0)
{
client->length=client->last;
client->last=0;
return 0;
}
client->last=(client->last)<<(8*(ret));
return ret;
}
Te lo chiedo giusto per capire dove sbaglio ho ritrovato il codice che hai consigliato te: Quote:
La recv non è detto riceva tutto in una volta sola giusto ? quindi ci sta benissimo che la length che sta su 4byte viene si inviata intera, ma che alla recv arrivi solo il primo dei 4 byte no ?? In quel caso non devo aspettare mi arrivino gli altri 3 byte ? Oppure non c'è possibilità che venga spezzettato un int ? Ultima modifica di aeroxr1 : 07-07-2014 alle 15:43. |
||
|
|
|
|
|
#54 | ||
|
Senior Member
Iscritto dal: Jan 2014
Messaggi: 852
|
Quote:
Quote:
|
||
|
|
|
|
|
#55 |
|
Senior Member
Iscritto dal: Mar 2006
Messaggi: 2056
|
@Mettiu_ ho modificato la risposta al tuo post
@Daniels118 quindi se gli shift agiscono sul valore indipendentemente dall'endianess del sistema è inutile il ntohl che ho applicato al mio int prima di fare la shift ? |
|
|
|
|
|
#56 | |
|
Member
Iscritto dal: Nov 2012
Messaggi: 126
|
Quote:
Il problema dello spedire su socket intere struct si risolve semplicemente non facendolo visto che è sempre e comunque una cattiva idea. L' "equivalenza" tra array e puntatori non esiste - non sono la stessa cosa. Punto. Sono intercambiabili in alcune circostanze, che è molto diverso. Possibile sia così difficile da capire? Di "char message[0];" già detto. Ultima modifica di van9 : 07-07-2014 alle 15:56. |
|
|
|
|
|
|
#57 |
|
Senior Member
Iscritto dal: Jan 2014
Messaggi: 852
|
@van9
Credo che tu ti stia rivolgendo alla persona sbagliata, non sono io a voler trasmettere la struct sul socket. |
|
|
|
|
|
#58 |
|
Senior Member
Iscritto dal: Jan 2014
Messaggi: 852
|
Si, se usi gli shift puoi fare a meno di ntohl.
|
|
|
|
|
|
#59 |
|
Member
Iscritto dal: Jul 2011
Messaggi: 246
|
Non c'è ragione per cui l'int possa essere spezzettato se il trasmettitore fà una sola send().
Vorrei ribadire la regola generale: se il tuo dato copre più bytes (come un uint32, che occupa 4 bytes) allora hai il problema dell'endianess e devi gestire la situazione tramite ntohl et similia. Se il tuo dato occupa solo un byte (come nel caso dei char) non c'è il problema dell'endianess perchè il modo di interpretare quel dato in memoria non cambia su piattaforme con diverso endianess. Per cui per la mia soluzione, valgono le mie considerazioni (devi usare htonl). Per la soluzione di Daniels, valgono le sue. La differenza è dovuta al fatto che lui suggerisce di inviare char, mentre io suggerisco di inviare un uint32.
__________________
Non c'è cosa peggiore nella vita di un programmatore di un errore che si presenta solo ogni tanto. CONCLUSO POSITIVAMENTE CON: oldfield |
|
|
|
|
|
#60 | |
|
Member
Iscritto dal: Nov 2012
Messaggi: 126
|
Quote:
edit: no aspè mi sto imbrogliando. L'ho capito che tu non sei per la trasmissione diretta. Quello che non ho chiarito è che, utilizzando i flexible members di C99 è vero che si perde la possibilità di copia diretta tra due struct tramite operatore d'assegnamento (non perché si copi un pointer come erroneamente pensi tu ma perché viene copiato un incomplete array). Ma la cosa non ci tange comunque visto che un invio di dati binari su socket come in questo caso e non serializzato a dovere può solo portare problemi, a cominciare dal padding nelle strutture e via dicendo. Ultima modifica di van9 : 07-07-2014 alle 16:51. |
|
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 20:34.




















