SERVER CLIENT. Letteralmente significa presa (di corrente) È l astrazione di un canale di comunicazione fra due computer connessi da una rete

Documenti analoghi
PRATICA - Lezione 1. Autunno PRATICA (Lez. 1)

Laboratorio di Reti di Calcolatori

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

Progettazione di Applicazioni Robuste. Applicazione Echo. Schema Generale di un Server TCP Ricorsivo 1. Applicazione echo

P3-04: I/O multiplexing

Sviluppo di Applicazioni su Rete. Introduzione all API socket di Berkeley. Interazione tra Processi. Modello Client-Server

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

Laboratorio di Reti di Calcolatori

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

Reti (già Reti di Calcolatori )

Interazione con il DNS Conversioni di Nomi ed Indirizzi

Laboratorio di Reti di Calcolatori

funzione fork() La funzione fork è usata per duplicare un processo. #include <unistd.h> pid_t fork (void);

Esercitazione [6] Client/Server con Socket

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

Funzioni bloccanti e soluzioni

Laboratorio di Reti di Calcolatori

Progettazione di un client TCP. Progettazione di un server TCP. Esempio: daytime TCP. Client TCP daytime

Introduzione ai socket

Socket per TCP: Fondamenti

Funzioni bloccanti e soluzioni. Funzioni bloccanti e soluzioni (2) Parametri della funzione select() Funzione select()

Socket TCP. prima parte

Scrittura dei programmi applicativi di rete

LABORATORIO di Reti di Calcolatori

Programmazione di applicazioni di rete con socket - parte 1

Programmazione socket. Queste slide sono distribuite con licenza Creative Commons Attribuzione-Non commerciale-condividi allo stesso modo 2.

Socket TCP. seconda parte

L interfaccia socket

Corso di laurea in Informatica. Reti di Calcolatori A.A Prof. Roberto De Prisco LABORATORIO. Lezione

funzione close() La funzione close è utilizzata normalmente per chiudere un descrittore di file, è utilizzata per chiudere un socket e terminare una

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

Esempio 1: stampa locale di file remoto

*HVWLRQHDYDQ]DWDGHOOH6RFNHWLQ& ODSULPLWLYDVHOHFW

Interazione (TCP) Client-Server con le socket

Socket per TCP: Fondamenti

Laboratorio reti AA 2008/2009. Dott. Matteo Roffilli Ricevimento in ufficio dopo la lezione

Socket II MIDLAB. Sirio Scipioni. M I D L A B.

IPC Inter Process Communication

Socket I MIDLAB. Sirio Scipioni. M I D L A B

Internetworking with TCP/IP (Douglas E. Comer) Vol. I and Vol III.

Programmazione di applicazioni di rete

Socket per TCP: Fondamenti

Scrittura dei programmi applicativi di rete

Una semplice applicazione client/server 1

I Socket. Laboratorio Software M. Grotto R. Farina

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

Laboratorio di Sistemi Operativi Cognome Nome Mat.

COMUNICAZIONE TRA PROCESSI REMOTI IN UNIX

(VHUFLWD]LRQLGLEDVHVXOOH6RFNHWLQ&

INTERNET DOMAIN SOCKETS (Cap.59)

Esempio 1: stampa locale di file remoto

Laboratorio di Reti di Calcolatori

I Socket di Berkeley

Sistemi Operativi Teledidattico

INGEGNERIA DEL WEB. VinX

rsystem Maximiliano Marchesi

Reti di Calcolatori - Laboratorio. Lezione 5. Gennaro Oliva

Timeout. Socket Avanzati. Funzione alarm() Client echo UDP con alarm

Esercitazione Laboratorio di Sistemi Operativi Cognome Nome Mat.

DATAGRAM SOCKET. Angelastro Sergio Diomede Antonio Viterbo Tommaso

Creare una applicazione Winsock di base

Cenni di programmazione distribuita in C++ Mauro Piccolo

Guida all' uso dei sockets nella programmazione in C

Corso di Reti di Calcolatori T

Sistemi di Elaborazione. Introduzione alla Programmazione distribuita

Parametri valore-risultatorisultato

Esempi di Client e Server

Programmazione di applicazioni di rete con socket - parte 2

Server Ricorsivii i. Dott. Delfina Malandrino.

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

Creare un'elementare backdoor in C in ambiente UNIX

SC per Inter Process Comminication. Comunicazione fra macchine diverse: socket

I/O su Socket TCP: read()

Socket per TCP: Fondamenti

Inter-process communication: socket

Paradigma client-server

TECN.PROG.SIST.INF. TCP socket in Windows Roberta Gerboni

Opzioni del Socket. Socket Options. Opzioni di Livello Socket. Livello delle Opzioni

Una socket è un punto estremo di un canale di comunicazione accessibile mediante un file descriptor. Alcuni tipi predefiniti di socket

Lo strato di applicazione in Internet

Sistemi Operativi. Marzo-Giugno 2011 matricole congrue 0 mod 3. Controllo dei processi - I

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

programmazione distribuita Introduzione Introduzione alla programmazione distribuita

Avviso ai programmatori. Programmazione in rete: i socket. Esercizio - copia dati. Messaggi di errore. stdarg.h. Funzioni di errore

CORSO DI SISTEMI OPERATIVI A - ESERCITAZIONE 6

Esercitazione sulle Socket

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

unsigned long inet_addr(cp) char *cp;

Corso di Laurea in Ingegneria Informatica. Corso di Reti di Calcolatori a.a. 2009/10

La programmazione di rete

Laboratorio di Programmazione in rete

IPC: InterProcess Communication

Introduzione alla programmazione C di socket

Program m azione di Sistem a 6

Processi - II. Franco Maria Nardini

Sistemi Operativi Anno Accademico 2011/2012. Segnali: Interrupt software per la gestione di eventi asincroni

Ret e i Inf n o f rm r a m t a ich c e Terza esercitazione

Esame Laboratorio di Sistemi Operativi Cognome Nome Mat.

Le Opzioni per i Socket

Transcript:

Socket 02.2 Letteralmente significa presa (di corrente) È l astrazione di un canale di comunicazione fra due computer connessi da una rete -02: Socket TCP Autunno 2002 Prof. Roberto De Prisco Sono definiti per vari protocolli Per TCP/IP un socket identifica i due punti della connessione Un indirizzo IP ed una porta su un host Un indirizzo IP ed una porta sull altro host Università degli studi di Salerno Laurea e Diploma in Informatica Funzioni per i socket 02.3 Sockaddr_in 02.4 Tipica interazione in una connessione TCP socket() bind() struct in_addr { in_addr_t s_addr; /* 32-bit, network byte ordered */ CLIENT socket() write() read() close() Stabilisce una connessione Dati (richiesta) Dati (risposta) Notificazione di fine comunicazione listen() accept() read() write() read() close() Aspetta una connessione SERVER struct sockaddr_in { uint8_t sin_len; sa_family_t sin_family; /* tipo di protocollo, AF_INET */ in_port_t sin_port; /* 16-bit, network byte ordered */ struct in_addr sin_addr; /* struttura indirizzo IP */ char sin_zero[8]; struct sockaddr { uint8_t sin_len; sa_family_t sin_family; /* tipo di protocollo: AF_XXX */ char sa_data[14]; /* indirizzo specifico del protocollo */ sin_zero Utilizzata per far si che la grandezza della struttura sia almeno 16 byte sin_len Non è richiesta dallo standard Posix Esistono diverse strutture con grandezze differenti Lunghezze strutture socket 02.5 Funzione socket 02.6 int socket(int family, int type, int protocol ); Valore di ritorno: -1 se errore un socket descriptor se OK Socket descriptor è come un file descriptor Sono presi dallo stesso insieme Se un intero è usato come file descriptor non può essere usato come socket descriptor e viceversa Socket e file sono visti più o meno allo stesso modo read, write, close sono le stesse funzioni dei file 1

Funzione socket int family Un intero che specifica quale famiglia di protocolli si intende usare: AF_INET IPv4 AF_INET6 IPv6 AF_LOCAL prot. locale (client e server sullo stesso host) AF_ROUTE Sockets per routing altri int type Un intero che dice il tipo di socket SOCK_STREAM per uno stream di dati (TCP) SOCK_DGRAM per datagrammi (UDP) SOCK_RAW per applicazioni dirette su IP int protocol 0, tranne che per SOCK_RAW 02.7 Funzione connect int connect(int sd, struct sockaddr *servaddr, socklen_t addrlen); Valore di ritorno: -1 se errore, 0 se OK Permette ad un client di aprire una connessione con il server Il kernel sceglie una porta effimera (e l indirizzo IP) Nel caso di una connessione TCP viene fatto l handshaking, in caso di errore ritorna ETIMEDOUT ECONNREFUSED EHOSTUNREACH 02.8 Funzione bind 02.9 Funzione listen 02.10 int bind(int sd, struct sockaddr*myaddr, socklen_t addrlen); Valore di ritorno: -1 se errore, 0 se OK Permette ad un server di assegnare un indirizzo per il server al socket Con TCP l indirizzo può essere indirizzo IP (deve essere una delle interfacce) porta entrambi nessuno Se la porta non è specificata (valore 0) ne viene scelta una effimera Se l indirizzo IP è quello wildcard (INADDR_ANY, 0) viene usato quello designato come IP destinazione nel SYN del client int listen(int sd, int backlog); Valore di ritorno: -1 se errore, 0 se OK Usata solo da un server TCP, serve a 1. Convertire il socket da attivo a passivo, per far sì che il kernel accetti connessioni sul socket Per default un socket è creato attivo, e il kernel si aspetta che sia il socket di un client Nel diagramma a stati TCP fa muovere da CLOSED a LISTEN 2. Backlog specifica quante connessioni accettare e mettere in attesa per essere servite Backlog 02.11 Funzione accept 02.12 int accept(int sd, struct sockaddr*cliaddr, socklen_t addrlen); accept CODA connessioni completate (stato ESTABLISHED) Valore di ritorno: -1 se errore, socked descriptor se OK apertura conn. completata SYN apertura connessione CODA connessioni incomplete (stato SYN_RCVD) Permette ad un server di prendere la prima connessione completata dalla coda Se non ce ne sono si blocca connect dal client La somma degli elementi in entrambe le code non può superare il backlog cliaddr è un parametro valore-risultato In chiamata contiene il listening socket Al ritorno contiene il socket connesso al particolare client 2

Daytime server (1) 02.13 Daytime server (2) 02.14 #include "basic.h" #include <time.h> pid_t pid; int listenfd, connfd; struct sockaddr_in servaddr; char buff[maxline]; time_t ticks; struct servent *sp; if( (listenfd = socket(af_inet, SOCK_STREAM, 0)) < 0) daytimesrv.c if( (connfd = accept(listenfd, (struct sockaddr *) NULL, NULL)) < 0) err_sys("accept error"); ticks = time(null); snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); write(connfd, buff, strlen(buff)); close(connfd); if( (sp = getservbyname("daytime", "tcp")) == NULL ) { fprintf(stderr, "getservbyname error for daytime, tcp."); exit(-1); servaddr.sin_addr.s_addr = htonl(inaddr_any); servaddr.sin_port = sp->s_port; if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0) err_sys("bind error"); iterativo Serve i client uno alla volta Quando un client è connesso il seguente client deve aspettare Accettabile per server semplici come il daytime if( listen(listenfd, BACKLOG) < 0 ) /* backlog = 5 */ err_sys("listen error"); Daytime client (1) 02.15 Daytime client (2) 02.16 #include "basic.h" daytimecli.c int sockfd, n; char recvline[maxline + 1]; struct sockaddr_in servaddr; struct in_addr **pptr; struct hostent *hp; struct servent *sp; if (argc!= 3) err_quit("usage: daytimecli <hostname> <service>"); if ( (hp = gethostbyname(argv[1])) == NULL) err_quit("hostname error for %s: %s", argv[1], hstrerror(h_errno)); if ( (sp = getservbyname(argv[2], "tcp")) == NULL) err_quit("getservbyname error for %s", argv[2]); pptr = (struct in_addr **) hp->h_addr_list; for ( ; *pptr!= NULL; pptr++) { if( (sockfd = socket(af_inet, SOCK_STREAM, 0)) < 0 ) servaddr.sin_port = sp->s_port; memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr)); if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == 0) break; /* success */ err_ret("connect error"); close(sockfd); if (*pptr == NULL) err_quit("unable to connect"); while ( (n = read(sockfd, recvline, MAXLINE)) > 0) { recvline[n] = 0; /* null terminate */ fputs(recvline, stdout); exit(0); ricorsivi 02.17 ricorsivi 02.18 Un server ricorsivo usa una copia di se stesso per servire una richiesta pid_t pid; int listenfd, connfd; Richiesta di connessione listenfd = socket(.); /* riempi la struttura sockaddr_in (es. numero di porta) */ bind(listenfd,.) listen(listenfd, LISTENQ) connfd = accept(listenfd, ); if ( (pid = fork()) == 0) { close(listenfd); /* figlio chiude il socket di ascolto */ DOIT(connfd); /* serve la richiesta */ close(connfd); /* chiude il socket */ exit(0); /* il figlio termina */ close(connfd); /* il padre chiude il socket della connessione */ Il server chiama accept() Viene creato un nuovo socket descriptor nel server per la connessione con questo particolare client Connessione stabilita 3

ricorsivi 02.19 ricorsivi 02.20 Connessione stabilita padre padre Connessione stabilita figlio Il padre chiude il socket della connessione Può accettare nuove connessioni figlio Il server chiama fork() Padre e figlio nel server condividono il socket Il figlio chiude il socket per l accettazione di nuove connessioni Può gestire la connessione con il client Getsockname e getpeername 02.21 Echo server (1) 02.22 int getsockname(int sd, struct sockaddr*localaddr, socklen_t addrlen); int getpeername(int sd, struct sockaddr*remoteaddr, socklen_t addrlen); #include #include "basic.h" "echo.h" echosrv.c Valore di ritorno: -1 se errore, socked descriptor se OK Ritornano l indirizzo locale associato al socket L indirizzo dell altro lato della connessione associata al socket Serve perché Un client che non chiama bind non sa quale porta è stata usata Un client non sa l indirizzo IP usato se ci sono più interfaccie Una chiamata a bind con porta=0 assegna una porta effimera Stessa cosa per l indirizzo IP (INADDR_ANY) Dopo una exec si può risalire agli indirizzi della connessione NB: un file descriptor rimane aperto quando si chiama exec pid_t childpid; int listenfd, connfd; struct sockaddr_in servaddr, cliaddr; socklen_t cliaddr_len; if( (listenfd = socket(af_inet, SOCK_STREAM, 0)) < 0) servaddr.sin_addr.s_addr = htonl(inaddr_any); servaddr.sin_port = htons(port); /* daytime server */ if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0) err_sys("bind error"); if( listen(listenfd, LISTENQ) < 0 ) err_sys("listen error"); Echo server (2) 02.23 Echo client (1) 02.24 cliaddr_len = sizeof(cliaddr); if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0) err_sys("accept error"); if( (childpid = fork()) == 0 ) { close(listenfd); str_echo(connfd); exit(0); close(connfd); void str_echo(int sockfd) { ssize_t n; char line[maxline]; if ( (n = read(sockfd, line, MAXLINE)) == 0) return; /* connection closed by other end */ write(sockfd, line, n); #include "basic.h" echocli.c #include "echo.h" int sockfd, n; struct sockaddr_in servaddr; if (argc!= 2) err_quit("usage: echotcpcli <IPaddress>"); if ( (sockfd = socket(af_inet, SOCK_STREAM, 0)) < 0) servaddr.sin_port = htons(port); /* echo server */ if (inet_pton(af_inet, argv[1], &servaddr.sin_addr) <= 0) err_quit("inet_pton error for %s", argv[1]); if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) err_sys("connect error"); str_cli(stdin, sockfd); /* svolge tutto il lavoro del client */ exit(0); 4

Echo client (2) 02.25 Echo server 02.26 void str_cli(file *fp, int sockfd) { char sendline[maxline], recvline[maxline]; while (fgets(sendline, MAXLINE, fp)!= NULL) { reti_writen(sockfd, sendline, strlen(sendline)); if (reti_readline(sockfd, recvline, MAXLINE) == 0) err_quit("str_cli: server terminated prematurely"); fputs(recvline, stdout); Per semplicità facciamo girare server e client sulla stessa macchina prompt > echoserver & [1] 21130 prompt > netstat a Proto Recv-Q Send-Q Local address Foreign address (state) Tcp 0 0 *.9877 *.* LISTEN prompt > echoclient 127.0.0.1 In un altra finestra prompt > netstat a Proto Recv-Q Send-Q Local address Foreign address (state) Tcp 0 0 localhost.9877 localhost.1052 ESTABLISHED Tcp 0 0 localhost.1052 localhost.9877 ESTABLISHED Tcp 0 0 *.9877 *.* LISTEN A questo punto la connessione è stabilita Echo server 02.27 Echo server 02.28 prompt > echoclient 127.0.0.1 Arrivederci Arrivederci prompt > prompt > netstat a grep 9877 Tcp 0 0 localhost.1052 localhost.9877 TIME_WAIT Tcp 0 0 *.9877 *.* LISTEN Il server ha chiuso il socket Digitata al terminale Risposta del server Digitata al terminale Risposta del server Digitata al terminale Il client è nello stato di TIME_WAIT Il lato che chiude la connessione rimane in questo stato per un certo periodo (2MSL) per 1. Mantenere informazioni nel caso l ultimo ACK viene perso e l altro lato rispedisce l ultimo FIN 2. Permettere a vecchi pacchetti di essere eliminati dalla rete in modo da non farli interferire con successive connessioni Digitando, il client termina chiamando exit Il kernel chiude tutti i file descriptor, quindi anche i socket descriptor Quindi il socket del client viene chiuso La chiusura implica la spedizione di FIN al server La ricezione dell ACK al FIN A questo punto il server è nello stato CLOSE_WAIT mentre il client è nello stato FIN_WAIT_2 La prima parte della chiusura di una connessione TCP è conclusa Quando il server riceve il FIN è nella readline che ritorna EOF e quindi chiama exit I file descriptor vengono chiusi, quindi anche il socket ed un FIN viene spedito al client A questo punto la conessione è completamente terminata ed il client va nello stato TIME_WAIT mentre il server ha chiuso la connessione Dopo un certo periodo (2 Maximum Segment Lifetime) il client chiude la connessione Segnale SIGCHLD 02.29 zombie 02.30 In un server ricorsivo, il server crea un figlio per gestire la connessione quando la connessione viene chiusa il figlio termina Il sistema operativo manda un segnale di SIGCHLD al padre e il figlio diventa zombie Zombie sono dei processi terminati per i quali vengono mantenuti dei dati nel sistema operativo Zombie sono necessari per permettere al padre di controllare il valore di uscita del processo e utilizzo delle risorse del figlio (memoria, CPU, etc.) Ovviamente non vogliamo lasciare zombie Occorre scrivere un signal handler che chiama wait Ognli client che termina lascia uno zombie <defunct> indica uno zombie robdep@zaffiro:~/corsi/reti/c> echocli 127.0.0.1 ciao ciao robdep@zaffiro:~/corsi/reti/c> echocli 127.0.0.1 pippo pippo robdep@zaffiro:~/corsi/reti/c> ps PID TTY TIME CMD 1077 pts/0 00:00:00 cat 22084 pts/2 00:00:00 bash 27162 pts/3 00:00:00 ssh 30007 pts/6 00:00:00 bash 30331 pts/11 00:00:00 bash 30761 pts/11 00:00:00 echosrv 30765 pts/11 00:00:00 echosrv <defunct> 30767 pts/11 00:00:00 echosrv <defunct> 30768 pts/6 00:00:00 ps Il client viene ucciso Il client viene ucciso 5

Signal handler 02.31 Interruzione delle system call 02.32 void sig_child(int signo) { pid_t pid; int stat; while ( pid = waitpid(-1,&stat,wnohang)) > 0) { printf( Child %d terminated\n,pid); Utilizzando il gestore di segnali si evitano i processi zombie Appena il figlio finisce viene chiamata waitpid Prompt > echoserver & [2] 19287 prompt > echoclient 127.0.0.1 Child 19293 terminated accept error: interrupted system call Il segnale è stato catturato dal padre durante l esecuzione di accept Il gestore del segnale viene eseguito Poiché è stata interrotta la funzione accept ritorna con il codice di errore EINTR Poiché la gestione di tale errore non è prevista il server termina l esecuzione Occorre tener presente questo problema In alcuni sistemi le system call sono automaticamente richiamate in altri no Una possibile soluzione 02.33 Reset connessione e accept 02.34 clilen = sizeof(cliaddr); if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) { if (errno = EINTR) continue; else { perror( accept error ); exit(1); Se la chiamata ad accept ritorna EINTR accept viene richiamata Se l errore è diverso da EINTR Si gestisce l errore (nell esempio si chiama exit) Un altro errore tipico da gestire con accept è il reset della connessione prima della chiamata ad accept La connessione diventa ESTABLISHED Il client spedisce un RST Il server chiama accept Accept ritorna un codice di errore ECONNABORTED Il server può richiamare accept per la prossima connessione Terminazione del server 02.35 SIGPIPE 02.36 Cosa succede se il server termina prematuramente? Prompt > echoclient 127.0.0.1 Ciao Ciao Il server viene ucciso Arrivederci Il server non risponde (dipende dal codice) Cosa succede se il client ignora l errore su readline e scrive nel socket? Questo può capitare se il codice ha due write consecutive La prima fa sì che il server spedisca RST La seconda crea il problema Viene generato un segnale di SIGPIPE Il processo termina se il segnale non viene catturato o ignorato Al kill i socket descriptor vengono chiusi Un FIN viene spedito al client Il client spedisce Arrivederci al server È permesso perché il client non ha chiuso il socket Il client chiama readline che ritorna EOF Non si aspetta di ricevere EOF quindi stampa il messaggio di errore e termina Se SIGPIPE è ignorato l operazione di write genera l errore di EPIPE Soluzione semplice, quando non si deve reagire all errore 1. Ignorare (SIG_IGN) il segnale di SIGPIPE Assume che non occorre fare niente di speciale in tale circostanza 2. Controllare l errore di EPIPE sulle write e nel caso di errore terminare (non scrivere più) 6

Macchina server non raggiungibile 02.37 shutdown and reboot 02.38 Un altra possibile causa di errore è se la macchina server non risponde proprio Diverso da uccidere il processo server (in quel caso vengono spediti FIN, RST) Può dipendere dalla rete O dalla macchina server Il client è bloccato in readline TCP ritrasmetterà i dati per ricevere l ACK fino ad un certo timeout La connessione viene stabilita Il server va giù e fa il reboot senza che il client se ne accorga Non c è comunicazione durante lo shutdown (server scollegato dalla rete altrimenti spedisce FIN) Il client spedisce nuovi dati al server dopo il reboot Il server non ha più il socket aperto TCP risponde ai dati con un RST La funzione di lettura dal socket ritorna un errore ETIMEOUT EHOSTUNREACH, ENETUNREACH è in readline quando riceve RST Readline ritorna ECONNRESET somma 02.39 somma 02.40 Solo la funziona che gestisce il client void server_somma(int sockfd) { int i, arg1, arg2; ssize_t n; char sendline[maxline], rcvline[maxline]; char c; if ( (n = reti_readline(sockfd, rcvline, MAXLINE)) == 0) return; /* connection closed by other end */ /* legge dalla stringa passata dal client i due interi da sommare */ if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 ) /* converte il risultato in stringa e lo scrive nel buffer */ sprintf(sendline, "%d\n", arg1 + arg2); else sprintf(sendline, "input error\n"); n = strlen(sendline); reti_writen(sockfd, sendline, n); sommasrv.c Il codice del client somma è un pò più complesso Deve gestire due input I dati in arrivo dal socket I dati digitati dall utente alla tastiera Questo problema verrà affrontato in seguito IO multiplexing Select Il codice è disponibile sulla pagina Web sommacli.c Problema 02.41 e server, stesso tipo di macchina sunos5 > sommacli 206.62.226.33 11 22 33-11 -44-55 e server, macchine di tipo diverso Una Sparc l altra Intel bsdi > sommacli 206.62.226.33 11 22 33-11 -44-16542537 Sparc: big-endian, Intel: little-endian 7