I semestre 02/03 Socket Avanzati Prof. Vincenzo Auletta auletta@dia.unisa.it http://www.dia.unisa.it/~auletta/ Università degli studi di Salerno Laurea e Diploma in Informatica 1 Timeout Abbiamo visto molte situazioni in cui è necessario fissare un su un operazione di I/O Per evitare che un applicazione si blocchi in attesa di dati che non arriveranno Ci sono tre tecniche per assegnare un ad un operazione sul socket funzione alarm() funzione select() opzioni del socket SO_RCVTIMEO e SO_SNDTIMEO Funzione alarm() Client echo UDP con alarm 2 #include <netdb.h> int alarm(unsigned int nsec); Genera un segnale SIGALRM l applicazione deve registrare un gestore per questo segnale il kernel interrompe l esecuzione della system call e chiama il gestore del segnale Il gestore restituisce il controllo all applicazione Controindicazioni L implementazione della gestione dei segnali non è standard La gestione di SIGALRM è unica in tutto il processo 3 socklen_t servlen, int ) { (1) if( signal(sigalrm, sig_) ) err_sys("errore in signal"); (2) n = strlen(sendline); if( sendto(sockd, sendline, n, 0, p_servaddr, servlen)!= n ) err_sys("errore nella sendto"); alarm(); (3) if( (n = recvfrom(sockd, recvline, MAXLINE, 0, NULL, NULL)) < 0 ) if( errno == EINTR ) err_msg("socket "); (4) else err_sys("errore in recvfrom"); alarm(0); (5) recvline[n] = 0; if( fputs(recvline, stdout) == EOF ) err_sys("errore nella fputs"); 2. Registra il gestore di SIG_ALRM Il gestore non fa nulla 3. Attiva l alarm prima di leggere 4. Se recvfrom è interrotta allora èscaduto il 5. Disattiva l alarm dopo aver letto
4 Funzione select con La select() prende un argomento di tipo timeval Permette di registrare i numeri di secondi e micro Il si applica a tutti i descrittori controllati dalla select La select() restituisce 0 se il è scaduto e non c è nessun descrittore pronto < 0 se c è stato un errore > 0 se ci sono descrittori pronti e la struttura timeval contiene il tempo rimanente 5 Client echo UDP con select() socklen_t servlen, int ) { (1) fd_set rset; struct timeval tv; /* scrive sul socket il contenuto di sendline */ FD_ZERO(&rset); FD_SET(sockd, &rset); (2) tv.tv_sec = ; (3) tv.tv_usec = 0; if( (n = select(sockd + 1, &rset, NULL, NULL, &tv)) < 0 ) (4) err_sys("errore in select"); if( n == 0 ) err_msg("socket "); (5) else /* legge dal socket e stampa su stdout */ 2. Registra il socket per la lettura 3. Setta il 4. Invoca la select 5. Se la select esce con 0 è scaduto il 6 Opzione SO_RCVTIMEO L opzione SO_RCVTIMEO fissa il sulle operazioni di lettura da un descrittore di socket Vale per tutte le operazioni di lettura da quel descrittore Utilizzabile solo per descrittori di socket Per le operazioni di scrittura si usa l opzione SO_SNDTIMEO 7 Client echo UDP con SO_RCVTIMEO socklen_t servlen, int ) { (1) struct timeval tv; tv.tv_sec = ; tv.tv_usec = 0; (2) if( (setsockopt(sockd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) ) err_sys("errore in setsockopt"); (3) /* scrive sul socket il contenuto di sendline */ n = recvfrom(sockd, recvline, MAXLINE, 0, NULL, NULL); if( n < 0 ) { if( errno == EWOULDBLOCK ) { (4) err_msg("socket "); continue; else err_sys("errore in recvfrom"); /* legge dal socket e stampa su stdout */ 2. fissa il valore del 3. Setta l opzione del socket 4. Se la recvfrom esce con EWOULDBLOCK èscaduto il
8 Funzioni recv() e send() ssize_t recv(int sockd, void* buff, size_t n, int flags); ssize_t send(int sockd const void* buff, size_t n, int flags); Numero di byte letti o scritti se OK Sono analoghe a read e write ma hanno un argomento in più (flags) Analogo al quarto argomento di sendto e recvfrom Definisce il comportamento della system call La variabile flags può essere usata soltanto per passare informazioni dal processo al kernel 9 Flag La variabile flags può contenere l OR logico di alcuni di questi valori MSG_DONTROUTE MSG_DONTWAIT MSG_OOB MSG_PEEK MSG_WAITALL Non legge la tabella di routing prima di spedire Operazione di lettura o scrittura eseguita in modalità non blocking Legge o invia dati fuori banda Legge i dati dal socket senza estrarli dal buffer Aspetta che tutti i dati siano pronti prima di leggere (come readn) Funzioni readv() e writev() Struttura iovec 10 #include <sys/uio.h> ssize_t readv(int fd, const struct iovec* iov, int iovcount); ssize_t writev(int fd, const struct iovec* iov, int iovcount); Numero di byte letti o scritti se OK Sono analoghe a read e write ma consentono di operare su più buffer contemporaneamente scattering (distribuzione) e gathering (raccolta) Utilizzate per evitare di inviare tanti piccoli pacchetti Con socket UDP una writev crea un unico datagram iov è un array di strutture iovec di lunghezza al più IOV_MAX Ogni struttura specifica un buffer 11 struct iovec { void* ; /* indirizzo di partenza del buffer */ int iov_len; /* lunghezza del buffer */ ; Es. una readv legge 180 caratteri
12 Funzioni recvmsg() e sendmsg() ssize_t recvmsg(int sockd, struct * msg, int flags); ssize_t sendmsg(int sockd, struct * msg, int flags); numero di byte letti o scritti se OK Sono le funzioni di I/O più generali Tutte le altre funzioni di I/O sono casi particolari di queste due Supportano Flags Lettura e scrittura da più buffer Specifica dell indirizzo Passaggio di dati ausiliari (ancillary data) Struttura struct { void* msg_name; /* indirizzo del socket */ socklen_t msg_namelen; /* lunghezza dell indirizzo */ struct iovec* msg_iov; /* array di buffer */ size_t msg_iovlen; /* lunghezza di msg_iov */ void* msg_control; /* dati ausiliari */ socklen_t msg_controllen; /* lunghezza di msg_control */ int msg_flags; /* flag restituiti da recvmsg() */ ; L argomento flags è usato solo da sendmsg() per passare flags al kernel Stessi valori di recv() msg_flags è usato solo da recvmsg() per contenere i flag ricevuti dal kernel 13 Restituisce anche MSG_BCAST, MSG_MCAST, MSG_TRUNC (messaggio troncato), MSG_CTRUNC (dati ausiliari troncati) 14 Dati Ausiliari I dati ausiliari possono essere passati o ricevuti solo utilizzando sendmsg() e recvmsg() Usati per passare informazioni da un protocollo ad un altro Es. opzioni IP_RECVSTDADDR, IP_RECVIF Passare informazioni dal processo figlio al processo padre o tra processi indipendenti (socket UNIX) Il campo msg_control è formattato come un insieme di oggetti di dati (ancillary data object) Oggetti distinti possono avere differenti lunghezze Per manipolare gli oggetti si usano macro ad hoc Ogni oggetto inizia con una struct c 15 Struttura c struct { socklen_t cmsg_len; /* lunghezza di un blocco */ struct iovec* cmsg_level; /* protocollo che ha prodotto i dati */ size_t cmsg_type; /* specifico del protocollo */ unsigned char cmsg_data[]; ; I dati devono essere allineati con la struct Ci possono essere caratteri di riempimento tra cmsg_type e cmsg_data
16 Operazioni su Dati Ausiliari struct c* CMSG_FIRSTHDR(struct * mhdrptr); /* restituisce il puntatore al primo blocco o NULL */ struct c* CMSG_NXTHDR(struct * mhdrptr, struct c* cmsgptr); /* restituisce il puntatore al prossimo blocco o NULL */ unsigned char* CMSG_DATA(struct c* cmsgptr); /* restituisce un puntatore al primo byte di cmsg_data */ unsigned int CMSG_LEN(unsigned int length); /* restituisce il valore di cmsg_len */ unsigned int CMSG_SPACE(unsigned int length); /* restituisce la dimensione complessiva di un oggetto */ 17 Esempio Chiama recvmsg() su un socket UDP con opzione IP_RECVSTDADDR settata per ricevere un datagram di 170 byte inviato da 198.69.10.2:2000 all indirizzo 192.41.218.225 msg_name msg_namelen 16 msg_iov msg_iovlen 3 msg_control msg_controllen20 msg_flags 0 iovec Esempio 18 Contenuto della struttura restituita da recvmsg() msg_name msg_namelen 16 msg_iov msg_iovlen 3 msg_control msg_controllen16 msg_flags 0 sockaddr_in 16, AF_INET, 2000 198.69.10.2 c cmsg_len cmsg_level cmsg_type iovec 16 IPPROTO_IP IP_RECVSTDADDR 192.41.218.225