<< Continua dalla parte 1
Visto il client nella parte precedente, rimane da vedere il server, leggermente
più complesso del precedente.
Il secondo progetto si compone di un solo form che farà uso del
controllo FBI Shape Progress Bar trattato
nella sezione Controlli utente da
aggiungere al progetto.
Sulla
superficie del form avremo una ListBox di
nome Stati, nella quale saranno inserite alcune informazioni sulla connessione
e sul trasferimento dei dati.
Sono presenti anche due controlli Winsock di
nome Socket e Passivo, due Label indicative
di nome StatoLabel ed Avanzamento. Entrambe avranno la proprietà
Autosize impostata a True, ma la prima avrà anche la proprietà
Wordwrap impostata a True, mentre la seconda avrà la proprietà
Alignment impostata a 2-Center.
Completiamo il nostro form con un'istanza
della FBI Shape Progress Bar di
nome AvanzamentoProgress con la proprietà Align impostata
a 2-vbAlignBottom, Alignment impostata a 2-vbCenter e la proprietà
Decimali impostata su 2.
Il server non potrà fornire alcun comando, eccetto che rispondere
SI o NO alla richiesta di trasferimento del file per accettarlo o rifiutarlo.
Vediamo passo dopo passo il nostro codice:
- Option Explicit
- Private FILEHANDLE As Integer
- Private DIMENSIONEFILE As Long
- Private FILEDASALVARE As String
Abbiamo dichiarato 3 variabili che verranno utilizzate più volte
all'interno del programma: FILEHANDLE è l'handle del file
aperto per il trasferimento; DIMENSIONEFILE è la dimensione
in bytes aspettata; infine FILEDASALVARE è il nome del file
da salvare compreso del percorso della cartella.
- Private Sub Form_Load()
- StatoLabel.Caption = ""
- Avanzamento.Caption = "0 bytes ricevuti su 0"
- Socket.LocalPort = 1500
- Socket.Listen
- End Sub
All'avvio del programma viene aperta la porta 1500 in ascolto per la
connessione sul controllo Socket (righe 10 e 11).
- Private Sub Form_Unload(Cancel As Integer)
- If Socket.State <> sckClosed Then Socket.Close
- End Sub
Così alla chiusura del form viene chiusa anche la connessione
aperta con Socket.
- Private Sub Socket_ConnectionRequest(ByVal requestID As Long)
- Socket.Close
- Socket.Accept requestID
- Stati.AddItem "Accettata connessione."
- End Sub
Nel momento in cui Socket riceve una richiesta di connessione,
esso termina di ascoltare ed accetta la chiamata, senza preoccuparsi di
chi sia.
- Private Sub Socket_Close()
- If Passivo.State <> sckClosed Then Passivo.Close
- If Socket.State <> sckClosed Then Socket.Close
- Stati.AddItem "Connessione chiusa."
- Socket.Listen
- End Sub
Nel momento in cui la connessione viene chiusa (dal lato client), vengono
chiusi tutti i socket aperti e Socket viene posto nuovamente in
attesa di chiamata.
L'evento DataArrival
sul primo socket servirà per accettare il trasferimento del file
oppure annullarlo una volta avviato. Ricordiamo che il server accetta
solo due comandi: "/FILE NomeFile Dimensione" e
"/FINE" .
- Private Sub Socket_DataArrival(ByVal bytesTotal As Long)
- Dim DATI() As Byte
- Dim NOMEFILE As String
- Dim DIMENSIONE As Long
- Dim POSIZIONE As Integer
- Dim TEMPSTR As String
Saranno utilizzate una serie di variabili: la matrice
di bytes DATI servirà
per contenere i dati ricevuti dal client; NOMEFILE verrà
utilizzata per contenere il nome del file in arrivo, POSIZIONE
verrà utilizzata per effettuare l'estrazione dei parametri dai
dati ricevuti dal client, DIMENSIONE sarà invece la dimensione
in bytes del file in arrivo e TEMPSTR sarà utilizzata per
manipolare le stringhe.
- Call Socket.GetData(DATI)
- DATI = StrConv(DATI, vbUnicode)
- Select Case Left(UCase(DATI), 5)
- Case "/FILE"
- TEMPSTR = Mid(DATI, 7)
- POSIZIONE = 0
- While InStr(POSIZIONE + 1, TEMPSTR,
" ") > 0
- POSIZIONE = InStr(POSIZIONE
+ 1, TEMPSTR, " ")
- Wend
- NOMEFILE = Left(TEMPSTR, POSIZIONE
- 1)
- DIMENSIONE = CLng(Mid(TEMPSTR,
POSIZIONE + 1))
- TEMPSTR = "È in arrivo un
file di nome " & NOMEFILE & " di " & CStr(DIMENSIONE)
& " bytes."
- TEMPSTR = TEMPSTR & vbNewLine
- TEMPSTR = TEMPSTR & "Desideri
accettarlo?"
Alla riga 38 vengono letti i dati in arrivo su Socket e subito
successivamente vengono convertiti in Unicode,
allo scopo di utilizzarli come stringhe.
Ottenuta la stringa inviata dal client, sarà necessario verificare
se essa è uno dei due comandi accettati (righe 40, 41 e 80).
Se la stringa ricevuta inizia per "/FILE" , sarà
necessario estrarre da essa il nome del file e la dimensione; l'inizio
di tali informazioni è alla posizione 7 della stringa.
Alle righe 43-46 viene estratta la posizione dell'ultimo spazio separatore,
al fine di rintracciare il punto dove termina il nome del file ed inizia
la dimensione. Tale posizione sarà salvata nella variabile POSIZIONE.
Solo allora (righe 47 e 48) sarà possibile estrarre il nome del
file (salvato in NOMEFILE) e la sua dimensione (memorizzata in
DIMENSIONE).
Alle righe 49-51 viene formata la stringa TEMPSTR che verrà
utilizzata per mostrare un avviso all'utente. Essa conterrà anche
il nome del file in arrivo e la sua dimensione.
- If MsgBox(TEMPSTR, vbYesNo + vbQuestion)
= vbYes Then
- FILEDASALVARE = App.Path
& "\RICEVUTI\" & NOMEFILE
- If Dir(FILEDASALVARE)
<> "" Then
- TEMPSTR
= "Il file " & NOMEFILE & " esiste già."
& vbNewLine
- TEMPSTR
= TEMPSTR & "Desideri sovrascriverlo?"
- If MsgBox(TEMPSTR,
vbYesNo + vbQuestion) = vbYes Then
- Kill
FILEDASALVARE
- Else
- Socket.SendData
" -ERR: NONACCETTATO" & vbNewLine
- Stati.AddItem
"File " & NOMEFILE & " rifiutato."
- Exit
Sub
- End If
- End If
Tale
avviso sarà mostrato all'utente. Se alla richiesta di trasferimento
l'utente rispnderà NO, sarà inviato al client
il messaggio " -ERR: NONACCETTATO" e la procedura
terminerà (righe 59-63).
Se l'utente invece risponderà SI, verrà generato
il percorso completo del file da salvare, composto dal percorso della
cartella in cui il programma server si trova, dalla cartella RICEVUTI
e dal nome del file in arrivo. Il percorso completo sarà memorizzato
nella variabile FILEDASALVARE.
Prima
di procedere alla scrittura del file, sarà necessario verificare
se il file esiste (le varie soluzioni possibili sono spiegate
in un HowTo). Se esso esiste, sarà mostrato un avviso di sovrascrittura
all'utente (righe 55-57). Se l'utente richiederà la sovrascrittura,
il file originale sarà cancellato prima di ricevere il file nuovo
(righe 57-58).
In caso contrario il file originale sarà lasciato lì e
verrà ritornato al server il messaggi di non accettazione del file.
In altre situazioni potrebbe essere comodo specificare un nuovo nome di
file in cui salvare il file in arrivo.
- DIMENSIONEFILE = DIMENSIONE
- If Passivo.State <>
sckClosed Then Passivo.Close
- Passivo.LocalPort
= 0
- Passivo.Listen
- Socket.SendData "
+OK: PORTA " & Passivo.LocalPort & vbNewLine
- Stati.AddItem "Accettazione
del file " & NOMEFILE
- Stati.AddItem "Ascolto
sulla porta " & Passivo.LocalPort
- StatoLabel.Caption
= "Ricezione del file " & NOMEFILE & " di "
& DIMENSIONE & " bytes."
- Avanzamento.Caption
= "0 bytes ricevuti su " & DIMENSIONE
- AvanzamentoProgress.Value
= 0
- AvanzamentoProgress.Max
= 0
- Else
- Socket.SendData "
-ERR: NONACCETTATO" & vbNewLine
- Stati.AddItem "File
" & NOMEFILE & " rifiutato."
- End If
Alla riga 65 viene impostata la variabile globale DIMENSIONEFILE,
che verrà utilizzata dal resto del programma, uguale a DIMENSIONE.
Sarà necessario aprire un socket passivo in ascolto su una porta
casuale e comunicare il numero di tale porta al client, nella forma "
+OK: PORTA Numero" (righe 66-69).
Saranno anche azzerati tutti i controlli grafici per monitorare l'avanzamento
del traferimento.
Se, invece, l'utete ha risposto NO alla richiesta di trasferimento, sarà
inviata al client la risposta " ERR: NONACCETATO" .
- Case "/FINE"
- Passivo.Close
- Close FILEHANDLE
- FILEDASALVARE = ""
- Stati.AddItem "Ricezione
del file completata."
- End Select
- End Sub
In caso che il client volesse terminare il trasferimento a metà,
invierà il comando "/FINE" (non implementato in questo
esempio) al cui ricevimento il server si occuperà di chiudere il
socket passivo e l'handle del file in scrittura.
- Private Sub Passivo_ConnectionRequest(ByVal requestID As Long)
- FILEHANDLE = FreeFile
- Open FILEDASALVARE For Binary As FILEHANDLE
- Passivo.Close
- Passivo.Accept requestID
- End Sub
Nel momento in cui il client richiede il collegamento con il socket passivo,
viene aperto il file FILEDASALVARE in modalità binaria e
viene accettata la richiesta di collegamento.
- Private Sub Passivo_Close()
- Close FILEHANDLE
- FILEDASALVARE = ""
- Stati.AddItem "Ricezione del file completata."
- End Sub
Analogamente nel momento della disconnessione viene chiuso il file aperto.
Tutti i dati che arriveranno al socket passivo saranno scritti all'interno
del file di output.
- Private Sub Passivo_DataArrival(ByVal bytesTotal As Long)
- Dim DATI() As Byte
- Call Passivo.GetData(DATI)
- If UBound(DATI) + LOF(FILEHANDLE) + 1 >= DIMENSIONEFILE
Then
- ReDim Preserve DATI(DIMENSIONEFILE - LOF(FILEHANDLE)
- 1)
- Put FILEHANDLE, , DATI
- Stati.AddItem "Salvato chunk di "
& UBound(DATI) + 1 & " bytes."
- Avanzamento.Caption = LOF(FILEHANDLE) &
" bytes ricevuti su " & DIMENSIONEFILE
- AvanzamentoProgress.Max = DIMENSIONEFILE
- AvanzamentoProgress.Value = LOF(FILEHANDLE)
- Socket.SendData " +OK: FINE"
- DoEvents
- Passivo.Close
- Close FILEHANDLE
- FILEDASALVARE = ""
- Stati.AddItem "Ricezione del file completata."
L'evento DataArrival
indica l'arrivo di dati al socket Passivo. Abbiamo detto che tali
dati andranno scritti all'interno del file. Ma è necessario effettuare
un controllo d'obbligo, ovvero che la somma dei dati ricevuti sia maggiore
o uguale della dimensione aspettata (DIMENSIONEFILE). In tal caso
sarà necessario eliminare i dati inutili che sono stati inviati
mediante l'utilizzo dell'istruzione Redim, avendo cura di specificare
la parola chiave Preserve per non distruggere il resto dei
dati (riga 105).
Solo allora sarà possibile completare la scrittura dei dati nel
file, inviare la risposta di fine del trasferimento " +OK: FINE"
e chiudere socket passivo e file.
- Else
- Put FILEHANDLE, , DATI
- Stati.AddItem "Salvato chunk di "
& UBound(DATI) + 1 & " bytes."
- Avanzamento.Caption = LOF(FILEHANDLE) &
" bytes ricevuti su " & DIMENSIONEFILE
- AvanzamentoProgress.Max = DIMENSIONEFILE
- AvanzamentoProgress.Value = LOF(FILEHANDLE)
- Socket.SendData " +OK: RECV " &
LOF(FILEHANDLE)
- End If
- End Sub
Se invece la somma dei dati inviati non corrisponde alla fine del file,
l'intero pacchetto sarà salvato nel file (riga 118), la barra di
avanzamento sarà aggiornata (righe 121 e 122) e verrà rimandato
indietro l'ACK " +OK: RECV
Dimensione" del totale dei dati finora ricevuti.
- Private Sub Stati_DblClick()
- Stati.Clear
- End Sub
Un'ultima funzioncina prima di vedere il funzionamento del programma
è legata al doppio click sopra la LisBox Stati. L'intera
lista sarà cancellata. Può essere utile quando essa diviene
troppo piena o per verificare l'andamento del trasferimento senza riavviare
il programma.
Avviare sia il client che il server su uno o due computer collegati.
Inserire nel client l'indirizzo IP
del computer in cui viene eseguito il server e premere il pulsante Connetti.
Se il server viene eseguito nella stesso computer del client, specificare
come IP l'indirizzo di loopback
127.0.0.1.
Specificare un file da trasferire e premere il pulsante Invia.
Se il file non esiste sarà mostrato un messaggio di errore. Specificare
quindi un nome di file valido e premere il pulsante Invia.
Il
server riceverà la richiesta di trasferimento. Premendo il pulsante
Si alla richiesta di trasferimento il programma client potrà avviare
il trasferimento.
Man mano che il trasferimento procede saranno mostrate le informazioni
di avanzamento nella ListBox del server e nella sua barra di avanzamento

Figura 7 |

Figura 8 |
Il
termine del trasferimento sarà notificato anche sul programma client
con una finestra di messaggio apposita.
|