2010-02-10 13 views
8

Ho un problema nel capire cosa recv()/recvfrom() restituisce da un socket UDP non-blockig.programmazione del socket udp non bloccante in C: cosa ottengo?

Un po 'più specifico e rispetto al protocollo TCP (per favore correggetemi se sbaglio):

  • Un socket di blocco (TCP o UDP) non sarà tornare da un recv() fino a quando non sono alcuni dati nel buffer. Questo potrebbe essere un numero di byte (TCP) o un datagramma completo (UDP).

  • Un socket TCP non bloccante restituisce EWOULDBLOCK (linux)/WSAEWOULDBLOCK (Windows) oi byte attualmente presenti nel buffer. Dato che i dati TCP sono uno stream, non importa quanti byte vengono restituiti.

Ora la domanda:

  • un socket UDP non bloccante restituisce anche WOULDBLOCK (linux)/WSAEWOULDBLOCK (Windows) Se non ci sono dati disponibili. Ma se ci sono dati disponibili, un socket UDP non bloccante restituisce solo alcuni byte, il che significa che si ottiene solo metà di un datagramma O un socket UDP restituisce sempre datagrammi completi ??

Edit:

Quello che voglio dire con "la metà di un datagram" è: cosa succede se io chiamo recv() in un solo momento in cui la presa sta ricevendo un datagramma. In quel momento ci sono alcuni byte nel buffer ma il datagramma non è ancora completo.

Le vostre spiegazioni e commenti sono apprezzati. Grazie!

risposta

8

Infine, una scusa per scavare i miei libri di Stevens dalle mie vecchie scatole da ufficio.

Se il buffer è sufficientemente grande, le funzioni standard di Berkeley socket recv() e recvfrom() non restituiranno mai un datagramma parziale. Il datagramma non è disponibile per l'applicazione fino a quando il kernel non ha completamente ricevuto e riassemblato il datagramma.

È interessante notare, e questo non è molto (qualsiasi?) Di un problema di oggi, le altre interfacce di programmazione di rete non sono d'accordo sul comportamento quando il buffer fornito è troppo piccolo:

La versione tradizionale di Berkeley dei socket API tronca il datagramma, scartando eventuali dati in eccesso. Se l'applicazione viene notificata dipende dalla versione. (4.3BSD Reno e versioni successive possono notificare all'applicazione che il datagramma è stato troncato)

L'API socket in SVR4 (incluso Solaris 2.x) non tronca il datagramma. Qualsiasi dato in eccesso viene restituito nelle letture successive. L'applicazione non viene informata che vengono eseguite più letture da un singolo datagramma UDP.

L'API TLI non elimina i dati. Invece viene restituito un flag che indica che sono disponibili più dati e le successive letture da parte dell'applicazione restituiscono il resto del datagramma.

(Stevens, TCP/IP Illustrated, Volume 1, p. 160)

+0

Sembra che sia possibile passare e ricevere un flag MSG_TRUNC su 'recvmsg' in Linux. Documentato nella manpage 'recv (2)'. In un'altra nota, forse sto fraintendendo, ma posso trovare solo il comportamento di scartare documentato nella manpage per 'socket (2)', che lo menziona solo per i socket 'SOCK_SEQPACKET'. Non li ho mai usati personalmente. –

+0

'MSG_TRUNC' come argomento per' recv (2) 'non è standard. Non è disponibile su FreeBSD o Mac OS X (i sistemi a cui ho accesso al momento, probabilmente vero per gli altri). 'MSG_TRUNC' è disponibile su Linux, FreeBSD e Mac OS X nel membro' flags' di 'struct msghdr' passato a' recvmsg (2) '. In ogni caso, il datagramma verrà troncato se il buffer passato non è abbastanza grande, anche con 'recv (2)' su Linux. Il chiamante deve controllare il valore di ritorno e confrontarlo con le dimensioni del buffer se viene utilizzato 'MSG_TRUNC'. Saprà che i dati sono persi, ma è ancora perso. –

+0

Grazie! Ciò significa che UDP è ** veramente ** orientato ai pacchetti ... – Uwe

0

Credo che si ottengano esattamente uno o zero datagrammi. Ma non posso confermarlo in questo momento. Forse qualcun altro potrebbe fornire una buona referenza?

Edit: Sono abbastanza sicuro che non puoi ricevere mezzo datagramma. O il datagramma è arrivato nel buffer in o no.

+0

Credo che sia corretto. Puoi anche ricevere un errore se non si adatta al buffer che hai fornito, o se i tuoi mbuf non sono abbastanza grandi (a volte capita con datagrammi frammentati di grandi dimensioni). –

+0

mbufs sono una struttura dati del kernel BSD. Non sono esposti a terra degli utenti. –

1

Sì, UDP restituisce solo i dati trasmessi in quel datagramma. UDP non è orientato al flusso come TCP. I datagrammi sono trasmissioni discrete e non sono in alcun modo legati ad altri datagrammi. Questo è il motivo per cui l'opzione socket per TCP è SOCK_STREAM.

Il lato positivo di questo è che è possibile ottenere un senso di trasmissioni separate, il che non è davvero facile da fare con TCP.

+0

Grazie a tutti e due. Conosco la differenza tra TCP orientato al flusso (SOCK_STREAM) e UDP orientato ai pacchetti (SOCK_DGRAM). Non ero sicuro se un UDP recv() non bloccante sarebbe anche orientato ai pacchetti. Per citare la manpage recv(): ... Se non ci sono messaggi disponibili sul socket, le chiamate di ricezione attendono l'arrivo di un messaggio, a meno che il socket non sia bloccante (vedere fcntl (2)), nel qual caso viene restituito il valore -1 e la variabile esterna errno è impostata su EAGAIN o EWOULDBLOCK. ** Le chiamate di ricezione normalmente restituiscono tutti i dati disponibili, ** ... – Uwe

Problemi correlati