Esempi di Client e Server

Documenti analoghi
Una semplice applicazione client/server 1

Interazione con il DNS Conversioni di Nomi ed Indirizzi

Socket. Nei sistemi operativi moderni i servizi disponibili in rete si basano principalmente sul modello client/server.

Acknowledgment: Prof Vincenzo Auletta, Università di Salerno. Approfondimento alla programmazione distribuita

Interazione (TCP) Client-Server con le socket

SERVER CLIENT. Struttura di un Applicazione UDP. Socket UDP. Parametri di sendto. Funzioni di Input/Output. Prof. Vincenzo Auletta

Socket. Nei sistemi operativi moderni i servizi disponibili in rete si basano principalmente sul modello client/server.

Elementi di programmazione con interfaccia Socket

INTERNET DOMAIN SOCKETS (Cap.59)

Server Iterativi. Server TCP Ricorsivi. Fork. Server Ricorsivi. un server iterativo gestisce una connessione alla volta. Prof.

Esercitazione di Lab. di Sistemi Operativi 1 a.a. 2011/ Comunicazione Tra Processi (IPC) Parte -

COMUNICAZIONE TRA PROCESSI REMOTI IN UNIX

getsockname() e getpeername() Formato dei dati - server Esempio getsockname() server (2)

L uso di Socket UDP. TCP vs. UDP UDP

request reply richiesta client processo di servizio processo server principale From - Valeria Cardellini, Corso Sist. Distr. A.A.

Inter-process communication: socket

Esempio 1: stampa locale di file remoto

Guida all' uso dei sockets nella programmazione in C

Esercitazione di Lab. di Sistemi Operativi 1 a.a. 2011/ Comunicazione Tra Processi (IPC)- - 1 Parte -

CORSO DI SISTEMI OPERATIVI A - ESERCITAZIONE 6

Reti (già Reti di Calcolatori )

Cenni di programmazione distribuita in C++ Mauro Piccolo

Progetto fine al superamento del corso di Sistemi Operativi. Http server proxy Http web monitor

Sistemi operativi Modulo II I semafori 2 Select

ESERCITAZIONE 2 RIPASSO. EX. 1 Un processo padre (parent) crea due processi figli (children) e attende la loro terminazione. Se, e solo se,...

IPC Inter Process Communication

Socket per TCP: Fondamenti

Applicazione Client-Server con Server Concorrente Specifiche

Creare una applicazione Winsock di base

Socket per TCP: Fondamenti

Socket TCP. seconda parte

unsigned long inet_addr(cp) char *cp;

Socket TCP. prima parte

Lab. di Sistemi Operativi - Esercitazione n 9- -Thread-

Creare un'elementare backdoor in C in ambiente UNIX

Esercitazione [6] Client/Server con Socket

DATAGRAM SOCKET. Angelastro Sergio Diomede Antonio Viterbo Tommaso

INGEGNERIA DEL WEB. VinX

Basi di network programming sotto Unix/Linux (draft version) Claudio Piciarelli

I Socket. Laboratorio Software M. Grotto R. Farina

Lab. di Sistemi Operativi - Esercitazione n 7- -Gestione dei processi Unix-

Laboratorio di. Reti Informatiche. Corso di Laurea Triennale in Ingegneria Informatica A.A. 2017/2018. Ing. Carlo Vallati

Laboratorio di Reti di Calcolatori

La sincronizzazione è legata alla implementazione delle pipe: int pipe(int fd[2]);

I/O su Socket TCP: read()

Guida di Beej alla Programmazione di Rete

Transcript:

Esempi di Client e Server Corso di laurea in Informatica Laboratorio di Reti di Calcolatori A.A. 0-0 Simone Bassis bassis@di.unimi.it #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #include <sys/wait.h> #include <signal.h> #define PORT "90" #define BACKLOG 0 void sigchld_handler(int s) while(waitpid(-, NULL, WNOHANG) > 0); Simple MultiProcess Stream Server // ottieni il sockaddr, IPv o 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(void) int sockfd, new_fd; struct addrinfo hints, *servinfo, *p; struct sockaddr_storage their_addr; socklen_t sin_size; struct sigaction sa; int yes=; char s[inet6_addrstrlen]; int rv; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; // usa il mio IP if ((rv = getaddrinfo(null, PORT, &hints, &servinfo))!= 0) fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return ; Invia la stringa Hallo World al client 76

Simple MultiProcess Stream Server // ciclo tra i risultati fino al primo bind corretto for(p = servinfo; p!= NULL; p = p->ai_next) if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -) perror("server: socket"); if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -) perror("setsockopt"); if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -) close(sockfd); perror("server: bind"); break; if (p == NULL) fprintf(stderr, "server: failed to bind\n"); return ; freeaddrinfo(servinfo); // un po di pulizia if (listen(sockfd, BACKLOG) == -) perror("listen"); // occupiamoci dei figli // evitando che l amministratore di sistema si agiti sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(sigchld, &sa, NULL) == -) perror("sigaction"); printf("server: waiting for connections...\n"); 77 Simple MultiProcess Stream Server // ciclo accept() principale while() sin_size = sizeof their_addr; new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); if (new_fd == -) perror("accept"); inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s); printf("server: got connection from %s\n", s); if (!fork()) // processo figlio close(sockfd); // il figlio non deve accettare connessioni if (send(new_fd, "Hello, world!",, 0) == -) perror("send"); close(new_fd); exit(0); close(new_fd); // al genitore non serve return 0; 5 Nota: tutto in un main per chiarezza espositiva: meglio suddividere il tutto in diverse funzioni 78

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #define PORT "90" Simple Stream Client // porta destinazione // lunghezza massima del buffer di lettura #define MAXDATASIZE 00 // ottieni sockaddr, IPv 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); Ancora più semplice del server int main(int argc, char *argv[]) int sockfd, numbytes; char buf[maxdatasize]; struct addrinfo hints, *servinfo, *p; int rv; char s[inet6_addrstrlen]; if (argc!= ) fprintf(stderr,"usage: client hostname\n"); memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ((rv = getaddrinfo(argv[], PORT, &hints, &servinfo))!= 0) fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return ; Nota: potreste anche testare il server con telnet: $ telnet hostname 90 79 Simple Stream Client // cicla tra I risultati fino alla prima connect corretta for(p = servinfo; p!= NULL; p = p->ai_next) if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -) perror("client: socket"); if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -) close(sockfd); perror("client: connect"); break; if (p == NULL) fprintf(stderr, "client: failed to connect\n"); return ; inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s); printf("client: connecting to %s\n", s); freeaddrinfo(servinfo); // un po di pulizia Nota: sappiamo le dimensioni della stringa inviata dal server e non ci preoccupiamo di controllare di averla letta tutta if ((numbytes = recv(sockfd, buf, MAXDATASIZE-, 0)) == -) perror("recv"); buf[numbytes] = '\0'; printf("client: received '%s'\n",buf); close(sockfd); return 0; 80

Chat Server I/O Multiplexing int main(void) fd_set master; fd_set read_fds; int fdmax; // master file descriptor list // temp file descriptor list per la select() // max file descriptor #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #define PORT "90" // listening port int listener; // listening socket descriptor int newfd; // data socket descriptor struct sockaddr_storage remoteaddr; // client address socklen_t addrlen; char buf[56]; // buffer per lo scambio dati int nbytes; char remoteip[inet6_addrstrlen]; int yes=; // per setsockopt() SO_REUSEADDR int i, j, rv; struct addrinfo hints, *ai, *p; // ottieni il sockaddr, IPv 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); FD_ZERO(&master); FD_ZERO(&read_fds); memset(&hints, 0, sizeof hints); // svuota master e temp sets // socket e bind hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if ((rv = getaddrinfo(null, PORT, &hints, &ai))!= 0) fprintf(stderr, "selectserver: %s\n", gai_strerror(rv)); Servono master e temp set perché la select() modifica il temp set mostrando quali fd sono pronti 505 Chat Server I/O Multiplexing for(p = ai; p!= NULL; p = p->ai_next) listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol); if (listener < 0) Ogni nuova connessione va aggiunta al master set Così come ogni chiusura necessita della rimozione dell fd dal master set // per gestire il messaggio d errore "address already in use" setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) close(listener); break; // arrivati a questo punto significa che qualcosa è andato storto if (p == NULL) fprintf(stderr, "selectserver: failed to bind\n"); exit(); freeaddrinfo(ai); // listen if (listen(listener, 0) == -) perror("listen"); exit(); // un po di pulizia // si aggiunge listener al set master FD_SET(listener, &master); // mantiene memoria del fd più grande fdmax = listener; // finora è questo 506

// main loop for(;;) Per questioni di portabilità meglio usare FD_COPY Chat Server I/O Multiplexing read_fds = master; // copia del master set if (select(fdmax+, &read_fds, NULL, NULL, NULL) == -) perror("select"); exit(); // si cicla per verificare quale fd è pronto per la lettura for(i = 0; i <= fdmax; i++) if (FD_ISSET(i, &read_fds)) // l i-esimo fd è pronto!! if (i == listener) // nuova connessione in arrivo addrlen = sizeof remoteaddr; newfd = accept(listener, (struct sockaddr *)&remoteaddr, &addrlen); if (newfd == -) perror("accept"); else FD_SET(newfd, &master); // si aggiunge il nuovo fd al master set if (newfd > fdmax) // e si aggiorna il max fd fdmax = newfd; printf("selectserver: new connection from %s on socket %d\n", inet_ntop(remoteaddr.ss_family, get_in_addr((struct sockaddr*)&remoteaddr), remoteip, INET6_ADDRSTRLEN), newfd); 5 507 Chat Server I/O Multiplexing else // un client ha inviato un messaggio if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) // errore o connessione chiusa dal client if (nbytes == 0) // connessione chiusa printf("selectserver: socket %d hung up\n", i); else perror("recv"); close(i); FD_CLR(i, &master); // un po di pulizia: chiusura socket e // rimozione fd dal set else // sono stati ricevuti dati dal client for(j = 0; j <= fdmax; j++) // inoltro agli altri client if (FD_ISSET(j, &master)) if (j!= listener && j!= i) // ad eccezione di listener e del client stesso if (send(j, buf, nbytes, 0) == -) perror("send"); // END loop tra I file descriptor // END for(;;) return 0; 6 508 5

Echo Client I/O Multiplexing - versione errata void str_cli(file *fp, int sockfd) int maxfdp; fd_set rset; char sendline[maxline], recvline[maxline]; FD_ZERO(&rset); for ( ; ; ) FD_SET(fileno(fp), &rset); FD_SET(sockfd, &rset); maxfdp = max(fileno(fp), sockfd) + ; Select(maxfdp, &rset, NULL, NULL, NULL); if (FD_ISSET(sockfd, &rset)) /* socket is readable */ if (readline(sockfd, recvline, MAXLINE) == 0) perror("str_cli: server terminated prematurely"); fputs(recvline, stdout); if (FD_ISSET(fileno(fp), &rset)) /* input is readable */ if (fgets(sendline, MAXLINE, fp) == NULL) return; /* all done */ writen(sockfd, sendline, strlen(sendline)); Si leggono le singole linee da file e le si invia al server che provvederà a ritornarle Problema: se si arriva a fine file, l applicazione termina, anche se ci possono essere dati sulla socket. Per avere più controllo è opportuno usare shutdown() 509 void str_cli(file *fp, int sockfd) int maxfdp, stdineof; fd_set rset; char sendline[maxline], recvline[maxline]; stdineof = 0; FD_ZERO(&rset); for ( ; ; ) if (stdineof == 0) FD_SET(fileno(fp), &rset); FD_SET(sockfd, &rset); maxfdp = max(fileno(fp), sockfd) + ; Select(maxfdp, &rset, NULL, NULL, NULL); if (FD_ISSET(sockfd, &rset)) /* socket is readable */ if (readline(sockfd, recvline, MAXLINE) == 0) if (stdineof == ) return; /* normal termination */ else perror("str_cli: server terminated prematurely"); Echo Client I/O Multiplexing - versione corretta Soluzione: con shutdown() viene inviato il segmento FIN alla controparte che dopo aver inviato tutti i dati terminerà con il suo FIN fputs(recvline, stdout); if (FD_ISSET(fileno(fp), &rset)) /* input is readable */ if (fgets(sendline, MAXLINE, fp) == NULL) stdineof = ; shutdown(sockfd, SHUT_WR); /* send FIN */ FD_CLR(fileno(fp), &rset); writen(sockfd, sendline, strlen(sendline)); 50 6