type Mailbox = record Chn : Channel; Ready : Event; Busy : Event end;In questo senso, Ready stabilisce quando un canale è pronto a ricevere un nuovo messaggio, mentre Busy notifica che il canale è temporaneamente occupato dal transito di qualche messaggio. Le primitive di comunicazione sono allora facilmente implementabili nel modo seguente
procedure send( var Mbx : Mailbox; Msg : Message ); begin with Mbx do begin await( Ready ); Put( Chn, Msg ); cause( Busy ) end end; procedure send( var Mbx : Mailbox; var Msg : Message ); begin with Mbx do begin await( Busy ); Get( Chn, Msg ); cause( Ready ) end end;Questo, naturalmente, non è l'unico modo possibile di implementare la comunicazione. Lo schema così realizzato è noto anche come protocollo RS232 ed è normalmente realizzato dalle porte seriali che connettono gli elaboratori ad una periferica di tipo terminale alfanumerico (ad esempio, un terminale VT100).
Nel caso, invece, di una connessione su coassiale di tipo ethernet si utilizza un protocollo di tipo CSMA/CD, il cui acronimo significa Carrier Sense Multiple Access/Carrier Detect. Sulla rete di comunicazione viene introdotta l'intestazione del messaggio e, se viene rilevato il transito di un messaggio, la spedizione viene ritardata.
Si noti, comunque, che l'introduzione e l'estrazione dei messaggi all'interno del canale viene fatta dalle due primitive
Le socket sono definite con un tipo il cui scopo è quello di rappresentare un dominio di comunicazione, che deve intendersi come un'astrazione che viene introdotta per raggruppare le proprietà comuni a tutti i processi che comunicano con quel tipo di socket. Una di queste proprietà è lo schema utilizzato per denominare le socket.
Le socket sono tipizzate secondo le proprietà di comunicazione visibili all'utente. Si presume che i processi comunichino solamente fra socket dello stesso tipo benchè non vi sia alcuna preclusione alla comunicazione fra socket di tipo diverso purchè il sottostante protocollo di comunicazione lo permetta. Normalmente sono disponibili tre tipi di socket che prendono il nome di
Definito un dominio, vi si possono introdurre un certo numero di socket ed accedervi mediante le seguenti operazioni primitive:
procedure Send( var Mbx : Mailbox; Msg : Message ); var S : Socket; begin MakeSocket( DOMAIN, S ); Connect( S, Mbx ); Put( S, Msg ); Close( S ) end;dove il nome della socket Mbx è, in parte, determinato dalla scelta di DOMAIN, il dominio prescelto all'interno del quale viene stabilita la comunicazione. Come si intuisce facilmente, ogni qualvolta il cliente necessita di inviare un messaggio alla mailbox Mbx, viene creata temporaneamente una socket di trasmissione entro la quale viene introdotto il messaggio; successivamente la socket viene distrutta.
Per quanto riguarda, invece, l'operazione receive, questa può essere implementata nel modo seguente
procedure Receive( var S : Socket; var Msg : Message ); var P : Socket; begin Listen( S, N ); { S = socket di trasmisione } Accept( S, P ); { P = socket di comunicazione } if fork = 0 then begin Close( S ); Get( S, Msg ); Close( P ) end} end;nella quale si fa riferimento alla Mailbox con la corrispondente socket piuttosto che col suo nome globale. Questo è possibile purchè si supponga che esiste un unico server che ascolta sulla socket, cioè, i nomi globali devono considerarsi identificativi di porte. Si deve aggiungere, pertanto, la procedura StartReceive che appare nel seguito
procedure StartReceive( Mbx : Mailbox; var S : Socket ); begin MakeSocket( DOMAIN, S ); Bind( S, Mbx ) { S = socket di trasmissione } end;il cui compito è quello di attivare il punto di connessione sul quale il server esegue le accettazioni di messaggi, essendo N il loro numero massimo in attesa di ricezione.
gcc main.c hangman.c -o hangman -lsocket -lnslPer l'utilizzo del server procedere nel seguente modo.
/* * main.c * main program implementing the skeleton of the server * */ #include#include #include #include #include #include #include extern time_t time(); void play_hangman(int in, int out); /* -------------------- main() -------------------- */ main (int argc, char *argv[]) { int port, sock, fd, client_len, value; struct sockaddr_in server, client; if (argc != 2) { fprintf(stderr, "Sintassi: %s ", argv[0]); exit(4); } port = atoi(argv[1]); setbuf(stdout,NULL); /* per emettere immediatamente l'output in stdout */ srand((int)time((long *)0)); /* randomize the seed */ sock = socket(AF_INET, SOCK_STREAM, 0); /* tipo per TCP */ if (sock < 0) { perror("creating stream socket"); exit(1); } server.sin_family = AF_INET; /* per internet */ server.sin_addr.s_addr = htonl(INADDR_ANY); /* indirizzo nullo: sceglie il sistema */ server.sin_port = htons(port); /* quanto segue serve per per poter riusare piu' volte * lo stesso indirizzo * (cioe' per piu' server lanciati successivamente ) */ value = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&value, sizeof(value)) < 0) { perror("during setsockopt"); exit(5); } if (bind(sock, (struct sockaddr *) &server, sizeof(server)) < 0) { perror("binding socket"); exit(2); } listen(sock,2); /* il valore 2 indica la lunghezza coda attesa */ signal(SIGCHLD,SIG_IGN); /* vedi nota in testa */ /* il server e' implementato in modo concorrente, */ /* cioe' viene clonato ad ogni nuova richiesta */ while (1) { client_len = sizeof(client); if ((fd = accept(sock, (struct sockaddr *) &client, &client_len)) < 0) { perror("accepting connection"); exit(3); } if (fork() == 0) { /* figlio */ play_hangman(fd,fd); exit(0); /* alla fine il figlio termina (e chiude la connessione)*/ } else close(fd); /* il padre chiude la connessione */ } }
/* * hangman.c * play_hangman subroutine - implements the action of the server * Si noti che play_hangman() non sa se sta manipolando files, * socket o altri dispositivi */ #includeint maxlives = 12; char *word[] = { #include "words" }; #define NUM_OF_WORDS (sizeof(word)/sizeof(word[0])) #define MAXLEN 80 void play_hangman(int in, int out) { char *whole_word, part_word[MAXLEN], /* parola parziale */ guess[MAXLEN], outbuf[MAXLEN]; int lives = maxlives; int game_state = 'I'; /* incomplete */ int i, good_guess, word_length; char hostname[MAXLEN]; gethostname(hostname, MAXLEN); sprintf(outbuf, "Playing hangman on host %s:\n\n", hostname); write(out,outbuf,strlen(outbuf)); /* scegli una parola a caso */ whole_word = word[rand() % NUM_OF_WORDS]; word_length = strlen(whole_word); /* syslog(LOG_USER|LOG_INFO, "hangman server chose word %s", whole_word); printf("hangman server chose word %s", whole_word); /* all'inizio non ci sono lettere indovinate */ for (i=0; i < word_length; i++) part_word[i] = '-'; part_word[i]='\0'; sprintf(outbuf, " %s %d\n", part_word, lives); write(out, outbuf, strlen(outbuf)); while (game_state == 'I') { /* leggi la proposta del cliente */ while (read(in, guess, MAXLEN)<0) { if (errno != EINTR) { perror("while reading"); exit(4); /* se altro errore termina */ } printf("Restarting read\n"); /* se interrotto da un signal */ } /* aggiorna la parola parziale */ good_guess = 0; for (i=0; i < word_length; i++) { if (guess[0] == whole_word[i]) { good_guess = 1; part_word[i] = whole_word[i]; } } if (! good_guess) lives--; if (strcmp(whole_word, part_word) == 0) game_state = 'W'; /* client won */ else if (lives == 0) { game_state = 'L'; /* client lost */ /* mostra la soluzione */ strcpy(part_word, whole_word); } sprintf(outbuf, " %s %d\n", part_word, lives); write(out, outbuf, strlen(outbuf)); } }
"programmazione", "robocup", "riferimento", "distribuito", "sistema", "concorrente" "giocatore", "orologio"