Socket Application Programming Interface
Εισαγωγή Το socket είναι ένας τύπος διεπαφής μεταξύ των προγραμμάτων εφαρμογής και του λογισμικού πρωτοκόλλων Είναι ευρέως διαθέσιμο για να διασφαλίζεται η μεταφερσιμότητα των προγραμμάτων Χρησιμοποιείται από πελάτες και εξυπηρετητές Αποτελεί μια προέκταση του μηχανισμού αρχείων Ι/Ο του UNIX
API Οι αλληλεπιδράσεις των εφαρμογών με το λογισμικό πρωτοκόλλων περιλαμβάνουν: παθητική ακρόαση ή ενεργοποίηση σύνδεσης πρωτόκολλο που θα χρησιμοποιηθεί διεύθυνση IP και αριθμός πόρτας Η διεπαφή προς το λογισμικό πρωτοκόλλων ονομάζεται Application Program Interface (API) Ορίζεται προγραμματιστικά ή από το λειτουργικό σύστημα περιλαμβάνει μια συλλογή από διαδικασίες προσπελάσιμες από το πρόγραμμα εφαρμογής
Το Socket API Τα πρωτόκολλα δεν προσδιορίζουν το API To API ορίζεται προγραμματιστικά Επιτρέπει συμβατότητα εφαρμογών σε διαφορετικά υπολογιστικά συστήματα Το socket API αρχικά δημιουργήθηκε στο Berkeley BSD UNIX διαθέσιμο στα Windows, linux, etc. Προσοχή: Δεν ορίζεται σαν κάποια επιπλέον τυποποίηση του TCP/IP
Sockets και βιβλιοθήκες sockets Το BSD UNIX υλοποιεί το socket API σαν κλήσεις του λειτουργικού συστήματος Άλλοι κατασκευαστές (κυρίως UNIX συστημάτων) ακολούθησαν την ίδια λογική Διαφορετικά συστήματα έχουν διαφορετικές υλοποιήσεις 1. προσθέτοντας τα sockets σε υπάρχον λειτουργικό σύστημα απαιτεί προφανώς την αλλαγή του ΛΣ ή 2. νέες ρουτίνες βιβλιοθήκης υλοποιούν τα sockets Προστίθεται έτσι ένα επιπλέον επίπεδο λογισμικού μεταξύ των εφαρμογών και του λειτουργικού συστήματος το οποίο αυξάνει τη μεταφερσιμότητα των εφαρμογών αποκρύπτει την πραγματική υλοποίηση της διεπαφής με τα πρωτόκολλα δηλαδή το πραγματικό API
Sockets και βιβλιοθήκες sockets (2)
Sockets και UNIX I/O Τα sockets αναπτύχθηκαν σαν μια προέκταση του συστήματος εισόδου/εξόδου I/O του UNIX Χρησιμοποιούν το ίδιο σύνολο descriptor αρχείων (μικροί ακέραιοι αριθμοί) Βασίζονται στη λογική open-read-write-close open - προετοίμασε ένα αρχείο/συσκευή για πρόσβαση read/write - διάβασε/γράψε τα περιεχόμενα του αρχείου close - τερμάτισε τη χρήση του αρχείου H κλήση οpen επιστρέφει ένα file descriptor, ο οποίος χρησιμοποιείται για να προσδιορίζει το αρχείο στο διάβασμα/γράψιμο/κλείσιμο
Το Socket API O προγραμματισμός sockets είναι πολυπλοκότερος από τη διαχείριση αρχείων I/O Απαιτεί περισσότερες παραμέτρους διευθύνσεις IP αριθμοί πορτών πρωτοκόλλων τύποι πρωτοκόλλων Δύο δυνατές προσεγγίσεις Πρόσθεση νέων παραμέτρων σε υφιστάμενες κλήσεις του συστήματος για Ι/Ο Δημιουργία νέων κλήσεων συστήματος Τα sockets χρησιμοποιούν ένα νέο σύνολο κλήσεων
Περίληψη των socket system calls socket - δημιουργία ενός νέου socket close - τερματισμός της χρήσης ενός socket bind - σύνδεση μιας δνσης δικτύου και ενός socket listen - αναμονή για εισερχόμενα μηνύματα accept - αποδοχή και έναρξη χρήσης εισερχόμενης σύνδεσης connect - επιχείρηση σύνδεσης σε απομακρυσμένο Η/Υ send - αποστολή δεδομένων σε ενεργή σύνδεση recv - λήψη δεδομένων από ενεργή σύνδεση
socket descriptor = socket(protofamily, type, protocol) Επιστρέφει socket descriptor που χρησιμοποιείται σε επόμενες κλήσεις protofamily - επιλέγει την οικογένεια πρωτοκόλλων,πχ: PF_INET - πρωτόκολλα διαδικτύου PF_APPLETALK - πρωτόκολλα AppleTalk type - επιλέγει τον τύπο της επικοινωνίας SOCK_DGRAM - χωρίς σύνδεση SOCK_STREAM - με σύνδεση protocol - προσδιορίζει το πρωτόκολλο μέσα στην οικογένεια πρωτοκόλλων IPPROTO_TCP - επιλέγει το TCP IPPROTO_UDP - επιλέγει το UDP μπορεί ένας τύπος επικοινωνίας να έχει δύο πρωτόκολλα
close close(descriptor) κλείνει τη σύνδεση τερματίζει τη χρήση ενός socket descriptor descriptor - πρόκειται για τον descriptor του socket που θα τερματιστεί
bind bind(socket,localaddr,addresslen) Αρχικά ένα socket στον εξυπηρέτη δεν αντιστοιχεί σε κάποια διεύθυνση (IP addr) και υπηρεσία (port) bind - επιλέγει μια τοπική διεύθυνση στον εξυπηρέτη και την τοπική πόρτα της υπηρεσίας και τις αντιστοιχίζει στο socket
Τύποι διευθύνσεων sockets Επειδή τα sockets μπορούν χρησιμοποιηθούν από διάφορα πρωτόκολλα ο τύπος της δνσης είναι γενικός: struct sockaddr { u_char sa_len; /* total length of address */ u_char sa_family; /* family of the address */ char sa_data[14]; /* address */ } Για τα πρωτόκολλα TCP/IP, το sa_data περιέχει τη δνση IP address και την πόρτα: struct sockaddr_in { u_char sin_len; /* total length of address */ u_char sin_family; /* family of the address */ u_short sin_port; /* protocol port number */ struct in_addr sin_addr; /* IP address */ char sin_zero[8] /* unused */ } Τα πρώτα δύο πεδία ταιριάζουν στη γενική δομή sockaddr Τα υπόλοιπα συναντώνται μόνο στα πρωτόκολλα IP Η σταθερά INADDR_ANY στη θέση της δνσης IP μεταφράζεται σαν οιαδήποτε IP δνση (multihomed Η/Υ)
Δομή sockaddr Δομή sockaddr_in (TCP/IP)
listen listen(socket, queuesize) Ο εξυπηρετητής χρησιμοποιεί τη listen για την αναμονή εισερχομένων συνδέσεων socket - προσδιορίζει το socket μέσω του οποίου θα καταφθάνουν οι αιτήσεις Νέες αιτήσεις μπορεί να καταφθάνουν καθώς ο εξυπηρετητής σερβίρει προηγούμενη αίτηση Το λειτουργικό κρατά τις αιτήσεις σε μια ουρά queuesize - θέτει ένα άνω όριο για τις αναμένουσες αιτήσεις - επιπλέον αιτήσεις χάνονται
accept newsock=accept(sock,caddress,caddresslen) Ο εξυπηρετητής χρησιμοποιεί την accept για να αποδεχθεί την επόμενη αίτηση σύνδεσης Η accept μπλοκάρει σε άδεια ουρά εισερχόμενων αιτήσεων και αναμένει μέχρι να φθάσει μια νέα σύνδεση Επιστρέφει ένα νέο socket που αντιστοιχεί στο νέο ακραίο σημείο πρόσβασης (στον εξυπηρετητή slave αν υπάρχει) της νέας σύνδεσης Το παλαιό socket παραμένει ανέπαφο και ο εξυπηρετητής (master αν υπάρχει) συνεχίζει να ακούει εισερχόμενες κλήσεις σε αυτό caddress - επιστρέφει τη δομή sockaddr με τη διεύθυνση του πελάτη; ο τύπος της δνσης εξαρτάται από την οικογένεια πρωτοκόλλων του socket caddresslen - επιστρέφει το μήκος της δνσης
connect connect(socket, saddress, saddresslen) Ο πελάτης χρησιμοποιεί την connect για να εγκαταστήσει μια σύνδεση με έναν εξυπηρετητή Μπλοκάρει μέχρι η σύνδεση να γίνει αποδεκτή socket - κρατά τον descriptor του socket που θα χρησιμοποιηθεί saddress - δομή sockaddr που προσδιορίζει τον εξυπηρετητή saddresslen - δίνει το μήκος της saddress Η connect συνήθως χρησιμοποιείται σε πρωτόκολλα με σύνδεση Χρησιμοποιείται και σε πρωτόκολλα χωρίς σύνδεση Συνδέει το τοπικό socket με τη δνση του εξυπηρετητή Εμμέσως προσδιορίζει/καταγράφει τον εξυπηρετητή για ενδεχόμενα επόμενα μηνύματα αποστολής σε αυτόν
send send(socket,data,length,flags) Χρησιμοποιείται για την αποστολή δεδομένων σε ένα ήδη συνδεδεμένο socket socket - descriptor που προσδιορίζει το socket data - δείχνει στα δεδομένα προς αποστολή length - το μήκος της περιοχής των δεδομένων (σε bytes) flags - προσδιορίζουν ειδικές επιλογές
Χρησιμοποιείται με μή συνδεδεμένα sockets προσδιορίζοντας ξεκάθαρα τον παραλήπτη destaddress - η δομή sockaddr της δνσης προορισμού addresslen - το μήκος της destaddress sendmsg - συνδυάζει μια λίστα παραμέτρων σε μια μόνο δομή: struct msgstruct { struct sockaddr *m_addr; /*ptr to destination address */ struct datavec *m_vec; /*pointer to message vector */ int m_dvlength; /*num. of items in vector */ struct access *m_rights;/* ptr to access rights list */ int m_alength; /* num. of items in list */ } sendto-sendmsg sendto(socket,data,length,flags,destaddress,addresslen) sendmsg(socket, msgstruct, flags)
recv len=recv(socket,buffer,length,flags) Χρησιμοποιείται για τη λήψη εισερχομενων δεδομένων μέσω κάποιου συνδεδεμένου socket socket - o descriptor που προσδιορίζει το socket Τα δεδομένα αντιγράφονται στην περιοχή που δείχνει ο buffer Το πολύ length bytes θα ληφθούν flags - δίνει ειδικές επιλογές options Επιστρέφει το πλήθος των bytes που πραγματικά ελήφθησαν 0 υπονοεί ότι η σύνδεση έκλεισε (υπονοεί λήψη EOF) -1 υπονοεί κάποιο λάθος
recvfrom - recvmsg recvfrom(socket,buffer,length,flags,sndraddress,addresslen) recvmsg(socket, msgstruct, flags) Οπως οι sendto και sendmsg για μη συνδεδεμένα sockets Η διεύθυνση του αποστολέα αντιγράφεται στη δομή sndraddress Το μήκος της δνσης δίνεται στο addresslen recvmsg - χρησιμοποιεί τη δομή msgstruct για όλες τις παραμέτρους
Χρήση κλήσεων sockets CLIENT SIDE socket connect send recv close SERVER SIDE socket bind listen accept recv send close
Αλλες βοηθητικές ρουτίνες gethostname - πληροφορίες για τον Η/Υ όπου εκτελείται getpeername - δίνει τη δνση του άλλου ακραίου σημείου της σύνδεσης gethostbyname - το DNS μεταφράζει όνομα σε IP δνση gethostbyaddr - το DNS μεταφράζει IP δνση σε όνομα getsockname - δίνει την τρέχουσα δνση που αντιστοιχεί σε κάποιο socket setsockopt - θέτει τις επιλογές για το socket (πχ μέγεθος buffer του πρωτοκόλλου - διαπραγμάτευση εφαρμογών) getsockopt - διαβάζει τις επιλογές για το socket
Διάταξη των bytes στο δίκτυο Σε μια ροή bytes ποιο είναι το πιο σημαντικό και ποιο το λιγότερο σημαντικό; Η διάταξη των οκτάδων στο δίκτυο μπορεί να διαφέρει από τη διάταξή τους σε κάποιο υπολογιστικό σύστημα Στο διαδίκτυο το πιο σημαντικό byte είναι το πρώτο στη σειρά (αυτό που φθάνει πρώτο) Απαιτούνται ρουτίνες μετατροπής ntohs : μετατροπή από network σε host αναπαράσταση μιας τιμής των 2 bytes (htons το αντίστροφο) ntohl : μετατροπή από host σε network αναπαράσταση μιας τιμής των 4 bytes (htonl το αντίστροφο) Προφανώς a=htons(ntohs(a)) και b=htonl(ntohl(b))
Sockets και διαδικασίες Οπως οι file descriptors, έτσι και τα sockets κληρονομούνται από τις διαδικασίες παιδιά Το socket ελευθερώνεται όταν όλες οι διαδικασίες που το έχουν ανοικτό το κλείσουν με close Οι εξυπηρετητές master χρησιμοποιούν την κληρονομικότητα των sockets για να περνούν νέες εισερχόμενες συνδέσεις σε αντίστοιχες διαδικασίες εξυπηρετητών slave
Παράδειγμα πελάτη - εξυπηρετητή
Επικοινωνία με σύνδεση Ο προγραμματιστής client/server εφαρμογών πρέπει να επιλέξει μεταξύ της επικοινωνίας με ή/και χωρίς σύνδεση Το παράδειγμα χρησιμοποιεί υπηρεσία μεταφοράς με σύνδεση Ο εξυπηρετητής καλεί το τοπικό λογισμικό πρωτοκόλλων για να αρχίσει να δέχεται εισερχόμενες συνδέσεις Ο πελάτης εγκαθιστά τη σύνδεση με τον εξυπηρετητή καλώντας το δικό του τοπικό λογισμικό πρωτοκόλλων Η ανταλλαγή δεδομένων γίνεται αφού η εγκατασταθεί σύνδεση
Υπηρεσία παραδείγματος Εξυπηρετητής καταγράφει το πλήθος των επαφών που του έχουν γίνει επιστρέφει τον αριθμό των επαφών σε ένα ASCII string Πελάτης Εγκαθιστά μια σύνδεση Περιμένει για ένα μήνυμα από τον εξυπηρετητή (ASCII string) Τυπώνει τα δεδομένα του μηνύματος Πρωτόκολλο επιπέδου εφαρμογής: Συντακτικό μηνυμάτων (syntax): Χωρίς επικεφαλίδες (επιπέδου εφαρμογής εννοείται) Ο πελάτης στέλνει μήνυμα χωρίς σώμα μηνύματος Το σώμα μηνύματος του εξυπηρέτη περιέχει το ASCII string Σημασιολογία μηνυμάτων (Semantics): ο πελάτης εγκαθιστά σύνδεση και ο εξυπηρετητής επιστρέφει ένα ASCII string
Προγράμματα παραδείγματος Εξυπηρετητής (server.c) Τρέχει σαν πρόγραμμα στο background Δέχεται εναλλακτικά και ένα όρισμα: τον αριθμό της πόρτας στην οποία θα ακούει (η default πόρτα είναι 5193) Πελάτης (client.c) Τρέχει σαν πρόγραμμα χρήστη στο foreground Δέχεται εναλλακτικά δύο ορίσματα: όνομα του Η/Υ που θα κληθεί (η default τιμή είναι localhost) τον αριθμό πόρτας της υπηρεσίας (που ακούει ο εξυπηρετητής)
/* client.c - code for example client program that uses TCP */ #ifndef unix #define WIN32 #include <windows.h> #include <winsock.h> #else #define closesocket close #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #endif #include <stdio.h> #include <string.h> #define PROTOPORT 5193 /* default protocol port number */ extern int errno; char localhost[] = "localhost"; /* default host name */ /*------------------------------------------------------------------------ * Program: client * * Purpose: allocate a socket, connect to a server, and print all output * * Syntax: client [ host [port] ] * * host - name of a computer on which server is executing * port - protocol port number server is using * * Note: Both arguments are optional. If no host name is specified, * the client uses "localhost"; if no protocol port is * specified, the client uses the default given by PROTOPORT. * *------------------------------------------------------------------------ */
main(argc, argv) int argc; char *argv[]; { struct hostent *ptrh; /* pointer to a host table entry */ struct protoent *ptrp; /* pointer to a protocol table entry */ struct sockaddr_in sad; /* structure to hold an IP address */ int sd; /* socket descriptor */ int port; /* protocol port number */ char *host; /* pointer to host name */ int n; /* number of characters read */ char buf[1000]; /* buffer for data from the server */ #ifdef WIN32 WSADATA wsadata; WSAStartup(0x0101, &wsadata); #endif memset((char *)&sad,0,sizeof(sad)); /* clear sockaddr structure */ sad.sin_family = AF_INET; /* set family to Internet */ /* Check command-line argument for protocol port and extract */ /* port number if one is specified. Otherwise, use the default */ /* port value given by constant PROTOPORT */ if (argc > 2) { /* if protocol port specified */ port = atoi(argv[2]); /* convert to binary */
} else { port = PROTOPORT; /* use default port number */ } if (port > 0) /* test for legal value */ sad.sin_port = htons((u_short)port); else { /* print error message and exit */ fprintf(stderr,"bad port number %s\n",argv[2]); exit(1); } /* Check host argument and assign host name. */ if (argc > 1) { host = argv[1]; /* if host argument specified */ } else { host = localhost; } /* Convert host name to equivalent IP address and copy to sad. */ ptrh = gethostbyname(host); if ( ((char *)ptrh) == NULL ) { fprintf(stderr,"invalid host: %s\n", host); exit(1); } memcpy(&sad.sin_addr, ptrh->h_addr, ptrh->h_length); /* Map TCP transport protocol name to protocol number. */ if ( ((int)(ptrp = getprotobyname("tcp"))) == 0) { fprintf(stderr, "cannot map \"tcp\" to protocol number"); exit(1); }
/* Create a socket. */ sd = socket(pf_inet, SOCK_STREAM, ptrp->p_proto); if (sd < 0) { fprintf(stderr, "socket creation failed\n"); exit(1); } /* Connect the socket to the specified server. */ if (connect(sd, (struct sockaddr *)&sad, sizeof(sad)) < 0) { fprintf(stderr,"connect failed\n"); exit(1); } /* Repeatedly read data from socket and write to user's screen. */ n = recv(sd, buf, sizeof(buf), 0); while (n > 0) { write(1,buf,n); n = recv(sd, buf, sizeof(buf), 0); } /* Close the socket. */ closesocket(sd); /* Terminate the client program gracefully. */ } exit(0);
/* server.c - code for example server program that uses TCP */ #ifndef unix #define WIN32 #include <windows.h> #include <winsock.h> #else #define closesocket close #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #endif int visits = 0; /* counts client connections */ /*------------------------------------------------------------------------ * Program: server * * Purpose: allocate a socket and then repeatedly execute the following: * (1) wait for the next connection from a client * (2) send a short message to the client * (3) close the connection * (4) go back to step (1) * * Syntax: server [ port ] * * port - protocol port number to use * * Note: The port argument is optional. If no port is specified, * the server uses the default given by PROTOPORT. * *------------------------------------------------------------------------ */ #include <stdio.h> #include <string.h> #define PROTOPORT 5193 /* default protocol port number */ #define QLEN 6 /* size of request queue */
main(argc, argv) int argc; char *argv[]; { struct hostent *ptrh; /* pointer to a host table entry */ struct protoent *ptrp; /* pointer to a protocol table entry */ struct sockaddr_in sad; /* structure to hold server's address */ struct sockaddr_in cad; /* structure to hold client's address */ int sd, sd2; /* socket descriptors */ int port; /* protocol port number */ int alen; /* length of address */ char buf[1000]; /* buffer for string the server sends */ #ifdef WIN32 WSADATA wsadata; WSAStartup(0x0101, &wsadata); #endif memset((char *)&sad,0,sizeof(sad)); /* clear sockaddr structure */ sad.sin_family = AF_INET; /* set family to Internet */ sad.sin_addr.s_addr = INADDR_ANY; /* set the local IP address */ /* Check command-line argument for protocol port and extract */ /* port number if one is specified. Otherwise, use the default */ /* port value given by constant PROTOPORT */ if (argc > 1) { /* if argument specified */
} else { port = PROTOPORT; /* use default port number */ } if (port > 0) /* test for illegal value */ sad.sin_port = htons((u_short)port); else { /* print error message and exit */ fprintf(stderr,"bad port number %s\n",argv[1]); exit(1); } /* Map TCP transport protocol name to protocol number */ if ( ((int)(ptrp = getprotobyname("tcp"))) == 0) { fprintf(stderr, "cannot map \"tcp\" to protocol number"); exit(1); } /* Create a socket */ sd = socket(pf_inet, SOCK_STREAM, ptrp->p_proto); if (sd < 0) { fprintf(stderr, "socket creation failed\n"); exit(1); } /* Bind a local address and port to the socket */ if (bind(sd, (struct sockaddr *)&sad, sizeof(sad)) < 0) { fprintf(stderr,"bind failed\n"); exit(1); }
/* Specify size of request queue */ if (listen(sd, QLEN) < 0) { fprintf(stderr,"listen failed\n"); exit(1); } /* Main server loop - accept and handle requests */ } while (1) { alen = sizeof(cad); if ( (sd2=accept(sd, (struct sockaddr *)&cad, &alen)) < 0) { fprintf(stderr, "accept failed\n"); exit(1); } visits++; sprintf(buf,"this server has been contacted %d time%s\n", visits,visits==1?".":"s."); send(sd2,buf,strlen(buf),0); closesocket(sd2); }
Αρχιτεκτονική προγραμμάτων Ακολουθία κλήσεων σε πελάτη και εξυπηρετητή
Εξυπηρετητής Αρχικοποίηση: getprotobyname - αναζητά τον αριθμό πρωτοκόλλου για το TCP socket - δημιουργεί ένα socket bind - συσχετίζει ένα socket με local IP και port listen - συσχετίζει ένα socket με πλήθος εισερχόμενων αιτήσεων Loop: accept - αποδέχεται εισερχόμενη αίτηση send - στέλνει μήνυμα στον πελάτη close - κλείνει τη σύνδεση socket
Πελάτης Αρχικοποίηση: gethostbyname - αναζητά τον εξυπηρετητή getprotobyname - αναζητά τον αριθμό πρωτοκόλλου για το TCP socket - δημιουργεί ένα socket connect - συσχετίζει ένα socket και συνδέεται με εξυπηρετητή σε κάποια απομακρυσμένη IP και port που αυτός ο εξυπηρετητής ακούει Loop: recv - λαμβάνει μήνυμα από τον εξυπηρετητή Τερματισμός: close - τερματίζει το socket
ΠΡΟΣΟΧΗ: Κλήσεις recv του πελάτη Γιατί η κλήση στη recv (στον πελάτη) είναι σε ένα loop αφού ο εξυπηρετητής στέλνει μόνο ένα μήνυμα; Aπάντηση: Το λογισμικό πρωτοκόλλων δεν εγγυάται την παράδοση των δεδομένων στα ίδια blocks όπως αυτά δημιουργήθηκαν από τον εξυπηρετητή Μπορεί να ταξιδεύουν σε διαφορετικά TCP segments H recv δεν χρειάζεται να επιστρέψει τόσα δεδομένα όσα της ζητήθηκαν Ο πελάτης πρέπει να κάνει επανειλημένες κλήσεις στην κλήση recv μέχρι αυτή να επιστρέψει 0(EOF)
Μπλοκάρισμα των κλήσεων sockets Οι περισσότερες κλήσεις socket μπλοκάρουν την καλούσα διαδικασία μέχρι η λειτουργία του socket ολοκληρωθεί (προφανώς αποσύρονται από τη λίστα των έτοιμων προς εκτέλεση διαδικασιών) Ο εξυπηρετητής μπλοκάρει: στην accept μέχρι να φθάσει μια νέα σύνδεση στη send μέχρι να παραδοθούν τα δεδομένα στο τοπικό λογισμικό πρωτοκόλλων Ο πελάτης μπλοκάρει: στη gethostbyname μέχρι να επιλυθεί το όνομα του εξυπηρετητή από το DNS στη recv μέχρι να παραδοθεί ένα μήνυμα από τον εξυπηρετητή
Χρήση διαφορετικού εξυπηρετητή (πελάτη) με τον δεδομένο πελάτη (εξυπηρετητή) Αλλες υπηρεσίες χρησιμοποιούν παρόμοια πρωτόκολλα εφαρμογής: DAYTIME CHARGEN Προσδιορίζοντας άλλη πόρτα στον πελάτη αυτός συνδέεται σε διαφορετική υπηρεσία (με άλλα λόγια σε διαφορετικό εξυπηρετητή) Παρομοίως κάποιος διαφορετικός πελάτης που χρησιμοποιεί το ίδιο πρωτόκολλο εφαρμογής μπορεί να χρησιμοποιηθεί για τον έλεγχο του εξυπηρετητή (πχ πελάτης telnet)
Επιπλέον θέματα σχεδίασης και υλοποίησης λογισμικού πελάτη - εξυπηρετητή
Αναζήτηση ενός domain name struct hostent{ char *h_name; /* official host name */ char **h_aliases; /* other aliases */ int h_addrtype; /* address type */ int h_length; /* address length */ int **h_addr_list; /* list of addresses */ } #define h_addr h_addr_list[0] struct hostent *hptr; hptr=gethostbyname( www.unipi.gr ) hptr->h_addr: περιέχει τη δνση IP σε δυαδική μορφή Inet_addr: παίρνει σαν είσοδο ένα ASCII string με την δεκαδική αναπαράσταση της ΙΡ δνσης και επιστρέφει τη δυαδική μορφή της
Αναζήτηση γνωστής υπηρεσίας struct servent{ char *s_name; /* official service name */ char **s_aliases; /* other aliases */ int s_port; /* port for this service */ int s_proto; /* protocol to use */ } struct servent *sptr; sptr=getservbyname( smpt, tcp ) sptr->s_pοrt: περιέχει τον αριθμό πόρτας
Αναζήτηση πρωτοκόλλου struct protoent{ char *p_name; /* official protocol name */ char **p_aliases; /* list of aliases allowed */ int p_proto; /* official protocol number */ } struct protoent *pptr; pptr=getprotobyname( udp ) pptr->p_proto: περιέχει τον αριθμό πρωτοκόλλου
Βιβλιοθήκη για σχεδιασμό και υλοποίηση λογισμικού πελατών socket = connecttcp(machine, service); contcp.cpp socket = connectudp(machine, service); conudp.cpp socket = connectsock(host, service, transport); consock.cpp errexit(format,...); errexit.cpp
Παραδείγματα πελατών Πελάτης TCP υπηρεσίας DAYTIME TCPdtc.cpp Πελάτης UDP υπηρεσίας TIME UDPtime.cpp Πελάτης TCP υπηρεσίας ECHO TCPecho.cpp Πελάτης UDP υπηρεσίας ECHO UDPecho.cpp
Βιβλιοθήκη για σχεδιασμό και υλοποίηση λογισμικού εξυπηρετητών socket = passivetcp(service); passtcp.cpp socket = passiveudp(service); passudp.cpp socket = passivesock(service, transport, qlen); passsock.cpp
Είδη εξυπηρετητών Ακολουθιακός χωρίς σύνδεση συνήθης ειδικά για υπηρεσίες με μικρή επεξεργασία Παράλληλος χωρίς σύνδεση λιγότερο συνήθης και πρέπει ο χρόνος δημιουργίας του νέου thread να είναι σημαντικά μικρότερος της απαιτούμενης επεξεργασίας Ακολουθιακός με σύνδεση λιγότερο συνήθης, για υπηρεσίες με σύνδεση που απαιτούν πολύ μικρή επεξεργασία Παράλληλος με σύνδεση συνήθης για υπηρεσίες με σύνδεση δύο είδη πολλαπλά threads που ακούν σε ένα socket το καθένα ένα thread που ακούει σε πολλά sockets ταυτόχρονα
Ακολουθιακός χωρίς σύνδεση server Server application process Socket σε γνωστή πόρτα Operating system Παράδειγμα : UDPtimed.cpp
Ακολουθιακός με σύνδεση server Server application process Socket σε γνωστή πόρτα για αποδοχή αιτήσεων Socket για κάθε ξεχωριστή αίτηση Operating system Παράδειγμα : TCPdtd.cpp
Παράλληλος με σύνδεση (Υλοποίηση πολλαπλών threads) Master Server Socket σε γνωστή πόρτα για αποδοχή αιτήσεων Slave 1 Slave 2 Slave n... Sockets για κάθε ξεχωριστή αίτηση Server application threads Operating system Παράδειγμα : TCPechod.cpp
Παράλληλος με σύνδεση (Υλοποίηση ενός thread) Server Socket σε γνωστή πόρτα για αποδοχή αιτήσεων... Sockets για κάθε ξεχωριστή αίτηση Server application process Operating system Παράδειγμα : TCPmechd.cpp Προσοχή : κλήση select()
Παράδειγμα παράλληλου προγράμματος Πρόγραμμα που υπολογίζει παράλληλα (δύο threads) το ίδιο άθροισμα κάποιων αριθμών: consum.cpp Πιθανό αναμενόμενο αποτέλεσμα: consum.out