Κατέβασμα παρουσίασης
Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε
1
Κεφάλαιο 11.2 Sockets
2
Το μοντέλο Client - Server
Η διεργασία - εξυπηρετητής αρχίζει να εκτελείται σε κάποιον υπολογιστή. Μετά την αρχικοποίησή της, πέφτει σε “λήθαργο”, αναμένοντας μία διεργασία - πελάτη να επικοινωνήσει μαζί της και να της ζητήσει κάποια υπηρεσία. Μία διεργασία - πελάτης αρχίσει να εκτελείται, είτε στο ίδιο σύστημα, είτε σε κάποιο απομακρυσμένο, το οποίο συνδέεται με τον υπολογιστή στον οποίο “τρέχει” ο εξυπηρετητής μέσω δικτύου. Ως διεργασία (process) σε ένα υπολογιστή ονομάζεται ένα πρόγραμμα κατά την διάρκεια που εκτελείται. Ένας υπολογιστής μπορεί να εκτελεί πολλά προγράμματα μαζί, την ίδια χρονική στιγμή, μοιράζοντας τον χρόνο εκτέλεσης του επεξεργαστή τους (CPU) μεταξύ αυτών των προγραμμάτων. Το ευρύτερα διαδεδομένο μοντέλο ανάπτυξης δικτυακών εφαρμογών (δηλαδή εφαρμογών που εκτελούνται σε ξεχωριστούς υπολογιστές και ανταλλάσσουν μηνύματα μεταξύ τους μέσω δικτύου) είναι το μοντέλο του πελάτη - εξυπηρετητή (client - server). Μια διεργασία που την χαρακτηρίζουμε ως εξυπηρετητή αρχίζει να εκτελείται σε κάποιον υπολογιστή. Μετά την αρχικοποίησή της, πέφτει σε “λήθαργο”, αναμένοντας μία διεργασία - πελάτη να επικοινωνήσει μαζί της και να της ζητήσει κάποια υπηρεσία. Μία διεργασία - πελάτης αρχίσει να εκτελείται, είτε στον ίδιο υπολογιστή, είτε σε κάποιο απομακρυσμένο υπολογιστή. Η διεργασία πελάτης επικοινωνεί με τον υπολογιστή στον οποίο “τρέχει” ο εξυπηρετητής μέσω δικτύου.
3
Το μοντέλο Client - Server
Η διεργασία πελάτης στέλνει μια αίτηση, μέσω του δικτύου, στον εξυπηρετητή, ζητώντας του κάποιου είδους υπηρεσία (π.χ. μεταφορά αρχείου, απομακρυσμένη εκτύπωση, ανάγνωση και αποστολή mail και άλλες). Ταυτόχρονα με την εξυπηρέτηση κάποιου πελάτη, ο server έχει την δυνατότητα να δέχεται και αιτήσεις άλλων πελατών προς εξυπηρέτηση. Όταν ο εξυπηρετητής τελειώσει με όλους τους πελάτες, τότε ξαναπέφτει σε “λήθαργο”, περιμένοντας για μια καινούργια αίτηση και η διαδικασία ξαναρχίζει από την αρχή. Ορίζουμε ως σύνδεση, τον επικοινωνιακό δίαυλο μεταξύ δύο διεργασιών. Η διεργασία πελάτης στέλνει μια αίτηση, μέσω του δικτύου, στον εξυπηρετητή, ζητώντας του κάποιου είδους υπηρεσία (π.χ. μεταφορά αρχείου, απομακρυσμένη εκτύπωση, ανάγνωση και αποστολή mail και άλλες). Ταυτόχρονα με την εξυπηρέτηση κάποιου πελάτη, ο server έχει την δυνατότητα να δέχεται και αιτήσεις άλλων πελατών προς εξυπηρέτηση. Όταν ο εξυπηρετητής τελειώσει με όλους τους πελάτες, τότε ξαναπέφτει σε “λήθαργο”, περιμένοντας για μια καινούργια αίτηση και η διαδικασία ξαναρχίζει από την αρχή. Ορίζουμε ως σύνδεση, την επικοινωνιακή σύνδεση (επικοινωνιακό δίαυλο) μεταξύ δύο διεργασιών.
4
Sockets Socket είναι το ένα άκρο, από έναν επικοινωνιακό δίαυλο διπλής κατεύθυνσης, μεταξύ δύο προγραμμάτων που εκτελούνται στο δίκτυο. Περιλαμβάνει το πρωτόκολλο, την διεύθυνση και τον αριθμό θύρας του άκρου. Κάθε πρόγραμμα διαβάζει από και γράφει σε ένα socket, με τρόπο παρόμοιο της εγγραφής και ανάγνωσης αρχείων του file system. Υπάρχουν δύο είδη sockets: TCP - UDP Ως Socket ορίζεται το ένα άκρο, ενός επικοινωνιακού δίαυλου διπλής κατεύθυνσης, μεταξύ δύο προγραμμάτων που εκτελούνται. Περιλαμβάνει το πρωτόκολλο, την διεύθυνση και τον αριθμό θύρας του άκρου. Κάθε πρόγραμμα διαβάζει από και γράφει σε ένα socket, με τρόπο παρόμοιο της εγγραφής και ανάγνωσης αρχείων του file system. Υπάρχουν δύο είδη sockets: TCP - UDP
5
TCP Sockets To TCP (Transmission Control Protocol) socket είναι υπηρεσία προσανατολισμένη στην σύνδεση (connection-oriented service). Κατά την αρχικοποίηση έχουμε την εγκαθίδρυση μιας σύνδεσης μεταξύ δύο διεργασιών. H σύνδεση με TCP socket απαιτεί την ανταλλαγή τριών “πακέτων χειραψίας” (handshake packets) και είναι πιο χρονοβόρα στην αρχικοποίησή της από την αντίστοιχη με UDP datagrams. Τα TCP sockets εξασφαλίζουν μια αξιόπιστη μεταφορά της πληροφορίας : ότι αποστέλλεται από το ένα άκρο είναι σίγουρο ότι θα φτάσει στο άλλο. Tο TCP ( Transmission Control Protocol ) socket είναι μια υπηρεσία προσανατολισμένη στην σύνδεση ( connection-oriented service ). Μπορούμε να το θεωρήσουμε ανάλογο της τηλεφωνικής υπηρεσίας, στην οποία, μετά την εγκαθίδρυση μιας σύνδεσης μεταξύ δύο συνομιλητών, η οποία χρησιμοποιείται μέχρι το πέρας της συζητήσεως τους. To TCP socket είναι υπηρεσία προσανατολισμένη στην σύνδεση (connection-oriented service). Κατά την αρχικοποίηση έχουμε την εγκαθίδρυση μιας σύνδεσης μεταξύ δύο διεργασιών. H σύνδεση με TCP socket απαιτεί την ανταλλαγή τριών “πακέτων χειραψίας” (handshake packets) και είναι πιο χρονοβόρα στην αρχικοποίησή της από την αντίστοιχη με UDP datagrams. Τα TCP sockets εξασφαλίζουν μια αξιόπιστη μεταφορά της πληροφορίας : ότι αποστέλλεται από το ένα άκρο είναι σίγουρο ότι θα φτάσει στο άλλο.
6
UDP Sockets To UDP (Unreliable Datagram Protocol ) socket είναι μια υπηρεσία χωρίς σύνδεση ( connectionless service ). Κατά την αρχικοποίηση δεν έχουμε την εγκαθίδρυση μιας σύνδεσης μεταξύ δύο διεργασιών. Ότι αποστέλλεται από το ένα άκρο δεν είναι σίγουρο ότι θα φτάσει στο άλλο. Είναι στην ευθύνη του αποστολέα να ελέγξει ότι αυτό που έστειλε, το έλαβε τελικά ο παραλήπτης και δεν χάθηκε στον δρόμο. Οι προηγούμενες δύο διαφορές καθορίζουν τελικά και την χρήση των δύο αυτών ειδών. Το δεύτερο είδος socket ονομάζεται UDP (Unreliable Datagram Protocol ) socket. To UDP socket είναι μια υπηρεσία χωρίς σύνδεση ( connectionless service ). To ανάλογο, σε αυτήν την περίπτωση, είναι το ταχυδρομείο : μπορούμε να στείλουμε πολλά γράμματα (μηνύματα) στον ίδιο παραλήπτη, αλλά δεν είναι σίγουρο ότι όλα θα ακολουθήσουν την ίδια διαδρομή (σύνδεση ) για να φτάσουν στον προορισμό τους. Κατά την αρχικοποίηση δεν έχουμε την εγκαθίδρυση μιας σύνδεσης μεταξύ δύο διεργασιών. Ότι αποστέλλεται από το ένα άκρο δεν είναι σίγουρο ότι θα φτάσει στο άλλο. Είναι στην ευθύνη του αποστολέα να ελέγξει ότι αυτό που έστειλε, το έλαβε τελικά ο παραλήπτης και δεν χάθηκε στον δρόμο. Οι προηγούμενες δύο διαφορές καθορίζουν τελικά και την χρήση των δύο αυτών ειδών.
7
Java.net package Για την αποφυγή σύγχυσης, να σημειώσουμε ότι ειδικά στην Java, ο όρος Socket χρησιμοποιείται για τα TCP sockets, στην ονοματολογία των κλάσεων και των μεθόδων. Για την δήλωση των UDP sockets, χρησιμοποιείται ο όρος Datagram. Στον πίνακα της διαφάνειας βλέπουμε τις διάφορες κλάσεις για τα sockets που περιέχονται στο πακέτο java.net καθώς και το είδος τους.
8
Client - Server με TCP O πελάτης ( EchoClient παράδειγμα)
socket() - αρχικοποίηση και σύνδεση στον server getInputStream() - σύνδεση του socket με τον μηχανισμό εισόδου/εξόδου getOutputStream() read() και write() - ανάγνωση και εγγραφή στο συνδεδεμένο με το socket stream ακολουθούν και άλλες αναγνώσεις/εγγραφές close() - κλείνει το socket και απελευθερώνεται αντίστοιχος πόρος του συστήματος. Μία από τις κλασικές υπηρεσίες που προσφέρονται στο δίκτυο, είναι η υπηρεσία αντήχησης ( echo service). Αυτή χρησιμοποιεί για την σύνδεση TCP sockets και αριθμό θύρας 7, ενώ σκοπός της είναι να μεταδώσει πίσω στον πελάτη, που θα ζητήσει τέτοια υπηρεσία, ό,τι αυτός της στείλει. Το πρόγραμμα του Server δεν επεξεργάζεται τα μηνύματα που λαμβάνει από τους πελάτες. Απλά κάθε μήνυμα που λαμβάνει το στέλνει πάλι πίσω στον πελάτη από τον οποίο το έλαβε. Μία τέτοια εφαρμογή θα αναπτύξουμε εδώ σαν παράδειγμα τόσο το πρόγραμμα της πλευράς του πελάτη όσο και αυτό της πλευράς του εξυπηρετητή (προγράμματα EchoClient.java και EchoServer.java). Αυτά τα δυο προγράμματα χρησιμοποιούν ως αριθμό θύρας, τον 8205 αντί της θύρας 7. Μια τυπική αλληλουχία εντολών που συνήθως ακολουθούνται σε προγράμματα τύπου client είναι οι εξής δηλώσεις: 1) Socket() : κάνει αρχικοποίηση και σύνδεση στον server 2) getInputStream() και getOutputStream() : συνδέει το socket με τον στάνταρ μηχανισμό εισόδου/εξόδου στη Java, δηλ. τα streams 3) read() και write() : για ανάγνωση και εγγραφή στο - συνδεδεμένο με το socket - stream 4) ακολουθούν και άλλες αναγνώσεις/εγγραφές 5) close() : με αυτή την εντολή κλείνει το socket και απελευθερώνεται ο αντίστοιχος πόρος του συστήματος. Το πρόγραμμα EchoClient.java καθώς και αναλυτική επεξήγηση των εντολών του βρίσκεται στις σελίδες των σημειώσεων και συστήνεται να αναγνωσθούν με ιδιαίτερη προσοχή.
9
Client - Server με TCP O εξυπηρετητής (EchoServer παράδειγμα)
ServerSocket() - αρχικοποίηση του listening socket accept() - αναμονή και εντοπισμός καινούργιου πελάτη new Thread - δημιουργία καινούργιου thread για την εξυπηρέτηση πελάτη getInputStream() - το καινούργιο socket που επιστρέφεται από την accept (τύπου Socket) συνδέεται με τον μηχανισμό εισόδου/εξόδου getOutputStream() Μια τυπική TCP εφαρμογή εξυπηρετητή ανοίγει ένα “καλά - γνωστό/διαδεδομένο” port για την λήψη αιτήσεων για σύνδεση και ύστερα δημιουργεί μία διεργασία-παιδί, ή ένα ξεχωριστό νήμα εκτέλεσης για να εκτελέσει την υπηρεσία. Το port που ανοίγει ο server ονομάζεται “καλά-γνωστό”, γιατί αυτό χρησιμοποιεί ο οποιοσδήποτε πελάτης για να συνδεθεί στον εξυπηρετητή. Επίσης λέμε ότι ο server “ακούει” το port στο οποίο αρχικοποιεί το socket του, για καινούργιες συνδέσεις, δηλ. καινούργιους πελάτες. Γι’ αυτό το λόγο, το socket ονομάζεται “listening socket”. Θα πρέπει να δοθεί προσοχή στην επιλογή του αριθμού θύρας της υπηρεσίας, ο οποίος δεν θα πρέπει να είναι ήδη σε χρήση. Ο εξυπηρετητής, μόλις δεχθεί την σύνδεση καινούργιου πελάτη, γεννά ένα thread, για την εξυπηρέτηση των αιτήσεων του. Με αυτόν τον τρόπο, καθίσταται δυνατή η παράλληλη εξυπηρέτηση παλιών και η αποδοχή νέων πελατών. Μια τυπική αλληλουχία εντολών που συνήθως ακολουθούνται σε προγράμματα τύπου Server είναι οι εξής δηλώσεις: 1) ServerSocket() : αρχικοποίηση του listening socket 2) accept() : αναμονή και εντοπισμός καινούργιου πελάτη 3) new Thread : δημιουργία καινούργιου thread για την εξυπηρέτηση πελάτη 4) getInputStream() και getOutputStream() : το καινούργιο socket που επιστρέφεται από την accept συνδέεται με τον στανταρ μηχανισμό εισόδου/εξόδου στη Java, δηλ. τα streams
10
Client - Server με TCP O εξυπηρετητής (EchoServer παράδειγμα) - συνέχεια read() και write() - ανάγνωση και εγγραφή στο συνδεδεμένο με το socket - stream ακολουθούν και άλλες αναγνώσεις/εγγραφές close() - κλείνει το socket (όχι το listening ) και απελευθερώνεται ο αντίστοιχος πόρος του συστήματος 5) read() και write() : ανάγνωση και εγγραφή στο - συνδεδεμένο με το stream του socket 6) ακολουθούν και άλλες αναγνώσεις/εγγραφές 7) close() : κλείνει το socket ( όχι το listening ) και απελευθερώνεται ο αντίστοιχος πόρος του συστήματος. Το πρόγραμμα EchoServer.java καθώς και αναλυτική επεξήγηση των εντολών του βρίσκεται στις σελίδες των σημειώσεων και συστήνεται να αναγνωσθούν με ιδιαίτερη προσοχή. Να παρατηρήσουμε σε αυτό το σημείο, ότι οι εντολές που σχετίζονται με αρχικοποίηση του socket και των streams αυτού, θα πρέπει να περιέχονται στο σώμα try εντολής κι αυτό γιατί υπάρχει περίπτωση να μην είναι εφικτή η σύνδεση, οπότε θα έχουμε και έγερση μιας εξαίρεσης IOException. Σε μια τέτοια περίπτωση, δεν θα πρέπει να πραγματοποιήσουμε λειτουργίες Ι/Ο σε μη συνδεδεμένο socket. Επίσης, η μέθοδος close() είναι θεμιτό να καλείται σε κάθε περίπτωση, γι’ αυτό και την καλούμε μέσα από την finally.
11
Περιγραφή και ορισμός των Datagrams (1)
Στα UDP Datagrams η επικοινωνία που συνήθως αναπτύσσεται είναι “peer-to-peer”, δηλ. “ίσος προς ίσον”. Δεν έχουμε πλήρη διάκριση των ρόλων μεταξύ των δύο επικοινωνούντων διεργασιών - τα δύο προγράμματα – πελάτης/εξυπηρετητής λειτουργούν με παρόμοια διαδικασία βημάτων. Στα datagrams, τα πακέτα ( packets ) που μετακινούνται μέσω του δικτύου δεν διακινούν την πληροφορία αξιόπιστα και η λήψη της από το άλλο άκρο δεν γίνεται με την ίδια σειρά με την οποία απεστάλησαν Στα UDP Datagrams η επικοινωνία που συνήθως αναπτύσσεται είναι “peer-to-peer”, δηλ. “ίσος προς ίσον”. Δεν έχουμε πλήρη διάκριση των ρόλων μεταξύ των δύο επικοινωνούντων διεργασιών - τα δύο προγράμματα – πελάτης/εξυπηρετητής λειτουργούν με παρόμοια διαδικασία βημάτων. Στα datagrams, τα πακέτα ( packets ) που μετακινούνται μέσω του δικτύου δεν διακινούν την πληροφορία αξιόπιστα και η λήψη της από το άλλο άκρο δεν γίνεται με την ίδια σειρά με την οποία απεστάλησαν
12
Παράδειγμα TimeClient/TimeServer (1)
O TimeClient, κάνει ερώτηση σε απομακρυσμένο host, ζητώντας του μάθει την εκεί ώρα. Αντίστοιχα ο TimeServer παρέχει αυτήν την υπηρεσία, καθώς εκτελείται συνέχεια στον απομακρυσμένο host. Για τον αριθμό θύρας της υπηρεσίας στον server διαλέξαμε τον 8505 κατά τυχαίο τρόπο, αλλά μπορεί να χρησιμοποιηθεί και οποιοσδήποτε άλλος διαθέσιμος. Θα χρησιμοποιήσουμε τα αντικείμενα δύο κλάσεων : η κλάση DatagramSocket, που υλοποιεί την επικοινωνία με UDP datagrams η κλάση DatagramPacket, που αποτελεί το καλούπι με το οποίο θα φτιάχνουμε τα προς αποστολή πακέτα. Όπως αναφέραμε και προηγουμένως, οι έννοιες του πελάτη και του εξυπηρετητή δεν είναι και τόσο διακριτές, όταν έχουμε υλοποίηση με UDP datagrams. Επίσης, το βάρος τώρα δεν δίνεται τόσο στην σύνδεση ( δεν υπάρχει άλλωστε με την τυπική έννοια ), αλλά στη σωστή δημιουργία του πακέτου που πρόκειται να σταλεί, έτσι ώστε να περιέχει όλες τις απαραίτητες πληροφορίες, μαζί με τα προς αποστολή δεδομένα. Ως παράδειγμα εφαρμογής δικτύου με UDP πρωτόκολλο, εξετάζουμε δύο προγράμματα : Tο πρώτο, o TimeClient, είναι ένα πρόγραμμα με το οποίο μπορεί κανείς να κάνει ερώτηση σε απομακρυσμένο host, ζητώντας του μάθει την εκεί ώρα. Αντίστοιχα ο TimeServer παρέχει αυτήν την υπηρεσία, καθώς εκτελείται συνέχεια στον απομακρυσμένο host. Για τον αριθμό θύρας της υπηρεσίας διαλέξαμε τον 8505 κατά τυχαίο τρόπο, αλλά μπορεί να χρησιμοποιηθεί και οποιοσδήποτε άλλος διαθέσιμος. Για την υλοποίηση των δυο προγραμμάτων θα χρησιμοποιήσουμε τα αντικείμενα δύο κλάσεων : η κλάση DatagramSocket, που υλοποιεί την επικοινωνία με UDP datagrams η κλάση DatagramPacket, που αποτελεί το καλούπι με το οποίο θα φτιάχνουμε τα προς αποστολή πακέτα. Οι διαφάνειες που ακολουθούν εξηγούν τα ποιο σημαντικά τμήματα των προγραμμάτων TimeClient.java και TimeServer.java. Συστήνεται οι επόμενες διαφάνειες να αναγνωστούν βλέποντας παράλληλα και τον κώδικα των προγραμμάτων TimeClient.java και TimeServer.java ο οποίος βρίσκεται στις σελίδες των σημειώσεων και αντίστοιχα.
13
Παράδειγμα TimeClient (1)
Κατασκευάσουμε το αντικείμενο της διεύθυνσης του host που τρέχει ο TimeServer. Αυτή παρέχεται από τον χρήστη με την εντολή εκτέλεσης του προγράμματος από την γραμμή εντολών java TimeClient To αντικείμενο της διεύθυνσης ανήκει στην κλάση InetAddress, της οποίας χρησιμοποιούμε την μέθοδο getByName(String), για να το μετατρέψουμε από την αλφαριθμητική μορφή που το λαμβάνουμε στον τύπο InetAddress : InetAddresshostAddress = InetAddress.getByName(args[0]); Στο πρόγραμμα TimeClient.java πρώτα πρέπει να κατασκευάσουμε το αντικείμενο της διεύθυνσης του host που τρέχει ο TimeServer. Αυτή παρέχεται από τον χρήστη με την εντολή εκτέλεσης του προγράμματος από την γραμμή εντολών π.χ. java TimeClient Αξίζει να αναφέρουμε εδώ οτι η διεύθυνση είναι η default διεύθυνση του τοπικού μηχανήματος την οποία μπορούμε και να πάρουμε μέσω της μεταβλητής localhost. To αντικείμενο της διεύθυνσης ανήκει στην κλάση InetAddress, της οποίας χρησιμοποιούμε την μέθοδο getByName(String), για να το μετατρέψουμε από την αλφαριθμητική μορφή (String) που το λαμβάνουμε στον τύπο InetAddress : InetAddress hostAddress = InetAddress.getByName(args[0]); Η παραπάνω δήλωση είναι η πρώτη που βάζουμε στη μέθοδο μέθοδο main του TimeClient.java προγράμματος.
14
Παράδειγμα TimeClient (2)
Κατόπιν δημιουργούμε το datagram socket, μέσω του οποίου θα στείλουμε το πακέτο με την αίτηση και θα λάβουμε με αυτό την απάντηση. Ο παρακάτω κώδικας εκτελεί ακριβώς αυτό : DatagramSocket serversoc = new DatagramSocket(); Ο κατασκευαστής που χρησιμοποιήσαμε, διαλέγει από μόνος του κάποιο ελεύθερο port στο μηχάνημα που “τρέχει” ο πελάτης, για να το συνδέσει με το datagram socket. Να σημειώσουμε εδώ, ότι δεν χρειάζεται να συνδέσουμε το datagram socket με streams εισόδου/εξόδου, αφού δεν έχουμε Ι/Ο σε κάποιο κανάλι, αλλά αποστολή και λήψη μεμονωμένων πακέτων. Στη συνέχεια δημιουργούμε το datagram socket, μέσω του οποίου θα στείλουμε το πακέτο με την αίτηση επιπλέον θα λάβουμε μέσω αυτού την απάντηση από τον TimeServer. Ο παρακάτω κώδικας εκτελεί ακριβώς αυτό : DatagramSocket serversoc = new DatagramSocket(); Ο κατασκευαστής που χρησιμοποιήσαμε, διαλέγει από μόνος του κάποιο ελεύθερο port στο μηχάνημα που “τρέχει” ο πελάτης, για να το συνδέσει με το datagram socket. Να σημειώσουμε εδώ, ότι δεν χρειάζεται να συνδέσουμε το datagram socket με streams εισόδου/εξόδου, αφού δεν έχουμε Ι/Ο σε κάποιο κανάλι, αλλά αποστολή και λήψη μεμονωμένων πακέτων. Οι εντολές που ακολουθούν ορίζονται στην μέθοδο timeclient η οποία έχει δηλωθεί μέσα στο TimeClient.java πρόγραμμα και καλείται μέσα από την main μέθοδό του.
15
Παράδειγμα TimeClient (3)
Ακολουθεί κατασκευή του πακέτου της αίτησης. Ανήκει στην κλάση DatagramPacket. DatagramPacket packet; byte[] message = new byte[256]; packet = new DatagramPacket(message, 256, hostaddress, port); Η αποστολή γίνεται, πολύ απλά, καλώντας την μέθοδο send(DatagramPacket) του datagram socket : serversoc.send(packet); Στη συνέχεια έχουμε αναμονή για λήψη της απάντησης με την μέθοδο receive(DatagramPacket) του datagram socket. Serversoc.receive(packet); Στην timeclient μέθοδο αρχικά γίνεται η κατασκευή του πακέτου της αίτησης. Το πακέτο θα περιέχει την αίτηση (στον κώδικα έχει το όνομα «packet») ανήκει στην κλάση DatagramPacket. Ο κατασκευαστής της κλάσης DatagramPacket δέχεται 4 ορίσματα : έναν buffer, ο οποίος αποτελεί τα δεδομένα σε μορφή πίνακα από bytes ( προσοχή δεν είναι String ή array από χαρακτήρες ) το μέγεθος του προηγούμενου πίνακα την InetAddress διεύθυνση προορισμού τον αριθμό θύρας του προορισμού. Στην περίπτωση της αίτησης στον TimeServer Να σημειώσουμε εδώ οτι ο buffer δεν περιέχει δεδομένα, γιατί δεν θέλουμε να στείλουμε κανενός είδους χρήσιμη πληροφορία. Οι δηλώσεις δημιουργίας του πακέτου είναι οι ακόλουθες: DatagramPacket packet; byte[] message = new byte[256]; packet = new DatagramPacket(message, 256, hostaddress, port); Η αποστολή γίνεται, πολύ απλά, καλώντας την μέθοδο send(DatagramPacket) του datagram socket : serversoc.send(packet); Στη συνέχεια έχουμε αναμονή για λήψη της απάντησης καλώντας την μέθοδο receive(DatagramPacket) του datagram socket: serversoc.receive(packet); Η receive() “μπλοκάρει” το thread το οποίο την καλεί, μέχρι να λάβει κάποιο datagram πακέτο από το port που έχει ανοίξει το datagram socket. Χρειάζεται όμως προσοχή, καθώς το UDP πρωτόκολλο δεν είναι αξιόπιστο και το πακέτο με την απάντηση μπορεί να χαθεί. Γι’ αυτό το λόγο συνίσταται η χρησιμοποίηση ενός χρονομέτρου, το οποίο θα ξεμπλοκάρει το νήμα, για να τερματιστεί ομαλά η εφαρμογή.
16
Παράδειγμα TimeClient (4)
Η receive() “μπλοκάρει” το νήμα απ’ το οποίο έχει κληθεί, μέχρι να λάβει κάποιο datagram πακέτο στο port που έχει ανοίξει το datagram socket. Mε την μέθοδο getData() της κλάσης DatagramPacket, λαμβάνουμε τον πίνακα από bytes με την χρήσιμη πληροφορία, τον οποίο μετατρέπουμε σε String για την εκτύπωση. Οι μέθοδοι getInetAddress() και getPort() επιστρέφουν την διεύθυνση και τον αριθμό θύρας αντίστοιχα, του αποστολέα του πακέτου. Πριν το πρόγραμμα τερματιστεί, απελευθερώνουμε με την close() το DatagramSocket(). Τέλος, με την μέθοδο getData() της κλάσης DatagramPacket, λαμβάνουμε τον πίνακα από bytes με την χρήσιμη πληροφορία, τον οποίο μετατρέπουμε σε String για την εκτύπωση. Επίσης οι μέθοδοι getInetAddress() και getPort() της κλάσης DatagramPacket επιστρέφουν την διεύθυνση και τον αριθμό θύρας αντίστοιχα, του αποστολέα του πακέτου. Πριν το πρόγραμμα τερματιστεί, δεν ξεχνάμε να απελευθερώσουμε τους πόρους που είχαμε δεσμεύσει, δηλ. το datagram socket. Αυτό γίνεται στη μέθοδο main του TimeClient.java προγράμματος με την μέθοδο close() : serversoc.close();
17
Παράδειγμα TimeServer(1)
Ο εξυπηρετητής κατασκευάζει ένα πακέτο λήψης αιτήσεων (max 8KBytes). Ο εξυπηρετητής δημιουργεί το datagram socket, για την λήψη των πακέτων. O κατασκευαστής του αντικειμένου DatagramSocket δέχεται ως port το 8505, στο οποίο θα “ακούει”, για νέες συνδέσεις. Επειδή η παρεχόμενη υπηρεσία απαιτεί μικρής διάρκειας εξυπηρέτηση, δεν είναι απαραίτητο να δημιουργεί για κάθε αίτηση πελάτη και νέο thread. Στη συνέχεια έχουμε αναμονή για λήψη της αίτησης πελάτη με την μέθοδο receive(DatagramPacket) του datagram socket. s.receive(packet); Ο server της υπηρεσίας ώρας (πρόγραμμα TimeServer.java) δεν διαφέρει και πολύ από τον client. Αρχικά να επισημάνουνε ότι, εξαιτίας του ότι η παρεχόμενη υπηρεσία απαιτεί μικρής διάρκειας εξυπηρέτηση, δεν είναι απαραίτητο ο εξυπηρετητής να δημιουργεί για κάθε πελάτη και ένα καινούργιο thread, ώστε να μπορεί να δεχθεί και νέες συνδέσεις. Επιπλέον όλη η χρήσιμη πληροφορία της απάντησης μπορεί να χωρέσει μέσα σε ένα datagram packet, το οποίο έχει μέγιστο μέγεθος 8ΚBytes. Στα UDP datagrams, το βάρος της σχεδίασης μετατοπίζεται, από την εξυπηρέτηση του πελάτη, στην αυτόνομη εξυπηρέτηση των πακέτων. Μπορούμε, επίσης, να παρατηρήσουμε τον τρόπο με τον οποίο ο εξυπηρετητής δημιουργεί το datagram socket, για την λήψη των πακέτων. Ο κατασκευαστής του αντικειμένου DatagramSocket δέχεται πλέον το port ( δηλ. το 8505 ) μέσω της δήλωσης: s= new DatagramSocket(8505); Και στη συνέχεια “ακούει” στο socket, για νέες συνδέσεις μέσω της: s.receive(packet);
18
Παράδειγμα TimeServer(2)
Οι μέθοδοι getInetAddress() και getPort() επιστρέφουν την διεύθυνση και τον αριθμό θύρας αντίστοιχα, του αποστολέα του πακέτου αίτησης του πελάτη. Ακολουθεί κατασκευή του πακέτου που θα σταλεί στον πελάτη και θα περιέχει: το String με την τοπική ώρα του εξυπηρετητή το μήκος του String τη διεύθυνση και το port του πελάτη. Η αποστολή γίνεται, πολύ απλά, καλώντας την μέθοδο send(DatagramPacket) του datagram socket Οι μέθοδοι getInetAddress() και getPort() επιστρέφουν την διεύθυνση και τον αριθμό θύρας αντίστοιχα, του αποστολέα του πακέτου αίτησης του πελάτη. Στο συγκεκριμένο πρόγραμμα οι μέθοδοι getInetAddress() και getPort() καλούνται ως εξής: InetAddress cl = packet.getAddress(); int port = packet.getPort(); Ακολουθεί η κατασκευή του πακέτου που θα σταλεί στον πελάτη και θα περιέχει: 1)το String με την τοπική ώρα του εξυπηρετητή 2) το μήκος του String 3) τη διεύθυνση και το port του πελάτη. Στο συγκεκριμένο πρόγραμμα καλούνται ως εξής: packet = new DatagramPacket(buf, buf.length, cl, port); Η αποστολή γίνεται, πολύ απλά, καλώντας την μέθοδο send(DatagramPacket) του datagram socket : s.send(packet);
Παρόμοιες παρουσιάσεις
© 2024 SlidePlayer.gr Inc.
All rights reserved.