tesinaSockets e DatagramSockets (498927 byte)

Transcript

tesinaSockets e DatagramSockets (498927 byte)
Tesina esame ‘Programmazione di Sistemi Mobile’
realizzata da Roberto Giuliani
matricola 633688
Sockets e
DatagramSocket
Windows Phone prevede un’interfaccia di programmazione per garantire agli sviluppatori di creare
applicazioni che possono comunicare con i servizi internet e altre applicazioni remote usando le
sockets. Esempi che usano le sockets per comunicare sono FTP, email, sistemi di chat e multimedia
streaming (YouTube). Usando le sockets nelle applicazioni Windows Phone si è in grado di creare
ricche applicazioni client che possono comunicare tramite Trasmission Control Protocol (TCP) o
User Datagram Protocol (UDP).
Una socket non è altro che un’interfaccia software che si posiziona tra i livelli del modello ISO-OSI
del trasporto e applicativo, la quale permette ad un processo di comunicare conoscendo sia la
rispettiva porta, che il rispettivo indirizzo. Vengono definite dallo strato di rete tramite un indirizzo,
il quale è combinazione dell’Internet Protocol (IP) address ed il numero di porta.
Windows Phone prevede un’interfaccia di programmazione necessaria a creare e usare le sockets
TCP e UDP. E’ possibile selezionare quale tipo si preferisce utilizzare in relazione alle
caratteristiche che l’applicazione richiede.
Il diagramma mostra una vista delle operazioni
che vengono eseguite durante una sessione di
comunicazione tra un’applicazione client e una
service.
Le sockets sono generalmente usate quando
non vi è un alto livello di API o altre astrazioni
per lo scenario, quando è coinvolto un
protocollo personalizzato, quando vi è la
necessità di un protocollo bidirezionale o
quando ha senso per ridurre al minimo il
sovraccarico di ogni scambio.
Nel mondo delle sockets, lo scambio di
informazioni può avvenire in due modi, come
pacchetto/messaggio singolo o come un canale
continuo (detto stream). Loro sono chiamati
datagram sockets e stream sockets e sono
entrambi supportati attraverso le API WinRT.
La suddivisione della scelta avviene in
relazione al protocollo che si implementa e che si utilizza. Sta al programmatore quindi decidere in
base alle caratteristiche del rispettivo applicativo. In questa tesina si è deciso di sviluppare il
concetto di Datagram Sockets.
Inoltre WinRT supporta anche le forme di scambio tra il protocollo WebSocket, una tecnologia
originariamente creata per web browsers e web servers ma che ha preso notevole sviluppo in scopi
di utilizzo generale all’interno delle app.
Tutte le informazioni possono essere trovare nelle classi che corrispondono all’API
Windows.Networking.Sockets.
Datagram Sockets
Nella terminologia delle sockets, un datagram corrisponde ad un unico messaggio completo, nella
quale sono contenute sia le informazioni relative alla comunicazione che ai dati che vogliono essere
trasferiti da una socket all’altra. Senza avviare a priori una connessione ed utilizzando lo standard
User Datagram Protocol (UDP).
Tale standard è considerato come una forma di scambio semplice, senza stato, unidirezionale ed
orientato alla transazione. Ha una minima intestazione rilasciata nel messaggio (di pochi bit) e non
prevende la ritrasmissione del datagramma in caso di non ricezione o perdita, per queste ragioni non
può garantire una corretta e sicura ricezione (non vi è quindi la presenza di Acknowledge).
Inoltre viene utilizzato dove il controllo degli errori e la loro correzione non sono necessari o dove
questo processo viene svolto in una parte superiore del modello ISO-OSI come per esempio nello
strato applicativo, quindi dall’applicazione stessa.
Per esempio sarà compito dell’applicazione assicurarsi che i dati ricevuti corrispondano a quelli
veramente concordati e richiesti. E non farlo svolgere al livello di traporto, quali per esempio il
protocollo TCP. Nel momento in cui i messaggi non sono quelli aspettati si procede alla richiesta di
rinvio (sempre a livello applicativo).
In poche parole, UDP permette di minimizzare i ritardi, una volta ricevuto il messaggio dello strato
superiore esso lo ‘impacchetta’ e lo invio immediatamente, rendendo le latenze
approssimativamente vicine allo zero. I protocolli di alto livello come Real-time Transport Protocol
RTP e il Real Time Streaming Protocol RTSP sono costruiti su UDP.
Possono essere aggiunti degli attributi che consentono un utilizzo migliore di questo protocollo.
Come, per esempio, un tempo in millisecondi che verrà decrementato fino alla ricezione del
messaggio, nel momento in cui tale valore raggiunge la soglia dello zero si ipotizzerà che la
richiesta è andata persa e quindi sarà rinviato il pacchetto.
L’implementazione di una socket a lato client potrebbe seguire la seguente sintassi:
Socket socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
Dove vengono indicate le seguente proprietà:
-
AddressFamily.InterNetwork: la socket utilizzerà lo schema di indirizzamento IP versione
4 per risolvere un indirizzo. Avviene quando l’oggetto socket si connette al terminare scelto.
-
SocketType.Dgram: indica che la socket supporta pacchetti che corrispondono a
datagrammi (o messaggi).
-
ProtocolType.Udp: fa riferimento al protocollo UDP (User Datagram Protocol)
Un’applicazione Windows Store lavora con questo tipo di trasporto – sia lato client che lato server –
usando la classe DatagramSocket, nella quale, inizializzando una specifica connessione, attivi un
listener che attende i messaggi.
Rimanendo in ascolto nell’evento messagereceived.
Quando i dati arrivano, l’handler riceve un oggetto DatagramSocketMessageReceived-EventArgs.
Questo contiene le proprietà localAddress e remoteAddress, entrambi dei quali sono un oggetto
Windows.Networking.HostName che contiene un indirizzo IP, un display name e un paio di altri
bits. Gli argomenti dell’evento contengono anche una stringa che indica il remotePort.
Più importanti sono i due metodi dalla quale si estraggono i dati. Uno è getDataStream, che
restituisce un IInputStream attraverso il quale si possono leggere sequenzialmente dei byte dal
network remoto di destinazione. L’altro è getDataReader che restituisce un oggetto
Windows.Storage.Streams.DataReader.
Questa è un’astrazione ad alto livello costruita su IInputStream che aiuta a leggere direttamente
specifici dati. Chiaramente, se si conosce la struttura dei dati, che ci si aspetta di ricevere nel
messaggio, usando il DataReader si opererà direttamente il tipo di conversione si vuole.
Ovviamente, si prende qualunque tipo di informazione da una socket, e si necessita di collegarla a
qualcosa. A questo proposito DatagramSocket include un paio di metodi per utilizzare e gestire la
connessione:
-
connectAsync: Inizia un’operazione di connessione dando un oggetto HostName ed il nome
al servizio (oppure una stringa per la porta UDP) della destinazione remota. Questo viene
utilizzato per creare una connessione mono-direzionale dal client al server.
-
Un’altra form di connectAsync prende un oggetto Windows.Networking.EndpointPair che
specifica il nome dell’host e del service sia per il terminale locale che per quello remoto.
Questo viene utilizzato per creare una connessione bidirezionale client/server, dove il
terminale locale implica una chiamata a bindEndpointAsync vista nel seguito.
-
bindEndpointAsync: Per una connessione monodirezionale – che solo ascolta ma non invia
i dati sulla sockets – questo metodo lega un terminale locale dando un HostName e un nome
o porta del service. Legando il nome del service da se stesso può essere fatto con
bindServiceNameAsync.
-
joinMulticastGroup: Da un Hostname, connesso alla Datagram socket a un gruppo
multicast.
-
Close: Permette di terminare la connessione e concludere qualunque operazione.
Nota che qualunque socket viene data, essa può essere connessa ad un qualunque numero di
terminale – si può invocare connectAsync più volte, unire molteplici gruppi di multicast e legare
molteplici terminali locali con bindEndpointAsync e bindServiceNameAsync.
A differenza del metodo close() chiude tutto una volta!
Una volta che la socket ha una o più connessioni, le informazioni della connessione possono essere
recuperate con la proprietà DatagramSocket.information.
Nota anche che il metodo statico DatagramSocket.getEndpointPairsAsync prevede (come un
risultato async) un vettore di oggetti EndpointPair disponibili per un hostname remoto e un service
name.
I dati di controllo possono anche essere impostati attraverso la proprietà DatagramSocket.control,
come l’oggetto DatagramSocketControl con le proprietà qualityOfService e
outputUnicastHopLimit.
Tutto questo lavoro, ovviamente, è solo il preambolo dell’invio dei dati sulla connessione socket.
Questo viene effettuato attraverso la proprietà del DatagramSocket.outputStream, un IOutputStream
nel quale tu puoi scrivere i dati che interessano usando i metodi writeAsync e SendToAsync.
Questo invierà i dati su ogni connessione entro la socket.
Alternativamente tu puoi utilizzare una delle variazione di getOutStreamAsync per specificare un
EndpointPair o Hostname/port nel quale inviare i dati.
Il risultato di entrambi queste operazioni async è ancora un IOutputStream. E in tutti i casi si può
creare un oggetto DataWriter di alto livello attorno allo stream:
Interessante aspetto è quando si presenta un errore sulla connessione socket, dove si può passare il
numero di tale errore al metodo getStatus dell’oggetto SocketError e riprendere un valore
SocketErrorStatus più completo. Magari per risolvere errori imprevisti.
Nel seguito vengono implementati due metodi (Send e Receive) di utilizzo delle sockets da
parte del client
Questo metodo chiamato Send(…) riceve in ingresso come parametri il nome del server, il numero
della porta della socket di destinazione e i dati che si desidera trasferire. E come valore di ritorno
ottengo una stringa che contiene un messaggio di errore se l’invio non è avvenuto correttamente,
mentre una segnalazione di TimeOut se è avvenuto correttamente e siamo in attesa della risposta a
lato server.
Da sottolineare in tale struttura è la creazione dell’oggetto SocketAsyncEventArgs, esso deriva dalla
classe corrispondente ed è parte di un insieme di miglioramenti che può essere utilizzato come
modello asincrono alternativo da applicazioni socket specializzate con elevate prestazioni.
Successivamente vengono impostate alcune delle sue proprietà, quali il buffer dei dati e la sua
dimensione. Anche in questo caso evidenziamo il metodo RemoteEndPoint il quale ottiene o
imposta l’endPoint remoto per l’operazione asincrona.
Il metodo conclusivo, SendToAsync(…), corrisponde al vero e proprio invio del pacchetto da parte
della Socket creata in precedenza.
Per ricevere la notifica di completamente è necessario creare un metodo di callback che implementa
il delegato EventHandler<SocketAsyncEventArgs> e collegarlo all’evento
SocketAsyncEventArgs.Completed, come si vede nella struttura sopra.
Differente invece la parte in ricezione svolta sempre a lato client, nella quale viene implementato il
metodo Receive(…) che restituisce come risultato una stringa contenente o l’errore avvenuto
durante la fase di ricezione dei dati oppure i dati stessi ricevuti dal server.
Anche in questo caso viene inizializzato un oggetto di tipo SocketAsyncEventArgs che con la
proprietà garantita dal metodo RemoteEndPoint permette di rimanere in ascolto in attesa di un
messaggio da qualunque indirizzo IP esterno che conosca la porta della Socket interessata.
Per poi impostare il buffer ed inserirci i dati ricevuti dal server tramite il metodo
ReceiveFromAsync(…).
Solo una volta conclusa l’intera comunicazione è possibile chiudere la socket.