[RISOLTO] C sockets e trasmissioni dati incompleta

di il
14 risposte

[RISOLTO] C sockets e trasmissioni dati incompleta

Salve a tutti!
Ho provato a googolare ma non ho avuto risultati soddisfacienti.

Vi illustro il mio problema:
Un mio programma deve comunicare ad un altro mio programma. Ho quindi definito un socket stream sul primo che accetta la connessione, ed un socket stream sul secondo che la effettua.
Il problema è che, quando vado ad inviare i dati, se questi superano una certa dimensione (ad esempio):
char output[100000];
send(clientsocket, output,sizeof(output),0);
E con l'altro li ricevo:
char buffer[101000];
recv(remoteSocket, buffer, sizeof(buffer), 0);
I dati non vengono trasmessi completamente o meglio: il mio programma mi chiede di inserire un comando, inserisco il comando e viene inviato all'altro programma, questo lo esegue e mi rimanda l'output, il primo dovrebbe riceverlo e printarlo ma invece ne riceve e printa solo una parte, e mi richiede l'input. A questo punto qualunque comando io inserisca il risultato restituitomi è la prosecuzione della parte mancante di output, rimanendo però della stessa dimensione. Ossia se:
n = 3 (ma potrebbe essere qualsiasi valore)
input1 con comando;
elabora comando ed ottiene output;
invia 1/n di output;
input2 con o senza comando;
invia 1/n di output successivo a quello precedente;
input3 con o senza comando;
invia 1/n di output successivo a quello precedente;
E a questo punto è completa la trasmissione dati.



So di aver scritto in maniera un po' confusa ma meglio non riesco ad esporlo.
Sostanzialmente:
Vi è un modo per assicurarsi di inviare e ricevere tutti i dati?


Grazie dell'attenzione, confido in voi

14 Risposte

  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    I dati non vengono trasmessi completamente o meglio: il mio programma mi chiede di inserire un comando, inserisco il comando e viene inviato all'altro programma, questo lo esegue e mi rimanda l'output, il primo dovrebbe riceverlo e printarlo ma invece ne riceve e printa solo una parte, e mi richiede l'input. A questo punto qualunque comando io inserisca il risultato restituitomi è la prosecuzione della parte mancante di output, rimanendo però della stessa dimensione.
    Ciao Ananke Melior,
    ma alla fine di tutto questo gran casino... qual'è il punto?
    Non riesci a gestire un buffer di dimensione superiore al dimensionamento?
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Il punto è che finche il buffer, e conseguentemente i dati in esso memorizzati rimangono sotto una certa soglia, ad esempio nel programma che invia:
    char messaggio[9500];
    E nel programma che riceve:
    char buffer[10000];
    Con il comando:
    send(remoteSocket,messaggio,sizeof(messaggio),0);
    ed il conseguente comando per riceverli:
    recv(remoteSocket, buffer, sizeof(buffer), 0);
    il traffico arriva completo.
    Altrimenti ne arriva solo un pezzo (se il buffer supera circa i 9500 di dimensione).
    Se creo un ciclo che riceva ed invii più di una volta, ad esempio richedendo un input per continuare, i dati rimanenti arrivano un po' alla volta e sempr enella stessa quantità direi.

    Io sapevo che i sockets STREAM si occupassero di completare tuta la trasmissione e la ricezione. Come posso fare affinche questo avvenga?

    Grazie della pazienza ma lo trovo complicato da spiegare (probabilmente perchè sono alle prime armi con il C)
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Caio Ananke Melior ,

    il giro della ricezione è semplicemente:
    
      while ((block_len=recv(client_sock, block, sizeof(block), 0))>0)
      {
        ...fai qualcosa...
      }
    
    Attenzione: se block è da trattare come stringa, riservati un carattere per il terminatore. Itera per:
    
      while (...blabla.., sizeof(block)-1, 0))>0) <---- meno 1
      {
        block[block_len]='\0'; 
        ...fai qualcosa di stringoso...
      }
    
    A mio avviso, per esperienza personale, ti consiglio una dimensione block che varia da 1 a 4K; potresti anche tenerla nello STACK oppure definirla STATICA.

    Per la trasmissione, la logica potrebbe essere:
    1) stessa logica della ricezione
    2) trasmetto dati esclusivamente di piccola entità e decido di allocare una dimensione massima. Non accetto dimensioni superiori.
    3) alloco dinamicamente, in base alla grandezza, fino ad esaurimento della memoria e libero in chiusura il blocco trattato

    a tua scelta, in base a quello che devi fare.
    Vi è un modo per assicurarsi di inviare e ricevere tutti i dati?
    La trasmissione è verificata a livello di protocollo.
    Se vuoi una conferma ufficiale puoi usare un algoritmo di hashing tipo MD5
    Io sapevo che i sockets STREAM si occupassero di completare tuta la trasmissione e la ricezione. Come posso fare affinche questo avvenga?
    L'unica tua preoccupazione è verificare che non avvengano errori durante le transazioni.
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Ciao Ixamit,
    grazie dell'esauriente risposta!
    Purtroppo però non ho capito bene:
    while ((block_len=recv(client_sock, block, sizeof(block)-1, 0))>0)
    	{
    		block[block_len]='\0';
                    ...fai qualcosa di stringoso...
    	}
    In questo codice, block_len come devo definirlo?
    Se inseriscp il codice per il caratetre come nell'esempio, non viene calcolata ogni ricezione come stringa quando a me servirebbe come stringa tutta la somma dei pacchetti?
    E "fai qualcosa di stringoso" se lo pongo all'interno del ciclo viene effettuato più volte, ma se io volessi solo ricevere i dati e immagazzinarli in block, posso smeplicmente non scrivere niente e quelli vengono sommati o devo creare un altra variabile in cui mettere i dati con strcat?

    Scusa per la mia inesperienza,
    grazie ancora
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Riciao,
    In questo codice, block_len come devo definirlo?
    
    ssize_t block_len 
    
    Se inseriscp il codice per il caratetre come nell'esempio, non viene calcolata ogni ricezione come stringa quando a me servirebbe come stringa tutta la somma dei pacchetti?
    Se definisci un blocco da 1K non ti basta per inviare una stringa? E se hai n stringhe basta ciclare riempendo lo stesso blocco.Prendiamo ad esempio una richiesta HTTP:
    
    GET /dammistofilechehobisognora HTTP/1.1\r\n
    Host: xxx.yyy.zz\r\n
    User-Agent: MyBrowser 1.2.3\r\n
    Accept: image/png,image/*;q=0.8,*/*;q=0.5\r\n
    Accept-Language: zz-ww,kk;q=0.9\r\n
    Accept-Encoding: gzip,deflate\r\n
    Keep-Alive: 300\r\n
    ...
    \r\n
    \r\n
    
    Sempre prendendo come esempio HTTP, in un rigo della risposta del server troviamo la dimensione del file che riceveremo. Non è l'unico modo per HTTP ma per il nostro esempio prendila così.
    
    HTTP/1.1 200 OK\r\n
    Date: Wed, 31 Dec 2025 16:26:24 GMT\r\n
    Server: Apache1.2.3.4\r\n
    X-Powered-By: PHP/9.8.7\r\n
    ...
    Content-Length: 12345678
    Content-Type: unknow type
    ...
    
    Il client legge le singole righe di risposta e se richiesto precedentemente, si prepara a ricevere il file della grandezza size_file definita nella riga Content-Length
    
    // Att.ne: non controllato
    while (size_file)
    {
      while ((block_len=recv(client_sock, block, sizeof(block), 0))>0)
      {
        if ((fwrite(block, block_len, 1, stream_out))!=1)
        {
          perror ("fwrite");
          break;
        }
        size_file-=block_len;
      }
      if (block_len<=0)
        perror ("recv");
    }
    
    ecc.. ecc..
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Mio caro ixamit, purtroppo, non riesco ancora a farlo funzionare.
    Ti mostro come ho provato così eventualmente magari è sufficente trovare un errore cosa assai più rapida di lunghe spiegazioni:
    Invio:
    ssize_t block_len;
    char block[4000];
    char output[1000000];
    while ((block_len=send(clientsocket, output, sizeof(output), 0))>0) {
    	recv(clientsocket, buffer, sizeof(buffer), 0); }
    Ricezione:
    ssize_t block_len;
    char block[4000];
    char buffer[1000010];
    while ((block_len=recv(remoteSocket, block, sizeof(block), 0))>0) {
            send(remoteSocket,"ok",sizeof("ok"),0);
    	strcat(buffer, block);
    	memset(&block, 0, sizeof(block));}
    printf("%s", buffer);
    Il programma rimane in attesa senza stampare ne chiedere un nuovo input (nel caso ricevesse dati = a NULL).

    Ho provato anche:
    Invio v2:
    ssize_t messaggio_len;
    char buffer[1000000];
    messaggio_len = sizeof(output);
    send(clientsocket, messaggio_len,sizeof(messaggio_len),0);
    send(clientsocket, output,sizeof(output),0);
    Ricezione v2:
    ssize_t block_len;
    char buffer[1000010];
    recv(remoteSocket, block_len,sizeof(block_len), 0);
    while (block_len > sizeof(buffer)){
    recv(remoteSocket, buffer, sizeof(buffer), 0);}

    Sapresti indicarmi dove sbaglio?
    Grazie ancora del prezioso aiuto
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Ciao Ananke Melior,

    Sconsiglio di usare allocazioni maggiori di qualche Kb sullo STACK. Dovresti usare allocazioni dinamiche.
    Negli spezzoni di codice che riporti vi sono errori soprattutto logici. Ti ho scritto un pezzo chiarificatore per trattare le stringhe, appositamente sottodimensionato a 8byte (8 caratteri) giusto per ritornare sui discorsi precedenti.
    
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    #define BLOCK_SIZE 8
    #define FAMILY AF_INET
    #define PORT 7777
    
    int main ()
    {
      int sock_in,client_sock;
      struct sockaddr_in addr_in;
      ssize_t len;
      char block[BLOCK_SIZE];
      
      sock_in=socket(AF_INET,SOCK_STREAM,0);
      
      addr_in.sin_family=FAMILY;
      addr_in.sin_addr.s_addr=INADDR_ANY;
      addr_in.sin_port=htons(PORT);
      
      if ((bind(sock_in,(struct sockaddr*) &addr_in,sizeof(addr_in)))<0)
        perror ("bind");
      
      if ((listen(sock_in,1))<0)
        perror ("listen");
      
      printf ("Waiting connection....\n");
      if ((client_sock=accept(sock_in,0,0))<0)
        perror ("accept");
      
      printf ("done\n");
      while ((len=recv(client_sock, block, sizeof(block)-1, 0))>0)
      {
        block[len]='\0';
        printf ("%s",block);
        if (strcmp(block,"EXIT\n")==0) 
          break;
      }
      
      if (len<0)
        perror ("recv");
      else if  (len==0)
        printf ("connection closed by peer. Bye!\n");
      else
        printf ("connection closed\n");
      
      close (client_sock);
      close (sock_in);
      
      
      return 0;
    }
    
    Per mostrarti l'esecuzione mi appoggio a netcat ma puoi usare tranquillamente telnet sulla porta 7777
    
    max@studio:~> gcc prova.c -Wall
    max@studio:~> ./a.out &
    [1] 2902
    Waiting connection....
    max@studio:~> nc localhost 7777
    done
    HELLO
    HELLO
    Un giorno credi di esser giusto e di essere un grande uomo
    Un giorno credi di esser giusto e di essere un grande uomo
    EXIT
    EXIT
    connection closed
    [1]+  Done                    ./a.out
    max@studio:~> 
    
    La stringa lunga ("Un giorno...") viene spezzata ed appare come l'hai scritta

    Per i FILE il discorso è identico solo che non hai bisogno di mettere il carattere terminatore nel blocco.
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Questa è sicuramente la chiaccherata più istruttiva che mi è capitata da molto tempo.
    Ti ringrazio per l'infinita pazienza e l'altruismo.

    Adesso il processo di ricezione mi è chiaro e limpido.
    Tuttavia mi rimane oscuro il processo di invio. Devo riprodurre il medesimo ciclo o devo modificarlo in qualche modo?

    Grazie ancora
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Ciao Ananke Melior,

    si, in trasmissione puoi usare la stessa tecnica, ma dipende sempre da quello che devi trasmettere. Se abbiamo una stringa, la send la spedisce interamente. Se abbiamo un file, leggiamo il block e lo spediamo fino a fine file (iteriamo fino a FF).

    Qualora avessimo un input della stringa, il buffer di tastiera ti viene d'aiuto.Osserva questo giro:
    
    ...
      for (;;)
      {   
        fgets (block, BLOCK_SIZE, stdin);
        if ((len=send(sock, block,strlen(block),0))<1)
          break;
        if (strcmp(block,"EXIT\n")==0)
          break;
      }
    ...
    
    La fgets accetta al massimo BLOCK_SIZE caratteri, ma se sforiamo la dimensione provvede a leggere il resto dal buffer di tastiera e questo potrebbe essere un grande aiuto che il sistema ti offre gratuitamente.

    Per darti un esempio ti ho scritto una versione client. Non far caso a come vado a prendermi l'indirizzo di connessione... se vuoi approfondire leggi il manuale ($man 3p getaddrinfo).
    
    include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    
    #define BLOCK_SIZE 8
    #define FAMILY AF_INET
    #define PORT 7777
    
    /** get sockaddr, IPv4 or IPv6 */
    void *get_in_addr(struct sockaddr *sa)
    {
      if (sa->sa_family == AF_INET) 
        return &(((struct sockaddr_in*)sa)->sin_addr);
    
      return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }
    
    int main (int argc, char **argv)
    {
      int sock,x;
      struct addrinfo hints,*servinfo, *p;
      
      char host_addr[INET6_ADDRSTRLEN];
      char port[16];
    
      ssize_t len;
      char block[BLOCK_SIZE];
      
      
      if (argc!=2)
      {
        fprintf (stderr, "Usage:\t%s host\n",argv[0]);
        return -1;
      }
      
      snprintf (port,sizeof(port),"%d",PORT);
      
      memset(&hints, 0, sizeof (hints));
      hints.ai_family =FAMILY;
      hints.ai_socktype = SOCK_STREAM;
      if ((x=getaddrinfo(argv[1], port, &hints, &servinfo)) != 0) 
      {
        fprintf (stderr,"getaddrinfo: %s\n", gai_strerror(x));
        return -1;
      }
      for(p=servinfo;p;p=p->ai_next) 
      {
        if ((sock=socket(p->ai_family, p->ai_socktype, p->ai_protocol))<0) 
        {
          perror("Warning: socket");
          continue;
        }
        
        inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), host_addr, sizeof (host_addr));
        printf ("try connecting to %s port %s...\n",host_addr,port);   
        if ((connect(sock, p->ai_addr, p->ai_addrlen))<0)
        {
          perror ("Warning: connect");
          continue;
        }
        printf ("Good one. Done\n");
        break;
      }
      
      for (;;)
      {   
        fgets (block, BLOCK_SIZE, stdin);
        if ((len=send(sock, block,strlen(block),0))<1)
          break;
        if (strcmp(block,"EXIT\n")==0)
          break;
    
      }
      if (len<0)
        perror ("send");
      else if  (len==0)
        printf ("connection closed by peer. Bye!\n");
      else
        printf ("connection closed\n");    
      
      freeaddrinfo(servinfo);
      close (sock);
      
      return 0;
    }
    
    Uso a scopo dimostrativo sempre lo stesso block sottodimensionato sempre ad 8 caratteri (8 byte) appositamente per scopo didattico.
    Per l'esecuzione mi riappoggio a netcat, in modalità listen, sempre sulla porta 7777. Ora uso 2 terminali:

    term1
    
    max@studio:~> nc -l localhost -p 7777
    
    term2
    
    max@studio:~> gcc prova.c -Wall
    max@studio:~> valgrind --leak-check=full ./a.out localhost
    
    NB: decido di usare valgrind per verificare se vi sono errori per quello che andrò a fare

    term2
    
    max@studio:~> valgrind --leak-check=full ./a.out localhost 
    ==4896== Memcheck, a memory error detector
    ==4896== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
    ==4896== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
    ==4896== Command: ./a.out localhost
    ==4896== 
    try connecting to 127.0.0.1 port 7777...
    Good one. Done
    ... in un altro ti svegli e devi cominciare da zero
    EXIT
    connection closed
    ==4896== 
    ==4896== HEAP SUMMARY:
    ==4896==     in use at exit: 0 bytes in 0 blocks
    ==4896==   total heap usage: 10 allocs, 10 frees, 1,696 bytes allocated
    ==4896== 
    ==4896== All heap blocks were freed -- no leaks are possible
    ==4896== 
    ==4896== For counts of detected and suppressed errors, rerun with: -v
    ==4896== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
    max@studio:~> 
    
    term1
    
    max@studio:~> nc -l localhost -p 7777
    ... in un altro ti svegli e devi cominciare da zero
    EXIT
    max@studio:~>
    
    Come puoi osservare, valgrind non ci segnala nessuno sforamento! Eppure abbiamo scritto un input lungo su un buffer di più piccolo.

    Saluti,
    Max
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Riciao Max.

    Grazie alle tue pazienti spiegazioni ho finalmente compreso.
    Solo due cose non ho ben compreso:
    1 Prima hai detto:
    Uso a scopo dimostrativo sempre lo stesso block sottodimensionato sempre ad 8 caratteri (8 byte) appositamente per scopo didattico.
    Ma quindi quando mi trovo a dimensionarlo "operativamente", a quanto devo dimensionarlo?

    2 Se invece di inviare una stringa che leggo da uno stream che sia la tastiera o un file volessi inviare una stringa da me definita come posso modificare il ciclo?


    Grazie ancora, questo tuo aiuto per me è stato come luce nelle tenebre


    P.S. Ho dato un occhio al tuo sito. Di poesia non ne capisco tanto, ma per il resto gli articoli sono grandiosi!

    EDIT:
    Mi sono accorto adesso che il programma continua il ciclo di ricezione all'infinito, e anche quello di invio. Ma se io volessi:
    Sul client digito una riga di comandi, entro invio e questa viene inviata. A questo punto il ciclo termina e inizio un ciclo di ricezione.
    Stessa cosa ma al contrario sul server (ricezione, arrivati i comandi, esco dal ciclo di ricezione e faccio qualcosa, poi entro in quello di invio e terminato quello riparto dall'inizio).
    Come posso fare?
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Ti rispondo al volo in quanto sono in ritardo...
    Ma quindi quando mi trovo a dimensionarlo "operativamente", a quanto devo dimensionarlo?
    detto precedentemente un valore da 1 a 4K. Tieni presente quasi sempre un pacchetto, completo di header e compagnia bella, è 1,5K.
    2 Se invece di inviare una stringa che leggo da uno stream che sia la tastiera o un file volessi inviare una stringa da me definita come posso modificare il ciclo?
    usi la send sulla tua stringa per la lunghezza della stessa.
    P.S. Ho dato un occhio al tuo sito. Di poesia non ne capisco tanto, ma per il resto gli articoli sono grandiosi!
    Le poesie le scrive mia figlia ma dimentica di usare il suo login ed usa il mio...
    Mi sono accorto adesso che il programma continua il ciclo di ricezione all'infinito, e anche quello di invio. Ma se io volessi:
    Sul client digito una riga di comandi, entro invio e questa viene inviata. A questo punto il ciclo termina e inizio un ciclo di ricezione.
    Stessa cosa ma al contrario sul server (ricezione, arrivati i comandi, esco dal ciclo di ricezione e faccio qualcosa, poi entro in quello di invio e terminato quello riparto dall'inizio).
    Come posso fare?
    Usa la logica che vuoi! Puoi fare ping-pong oppure usare una sequenza che indica l'inversione tra ricezione e trasfert...

    Scappo,
    Ciao
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Capisco, quindi è un lavoro di squadra!

    Ho definito BLOCK_SIZE 1024, spero che sia corretto.
    Il problema adesso è che, per i primi invi e ricezioni i programmi funzionano, ma dopo un paio smettono di funzionare. Non riesco proprio a venirne a capo.
    Visto che è un progetto piccolo ma che dovrei pubblicare, e vorrei evitare di svelare in anticipo i sorgenti, non è che, se non ti è di disturbo eccessivo, ti posso inviare con un PM i sorgenti così puoi vedere, quando ne hai tempo, con i tuoi occhi dove sbaglio?

    Grazie,
    a presto
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Ho definito BLOCK_SIZE 1024, spero che sia corretto.
    Le grandezze non sono prestabilite, sono consigliate ma potrebbero benissimo variare (anche molto) secondo il reale utilizzo. Parecchi anni fa ho scritto un software (mai pubblicato) per trasferire pacchetti voice. I pacchetti dovevano esser spediti/rivevuti, a seconda del protocollo, con tempi e dimensioni ben precisi. La grandezza di un pacchetto era meno di mezzoK per un sample rate o frequenza dai 10 ai 20ms.
    Visto che è un progetto piccolo ma che dovrei pubblicare, e vorrei evitare di svelare in anticipo i sorgenti, non è che, se non ti è di disturbo eccessivo, ti posso inviare con un PM i sorgenti così puoi vedere, quando ne hai tempo, con i tuoi occhi dove sbaglio?
    Sinceramente,conoscendomi, non te lo guarderei mai. In buona fede potrei dirti: "ok, va bene" ma ho già una marea di roba da guardare che ha priorità assoluta. Mi spiace, la risposta è no.

    Ciao,
    Max
  • Re: [RISOLTO] C sockets e trasmissioni dati incompleta

    Ok, capisco, grazie lo stesso per tutto il prezioso aiuto.

    Il problema che mi rimane, verificandosi circa al quarto o quinto giro del ciclo, non penso sia dovuto ad un problema di trasmissione dati. Posso quindi concludere questo topic.

    Grazie mille Max, poche volte ho trovato persone così disponibili.

    P.S. Continua con gli articoli sul sito che sono molto interessanti e non mi dispiacerebbe poterne leggere di nuovi.
Devi accedere o registrarti per scrivere nel forum
14 risposte