Domanda

Sono un po' confuso riguardo alla programmazione socket in C.

Crei un socket, lo colleghi a un'interfaccia e a un indirizzo IP e lo fai ascoltare.Ho trovato un paio di risorse web a riguardo e l'ho capito bene.In particolare, ho trovato un articolo Programmazione di rete su sistemi Unix essere molto informativo.

Ciò che mi confonde è la tempistica con cui i dati arrivano sul socket.

Come puoi sapere quando arrivano i pacchetti e quanto è grande il pacchetto, devi fare tutto il lavoro pesante da solo?

Il mio presupposto di base qui è che i pacchetti possono essere di lunghezza variabile, quindi una volta che i dati binari iniziano ad apparire nel socket, come inizi a costruire pacchetti da quelli?

È stato utile?

Soluzione

La risposta breve è che devi fare tutto il lavoro pesante da solo.Puoi ricevere una notifica che ci sono dati disponibili per la lettura, ma non saprai quanti byte sono disponibili.Nella maggior parte dei protocolli IP che utilizzano pacchetti di lunghezza variabile, al pacchetto sarà anteposta un'intestazione con una lunghezza fissa nota.Questa intestazione conterrà la lunghezza del pacchetto.Leggi l'intestazione, ottieni la lunghezza del pacchetto, quindi leggi il pacchetto.Si ripete questo schema (leggere l'intestazione, quindi leggere il pacchetto) fino al completamento della comunicazione.

Quando si leggono i dati da un socket, si richiede un certo numero di byte.La chiamata di lettura può bloccarsi finché non viene letto il numero di byte richiesto, ma può restituire meno byte di quelli richiesti.Quando ciò accade, riprova semplicemente la lettura, richiedendo i byte rimanenti.

Ecco una tipica funzione C per leggere un determinato numero di byte da un socket:

/* buffer points to memory block that is bigger than the number of bytes to be read */
/* socket is open socket that is connected to a sender */
/* bytesToRead is the number of bytes expected from the sender */
/* bytesRead is a pointer to a integer variable that will hold the number of bytes */
/*           actually received from the sender. */
/* The function returns either the number of bytes read, */
/*                             0 if the socket was closed by the sender, and */
/*                            -1 if an error occurred while reading from the socket */
int readBytes(int socket, char *buffer, int bytesToRead, int *bytesRead)
{
    *bytesRead = 0;
    while(*bytesRead < bytesToRead)
    {
        int ret = read(socket, buffer + *bytesRead, bytesToRead - *bytesRead);
        if(ret <= 0)
        {
           /* either connection was closed or an error occurred */
           return ret;
        }
        else
        {
           *bytesRead += ret;
        }
    }
    return *bytesRead;
}

Altri suggerimenti

Quindi, la risposta alla tua domanda dipende in gran parte dal fatto che tu stia utilizzando UDP o TCP come trasporto.

Per UDP, la vita diventa molto più semplice, in quanto puoi chiamare recv/recvfrom/recvmsg con la dimensione del pacchetto che ti serve (probabilmente invierai comunque pacchetti a lunghezza fissa dalla sorgente) e presupporre che se i dati sono disponibili , è presente in multipli delle dimensioni della lunghezza del pacchetto.(CIOÈ.Chiami recv* con la dimensione del pacchetto lato mittente e sei a posto.)

Per TCP, la vita diventa un po' più interessante - ai fini di questa spiegazione, presumo che tu sappia già come usare socket(), bind(), listen() e Accept() - quest'ultimo è il modo in cui ottieni il descrittore di file (FD) della connessione appena effettuata.

Ci sono due modi per eseguire l'I/O per un blocco del socket, in cui chiami read(fd, buf, N) e la lettura rimane lì e attende finché non hai letto N byte in buf - o non bloccante, in cui devi verificare (usando select() o poll()) se l'FD è leggibile, e POI eseguire la lettura().

Quando si ha a che fare con connessioni basate su TCP, il sistema operativo non presta attenzione alle dimensioni dei pacchetti, poiché è considerato un flusso continuo di dati, non blocchi separati delle dimensioni di un pacchetto.

Se la tua applicazione utilizza "pacchetti" (strutture dati compresse o decompresse che stai passando), dovresti essere in grado di chiamare read() con l'argomento size corretto e leggere un'intera struttura dati dal socket alla volta.L'unico avvertimento che devi affrontare è ricordare di ordinare correttamente i byte di tutti i dati che stai inviando, nel caso in cui il sistema di origine e quello di destinazione abbiano un byte endian diverso.Questo vale sia per UDP che per TCP.

Per quanto riguarda la programmazione dei socket *NIX, consiglio vivamente W."Programmazione di rete Unix, vol.1" (UNPv1) e "Programmazione avanzata in ambiente Unix" (APUE).Il primo è un tomo riguardante la programmazione basata sulla rete, indipendentemente dal trasporto, e il secondo è un buon libro di programmazione a tutto tondo poiché si applica alla programmazione basata su *NIX.Cercare inoltre "TCP/IP Illustrated", volumi 1 e 2.

Quando esegui una lettura sul socket, gli dici quanti byte massimi leggere, ma se non ne ha così tanti, te ne dà quanti ne ha.Sta a te progettare il protocollo in modo da sapere se hai un pacchetto parziale o meno.Ad esempio, in passato quando si inviavano dati binari a lunghezza variabile, all'inizio inserirei un int che indicava quanti byte aspettarsi.Farei una lettura richiedendo un numero di byte maggiore del pacchetto più grande possibile nel mio protocollo, quindi confronterei il primo int con il numero di byte che avevo ricevuto e lo elaborerei o proverei più letture finché non avrò avevo ricevuto il pacchetto completo, a seconda.

I socket funzionano a un livello superiore rispetto ai pacchetti grezzi: è come un file da cui puoi leggere/scrivere.Inoltre, quando provi a leggere da un socket, il sistema operativo bloccherà (metterà in attesa) il tuo processo finché non avrà i dati per soddisfare la richiesta.

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