Sincronizza Indice |
Scarica il progetto |
Testo dell'articolo |
Stampa l'articolo |
Scambiare uno o più files in un'applicazione Client / Server con un protocollo di nostra progettazione può essere relativamente semplice. Basterà infatti aprire il file in maniera binaria, leggerne il contenuto in più volte e mandari i singoli pezzetti mediante il metodo SendData del controllo Winsock.
Il protocollo TCP/IP si occuperà di verificare se il pacchetto è arrivato a destinazione e comunicare la risposta all'altro capo della comunicazione. Tuttavia in questa maniera la nostra applicazione non saprà nulla sullo stato di avanzamento del trasferimento dei files; la parte client si occuperà di inviare il file e basta, mentre la parte server salverà il file senza mandare risposte esplicite al client. In questo esempio vedremo invece come controllare l'avanzamento del trasferimento
del file mediante l'invio di piccoli pacchetti di conferma. Questo genere
di pacchetti di risposta sono detti ACK
(Acknowledgement - mettere a conoscenza). Il progetto si comporrà di due parti: una Client ed una Server. Il trasferimento di files di cui ci occuperemo andrà unicamente dal client verso il server, sarò ovvero un upload. Utilizzeremo un metodo di trasferimento molto simile al protocollo FTP. Il server, dopo aver accettato il trasferimento del file, aprirà un socket passivo su una porta casuale e comunicherà il numero della porta aperta al Client. Tutti i dati che arriveranno sul socket passivo saranno salvati all'interno del file. Prima di vedere l'interfaccia utente ed il codice definiamo un nostro protocollo, un set di regole, per far comunicare queste due applicazioni. Il Client potrà inviare soltanto due comandi:
Il Server da parte sua avrà altri comandi unicamente di risposta:
Il procedimento da seguire è molto semplice: il server apre un
socket in attesa su una porta specifica a cui il client si collegherà.
Stabilita la connessione il client invia la richiesta di trasferimento
di un file mediante il comando
A questo punto la connessione di trasferimento è stabilita: il client potrà leggere un primo pacchetto di dati dal file ed inviarlo mediante il nuovo socket aperto collegato al socket passivo sul server. Inviato il primo pacchetto il client rimarrà in attesa del pacchetto
di ACK di risposta. Il server, al ricevimento dei dati sul socket passivo,
li memorizzerà nel file di destinazione ed invierà il pacchetto
di ACK Il server, al ricevimento dell'ultimo pacchetto non dovrà rispondere
con una risposta di ACK ma in un altro modo, per non far richiedere dati
oltre la fine del file. Il pacchetto che invierà sarà Se il client vuole interrompere l'invio del file gli basterà mandare
il messaggio Cominciamo a vedere il Client, la parte più semplice del progetto: Il form si compone di pochi e semplici controlli: due CommandButtondi nome Connetti ed Invia, due TextBoxdi nome IndirizzoIP e NomeFile e due controlli Winsockdi nome Socket e SocketInvio. Il funzionamento sfiora il ridicolo per la sua semplicità: l'utente inserirà l'indirizzo IP del server e premerà il tasto Connetti per stabilire la connessione. Fatto questo inserirà il nome di un file nella casella NomeFile e premerà il pulsante Invia per richiedere il trasferimento al server. L'utente ha finito e non dovrà fare altro che attendere la fine del trasferimento. Il codice non si presenta molto complesso:
Al fine di evitare alcuni problemi legati all'invio di una quantità di dati superiore a quella trasferibile in un singolo pacchetto con un socket TCP/IP, abbiamo dichiarato la funzione API getsockopt, che utilizzeremo più avanti in combinazione con le due costanti API SO_SNDBUF e SOL_SOCKET allo scopo di ottenere la massima quantità di dati in bytes trasferibili in un singolo pacchetto. Alla riga 6 abbiamo definito una variabile chiamata FILEHANDLE
che utilizzeremo per memorizzare l'handle
del file aperto da trasferire.
Nel momento in cui l'utente clicca sul pulsante Connetti dovrà essere avviata la connessione con il server in attesa. Prima di fare questo, però, è necessario terminare un'eventuale precedente connessione (riga 10) e, molto importante, scegliere un'altra porta da utilizzare per collegarsi, poiché la precedente connessione avrebbe potuto lasciare la porta utilizzata nello stato WAIT_STATE (vedi informazioni sugli stati di NETSTAT), impostando la porta locale a 0 (riga 11). Solo allora potrà essere avviata la connessione con il server, il cui indirizzo è specificato nella TextBox IndirizzoIP sulla porta 1500.
Al momento della chiusura del form verranno chiusi tutti i socket aperti.
Il click sopra il pulsante Invia effettuerà l'invio della richiesta di trasferimento del file, ma prima di farlo è necessario controllare che la connessione con il server sia attiva (riga 23) e che il file specificato nella casella di testo NomeFile esista effettivamente (riga 24). Se esso non dovesse esistere sarà mostrato un messaggio di errore (riga 25). Per una spiegazione del metodo di verifica dell'esistenza di un file consultare l'HowTo dedicato. Soltanto quando abbiamo la certezza che il file da inviare esista effettueremo
la richiesta di trasferimento. La richiesta è una stringa formata
da Trovato il punto della stringa in cui inizia il nome del file vero e proprio, sarà mandata la richiesta di trasferimento; la dimensione del file da inviare sarà ritrovata mediante l'utilizzo della funzione FileLen (riga 31). Se il server risponderà positivamente alla richiesta di trasferimento, sarà mandato un messaggio specifico che vedremo fra poco da cui il nostro client recupererà il numero di porta del socket passivo a cui connettersi mediante il socket SocketInvio.
Al momento della connessione tra SocketInvio ed il socket passivo del server viene aperto il file da trasferire in modalità binaria (riga 38), viene letto un primo pacchetto di dati della grandezza di 512 bytes al solo scopo di avviare il trasferimento vero e proprio che sarà gestito da una procedura successiva. Il buffer letto sarà poi inviato mediante SocketInvio (righe 39 e 40). Alla riga 41 abbiamo utilizzato la funzione API getsockopt
per ritrovare la dimensione massima in bytes per ogni singolo pacchetto,
senza che questo venga spezzettato in due o più parti, con effetti
distruttivi. La dimensione massima per pacchetto trasferibile verrà
salvata nella variabile DIMENSIONEPACCHETTO (riga 41).
Alla chiusura della connessione tra SocketInvio ed il socket passivo sul server, verranno chiusi l'handle del file ed il socket di trasferimento (righe 46 e 47). Quella che segue è il cuore del programma. Il socket utilizzato per la connessione iniziale servirà per ricevere le risposte dal server. Le risposte che esso dovrà verificare saranno 3: l'accettazione del file da parte del server, l'ACK di trasferimento ed il termine del trasferimento del file. La procedura che gestirà queste funzioni sarà ovviamente quella legata all'eventoDataArrival sul primo socket.
I dati arrivati saranno estratti mediante il metodo GetData e successivamente convertiti in Unicode per essere utilizzati validamente come stringhe da analizzare.
Se i primi 11 caratteri dei dati riportati sono la stringa
Se i primi 10 caratteri della risposta ricevuta corrispondono alla stringa
Alla riga 60 abbiamo voluto aggiungere un controllo che non dovrebbe essere necessario ma può essere utile in quei casi in cui la linea è molto lenta oppure i pacchetti per qualche motivazione vengono spezzettati. La lettura e l'invio di dati del file sarà effettuata soltanto se l'handle del file aperto è diverso da 0. Alla riga 61 viene allocato un buffer di dimensione in bytes specificata dalla variabile DIMENSIONEPACCHETTO. L'allocazione dei dati viene effettuata mediante l'utilizzo della funzione Space; ma poiché la funzione crea una stringa e tutte le stringhe in Visual Basic sono codificato secondo lo standard UNICODE a doppio byte, la lunghezza della stringa da creare sarà dimezzata. Alle righe 62 e 63 viene effettuata la lettura dei dati dal file aperto e l'invio degli stessi mediante SocketInvio.
L'ultima risposta da verificare riguarda la chiusura della connessione
dal lato server per completata ricezione del file aspettato. Se quindi
la stringa ricevuta è |
Il nostro client termina qui. I comandi sono pochi e semplici ed il programma non si occupa di controllare eventuali errori di connessione o di altro genere. Esso presuppone che tutte le operazioni siano svolte in maniera corretta. Inoltre la dimensione per pacchetto di dati da inviare sarà controllata soltanto sul client, ma non verrà controllata la dimensione di ricezione del server. Fibia
FBI
|
Torna all'indice Client/Server |