40
C.Brandolese Politecnico di Milano Socket Socket Calcolatori Elettronici

Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Embed Size (px)

Citation preview

Page 1: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

C.Brandolese Politecnico di Milano

SocketSocket

Calcolatori Elettronici

Page 2: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 2C.Brandolese

SommarioSommario

• Introduzione• Interfaccie e protocolli• Il modello Client/Server• Indirizzamento• Connessione• Strutture dati e costanti• Funzioni di utilità• Gestione dei socket• Esempi

Page 3: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 3C.Brandolese

IntroduzioneIntroduzione

• Una applicazione è un insieme di programmi coordinati in modo da svolgere una determinata funzione: la funzione applicativa

• In un ambiente di programmazione usuale una applicazione coincide generalmente con un singolo programma.

• In un ambiente distribuito si parla di applicazioni distribuite• Applicazione distribuita:

• E’ costituita da un insieme di programmi che vengono generalmente eseguiti su macchine diverse

• I vari programmi cooperano attraverso una rete di calcolatori

• Le applicazioni distribuite richiedono la capacità di comunicare attraverso una rete.

Page 4: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 4C.Brandolese

IntroduzioneIntroduzione

• I vari programmi che costituiscono una applicazione distribuita devono seguire alcune regole per comunicare in modo corretro

• L’insieme delle regole per la comunicazione prende il nome di protocollo applicativo

• Si noti che, come è ovvio, due programmi comunicano tra di loro solo quando sono in esecuzione: a rigore quindi sono i processi a comunicare sulla base delle istruzioni constenute nei programmi

• Nel seguito si parlerà indifferentemente di processi o di programmi, intendendo quanto appena chiarito

Page 5: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 5C.Brandolese

Interfaccie e protocolliInterfaccie e protocolli

• Per la comunicazione i programmi di una applicazione distribuita si appoggiano a un insieme di servizi tipici forniti:• Dal sistema operativo• Dal software di rete

• Il programmatore utilizzare in pratica delle funzioni di libreria• L’insieme delle funzioni di base prende il nome di Application

Program Interface o API• E’ bene notare la differenza tra protocollo applicativo e API:

• Il protocollo applicativo rappresenta le regole per la comunicazione, ma non è un canale diretto di comunicazione

• Le API sono le funzioni che realizzano sul canale fisico le regole di comunicazione definite nel protocollo.

Page 6: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 6C.Brandolese

Interfaccie e protocolliInterfaccie e protocolli

• Esempio di applicazione distribuita:

APPLICAZIONE DISTRIBUTA

PROGRAMMAAPPLICATIVO

PROGRAMMAAPPLICATIVO

Sistema Operativoe

Software di Rete

Sistema Operativoe

Software di Rete

Protocollo Applicativo

API API

RETE

Page 7: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 7C.Brandolese

Interfaccie e protocolliInterfaccie e protocolli

• Nel seguito faremo riferimento ai sistemi operativi:• UNIX • WindowsNT

• Supporremo inoltre che il software di rete disponibile sia TCP/IP• TCP/IP definisce in maniera astratta alcuni servizi che i vari

sistemi operativi realizzano e rendono disponibili tramite le API• Uno dei servizi forniti dal protocollo TCP/IP è l’interfaccia:

• SOCKET (UNIX)• WINSOKET (WindowsNT)

Page 8: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 8C.Brandolese

Interfaccie e protocolliInterfaccie e protocolli

• La configurazione di riferimento di una applicazione distribuita basata su TCP/IP e soket è il seguente:

PROGRAMMA C PROGRAMMA C

UNIX API + SOKET NT API + WINSOKET

UNIX + TCP/IP WindowsNT + TCP/IP

Protocollo Applicativo

Canale

Page 9: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 9C.Brandolese

Il modello Client/ServerIl modello Client/Server

• Il protocollo TCP/IP fornisce un meccanismo di comunicazione tra processi residenti su macchine diverse

• Il tipo di comunicazione è detto peer-to-peer (da pari a pari)• Sta al programmatore definire le regole di comunicazione cioè il

protocollo applicativo• Molte applicazioni sono basate sul modello Client/Server• In questo modello una delle applicazioni agisce da Server mentre

le altre agiscono da Client• In generale un processo può assumere il ruolo di Client o di

Server dinamicamente nel tempo

Page 10: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 10C.Brandolese

Il modello Client/ServerIl modello Client/Server

• Il processo Server:• Fornisce servizi• Accetta richieste provenienti dai client• Esegue i servizi richiesti• Eventualmente ritorna un risultato al richiedente

• Il processo Client:• Richiede servizi ad un server• Attende una risposta da parte del server

• Ad esempio:• Server: WEBServer• Client: Browser

Page 11: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 11C.Brandolese

IndirizzamentoIndirizzamento

• Quando un processo P intende comunicare con un processo Q, in esecuzione su un’altra macchina deve:• Identificare la macchina su cui è in esecuzione Q• Identificare il processo Q fra tutti quelli in esecuzione sulla macchina

• Una macchina è individuata dal suo indirizzo IP:• Un indirizzo IP è un numero di 32 bit univoco all’interno di una rete• Gli indirizzi IP vengono scritti esprimendo il valore decimale dei 4 gruppi di

8 bit che lo compongono, separati da un punto

• Un processo è individuato attraverso un port:• Un port è un numero usato per identificare un processo solo ai fini della

comunicazione TCP/IP• Il port di un processo non ha nessuna relazione con il suo pid

Page 12: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 12C.Brandolese

IndirizzamentoIndirizzamento

• Port e pid identificano un processo ma:• Il pid di un processo è assegnato dal sistema operativo ed il

programmatore non ha alcun controllo sul suo valore• Il port viene assegnato dal programmatore

• Affinché il processo P possa comunicare con il processo Q, i due programmi devono stabilire un port con cui identificarsi

• I port da 0 a 1023 sono utilizzati da servizi standard e non possono essere usati dal programmatore

• Un idirizzo TCP completo è quindi costituito dalla coppia:

< IP_Address, Port >

Page 13: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 13C.Brandolese

ConnessioneConnessione

• La comunicazione in TCP si dice orientata alla connesione• Questo significa che, prima di poter scambiare dati, due processi

devono stabilire una connessione• In un modello client/server, per stabilire una connessione tra due

processi P e Q è necessario che:• Il processo P, che agisce da server, si metta in attesa di richieste da parte

del processo Q• Il processo Q, che agisce da client, invia richieste al processo P ed

attende una risposta

• Quando il server P accetta una richiesta di un servizio da parte del processo Q si stabilisce la connessione

Page 14: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 14C.Brandolese

ConnessioneConnessione

• Aspetti generali di una connessione:• I due processi che stabiliscono una connessione sono detti punti terminali• Ogni processo è identificato dalla coppia <IP_Address, Port>• La connessione è identificata dai suoi punti terminali cioè dalle due coppie <IP_Address, Port> relative appunto ai punti terminali

• Dopo aver stabilito la connessione, i processi dispongono di un canale:• Bidirezionale: la comunicazione può avvenire nelle due direzioni

• Affidabile: un meccanismo di acknowledge garantisce che i dati siano ricevuti

• Orientato allo stream: i dati vengono trasmessi in modo continuo

• Si noti che l’identificazione della connessione consente ad un processo di partecipare a diverse connessioni in quanto TCP le distingue in base agli indirizzi dei punti terminali

Page 15: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 15C.Brandolese

Strutture dati e costantiStrutture dati e costanti

• Gli indirizzi di socket sono memorizzati in una struttura dati C:

• Il significato dei campi è il seguente• sin_family: famiglia degli indirizzi. Nel nostro caso AF_INET.• sin_port: numero del port. Compreso tra 0 e 64k• sin_addr: indirizzo IP. Si tratta di una union di cui useremo solo il

campo s_addr di tipo u_long, cioè intero a 32 bit

senza segno• sin_zero: non utilizzato

struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8];

Page 16: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 16C.Brandolese

Strutture dati e costantiStrutture dati e costanti

• Sono definiti alcuni gruppi di costanti• Costanti per la definizione del tipo di indirizzo

• AF_UNIX: Indirizzi ARPA, locali alle macchine• AF_INET: Indirizzi Internet

• Costanti per la definizione del tipo di comunicazione:• SOCK_STREAM: Orientata allo stream• SOCK_DGRAM: Datagram• SOCK_RAW: Raw data

• Queste costanti vengono utilizzate nella creazione dei socket e nella definizione degli indirizzi

Page 17: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 17C.Brandolese

Funzioni di utilitàFunzioni di utilità

• Conversione da stringa a indirizzo IP:

• Conversione da formato host a formato network

• Conversione da formato network a formato host

uint32_t htonl(unint32_t hostlong);

uint16_t htons(uint16_t hostshort);

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);

unsigned long inet_addr(const char *cp);

Page 18: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 18C.Brandolese

Gestione dei socketGestione dei socket

• Creazione di un socket:

• I parametri hanno il seguente significato:• domain: famiglia di protocolli, nel nostro caso AF_INET• type: tipo di connessione, nel nostro caso SOCK_STREAM• protocol: tipo di protocollo, nel nostro caso esiste solo il

protocollo IPquindi questio parametro vale generalmente 0

• Crea un socket con le caratteristiche specificate e restituisce un descrittore

• Il descrittore è simile al descrittore utilizzato per la gestione dei files

int socket(int domain, int type, int protocol);

Page 19: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 19C.Brandolese

Gestione dei socketGestione dei socket

• Chiusura di un socket:

• I parametri hanno il seguente significato:• socket: descrittore di socket

• Chiude il socket individuato dal descrittore specificato• Dopo la chiusura, il socket non è più accessibile

int close(int socket);

Page 20: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 20C.Brandolese

Gestione dei socketGestione dei socket

• Associazione di un socket ad un indirizzo locale:

• I parametri hanno il seguente significato:• s: descrittore di socket• locaddr: indirizzo IP local al quale il socket viene associato• locaddrlen: dimensioni della struttura dati utilizzata per

specificare l’indirizzo locale. Generalmente si ottiene tramite la funzione standard sizeof()

• Lega il socket il cui descrittore è s all’indirizzo locale specificato nella struttura dati locaddr.

• Questa funzione associa al socket uno dei due punti terminali

int bind(int s, struct sockaddr *locaddr, int locaddrlen);

Page 21: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 21C.Brandolese

Gestione dei socketGestione dei socket

• Dimensione della coda di un socket:

• I parametri hanno il seguente significato:• s: descrittore di socket• backlog: dimensione massima della coda di richieste

• Specifica il numero massimo di richieste che possono essere messe in coda sul socket s

• Una richiesta che arrivi quando la coda è piena viene rifutata ed il processo richiedente riceve un messaggio di errore

int listen(int s, int backlog);

Page 22: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 22C.Brandolese

Gestione dei socketGestione dei socket

• Accettazione di una richiesta di connessione:

• I parametri hanno il seguente significato:• s: descrittore di socket• destaddr: indirizzo del processo richiedente• destaddrlen: dimensione della struttura dati contenete l’indirizzo

del processo richiedente

• Accetta la prima richiesta di connessione in coda, crea un nuovo socket e ne restituisce il descrittore

• Il socket creato non può più essere utilizzato per accettare altre richieste mentre il socket originale si

int accept(int s, struct sockaddr *destaddr, int *destaddrlen);

Page 23: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 23C.Brandolese

Gestione dei socketGestione dei socket

• Crea una connessione:

• I parametri hanno il seguente significato:• s: descrittore di socket• destaddr: indirizzo del processo remoto richiedente• destaddrlen: dimensione della struttura dati contenete l’indirizzo

del processo richiedente

• Crea una connessione, nel nostro caso TCP/IP, con il richiedente il cui indirizzo è specificato dagli ultimi due parametri

• Dopo la creazione della connessione i due processi possono iniziare lo scambio di dati

int connect(int s, struct sockaddr *destaddr, int *destaddrlen);

Page 24: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 24C.Brandolese

Gestione dei socketGestione dei socket

• Scrive/legge dati attraverso un socket:

• I parametri hanno il seguente significato:• fd: descrittore di socket (o di file)• buf: puntatore all’area dati• nbyte: dimensione dei dati

• Legge o scrive dati attraverso un socket• Queste funzioni sono le stesse utlizzate per la lettura scrittura di

dai da/verso i files

int write(int fd, char *buf, int nbyte);

int read (int fd, char *buf, int nbyte);

Page 25: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 25C.Brandolese

Esempio: Client/Server – Singola connessioneEsempio: Client/Server – Singola connessione

• Il server:• Legge dalla linea di comando il numero di porta• crea un socket su quella porta • Aspetta chiamate

• Ogni volta che arriva una chiamata:• Legge da socket un comando e lo esegue • Scrive il risultato sullo stesso socket• Chiude la connessione • Si rimette in attesa

• Il ciclo di attesa non termina mai• Per terminare il processo server occorre ucciderlo (kill) esplicitamente

Page 26: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 26C.Brandolese

Esempio: Client/Server – Singola connessioneEsempio: Client/Server – Singola connessione

• Il client: • Legge dalla linea di comando l'indirizzo IP e il numero di port del server• Crea un socket• Si connette al server• Legge un comando da standard input • Invia il comando al server• Legge la risposta• Trascrive la risposta su standard output • Chiude la connessione • Termina

• Ogni volta che il client è invocato comunica un solo comando al server

Page 27: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 27C.Brandolese

Esempio: Client/Server – Singola connessioneEsempio: Client/Server – Singola connessione

• Il server è organizzato come segue:• Creazione del socket socket()

• Inizializzazione dell’indirizzo: • contenuto nella variabile server_addr • il tipo è struct sockaddr_in

• Binding del socket bind()

• Definizione del numero massimo di connessioni listen()

• Ciclo infinito, in cui:• Accetta una connessione accept()• Legge un comando da socket read()• Scrive il risultato dell’esecuzione del comando su socket write()• Termina la connessione close()

Page 28: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 28C.Brandolese

Esempio: Client/Server – Singola connessioneEsempio: Client/Server – Singola connessione

#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h> #define MAX_CONN 5 int main(int argc, char* argv[]){ int sock_fd; int new_sock_fd; int client_len; struct sockaddr_in server_addr; struct sockaddr_in client_addr; char command;

if( argc != 2 ){ printf(“Numero di parametri errato\n”); exit(1); }

sock_fd = socket(AF_INET, SOCK_STREAM, 0);

...

SERVER

Page 29: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 29C.Brandolese

Esempio: Client/Server – Singola connessioneEsempio: Client/Server – Singola connessione

...

server_addr.sin_family = AF_INET;

server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

server_addr.sin_port = htons((u_short) atoi(argv[1]));  

bind( sock_fd,

(struct sockaddr *) &server_addr,

sizeof(server_addr)

);

listen(sock_fd, MAX_CONN);

...

SERVER

Page 30: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 30C.Brandolese

Esempio: Client/Server – Singola connessioneEsempio: Client/Server – Singola connessione

... while(1) { new_sock_fd = accept( sock_fd, (struct sockaddr *) &client_addr,

&client_len ); read(new_sock_fd, &command, 1); switch (command) { case 'a': write(new_sock_fd, "SERVICE_A", strlen("SERVICE_A") ); break; case 'b': write(new_sock_fd, "SERVICE_B", strlen("SERVICE_B") ); break; default: write(new_sock_fd, "UNKNOWN", strlen("UNKNOWN") ); break; } close(new_sock_fd); }}

SERVER

Page 31: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 31C.Brandolese

Esempio: Client/Server – Singola connessioneEsempio: Client/Server – Singola connessione

• Il client è organizzato come segue:• Creazione del socket socket()

• Inizializzazione dell’indirizzo: • L’indirizzo IP è in argv[1] • Il port del server è in argv[2]

• Connessione al server connect()

• Legge un comando da standard input• Scrive il comando sul soket write()

• Legge il risultato dal socket read()

• Scrive il risultato sullo standard output• Chiude il socket e termina close()

Page 32: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 32C.Brandolese

Esempio: Client/Server – Singola connessioneEsempio: Client/Server – Singola connessione

#include <stdio.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

 

int main(int argc, char* argv[]) {

int sock_fd;

int err;

struct sockaddr_in server_addr;

char command;

char result;

if (argc != 3) {

printf(“Numero di parametri errato\n”);

exit(1);

}

sock_fd = socket(AF_INET, SOCK_STREAM, 0);

...

CLIENT

Page 33: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 33C.Brandolese

Esempio: Client/Server – Singola connessioneEsempio: Client/Server – Singola connessione

server_addr.sin_family = AF_INET;

server_addr.sin_addr.s_addr = inet_addr( argv[1] );

server_addr.sin_port = htons( (u_short)atoi(argv[2]) );

err = connect( sock_fd,

(struct sockaddr *) &server_addr,

sizeof(server_addr)

);

if( err < 0 ) exit(1);

read(0, &command, 1);

write(sock_fd, &command, 1);

while( read( sock_fd, &result, 1 ) )

write( 1, &result, 1 );

close( sock_fd );

}

CLIENT

Page 34: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 34C.Brandolese

Esempio: Client/Server – Più connessioniEsempio: Client/Server – Più connessioni

• Il server: • Legge dalla linea di comando il numero di porta• Crea un socket su quella • Aspetta chiamate

• Ogni volta che arriva una chiamata:• Genera un processo figlio, che gestirà la connessione• Il processo padre chiude la connessione e si rimette in attesa• Il processo figlio legge da socket un comando• Esegue il comando e scrive il risultato sullo stesso socket• Ripete la sequenza di gestione dei comandi finché non riceve il comando

di uscita• Chiude la connessione e termina

Page 35: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 35C.Brandolese

Esempio: Client/Server – Più connessioniEsempio: Client/Server – Più connessioni

• Il server è organizzato come segue:• Creazione del socket socket()

• Inizializzazione dell’indirizzo: • contenuto nella variabile server_addr • il tipo è struct sockaddr_in

• Binding del socket bind()

• Definizione del numero massimo di connessioni listen()

• Ciclo infinito, in cui:• Accetta una connessione e crea un figlio

accept()• Il figlio legge un comando da socket read()• Il figlio scrive un risultato sul socket write()• Il figlio terminala connessione close()• Il padre terminala connessione close()

Page 36: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 36C.Brandolese

Esempio: Client/Server – Più connessioniEsempio: Client/Server – Più connessioni

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h> 

#define MAX_CONN 5 

int main(int argc, char* argv[])

{

int sock_fd, new_sock_fd;

int client_len;

int pid;

char command;

  struct sockaddr_in server_addr;

struct sockaddr_in client_addr;

if (argc != 2) {

printf(“Numero di parametri errato\n”);

exit(1);

}

SERVER

Page 37: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 37C.Brandolese

Esempio: Client/Server – Più connessioniEsempio: Client/Server – Più connessioni

...

sock_fd = socket(AF_INET, SOCK_STREAM, 0);

server_addr.sin_family = AF_INET;

server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

server_addr.sin_port = htons((u_short) atoi(argv[1]));

 

bind( sock_fd,

(struct sockaddr *) &server_addr,

sizeof(server_addr)

);

listen(sock_fd, MAX_CONN);

 ...

SERVER

Page 38: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 38C.Brandolese

Esempio: Client/Server – Più connessioniEsempio: Client/Server – Più connessioni

while( 1 ) {

new_sock_fd = accept( sock_fd,

(struct sockaddr *) &client_addr,

&client_len

);

pid = fork();

if( pid == 0 ) {

do {

read( new_sock_fd, &command, 1 );

execute_command( new_sock_fd, command );

} while( command != 'q‘ );

close( new_sock_fd );

exit( 0 );

} else {

  close( new_sock_fd ); } }}

SERVER

child

Page 39: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 39C.Brandolese

Esempio: Client/Server – Più connessioniEsempio: Client/Server – Più connessioni

• Il server si aspetta una sequenza di comandi terminata dal carattere q

• I client gestiscono una sequenza di comandi• Nel codice seguente si mostra solamente ciò che accade dopo

l’apertura della connessione• La parte di codeice del client qui omessa è molto simile al codice

visto per la connessione singola

Page 40: Politecnico di MilanoC.Brandolese Socket Calcolatori Elettronici

Calcolatori Elettronici 40C.Brandolese

Esempio: Client/Server – Più connessioniEsempio: Client/Server – Più connessioni

#define MAXSTR 4096

char command;

int nbytes;

char buf[MAXSTR];

...

while( 1 ) {

read( 0, &command, 1 );

write( sock_fd, &command, 1 );

if( command == ‘q‘ )

break;

nbytes = read( sock_fd, buf, MAXSTR);

write( 1, buf, nbytes );

}

close( sock_fd );

}

CLIENT