Casino online











Mercato forex






C16. I Socket


La classe System.Net.Sockets offre funzionalità per gestire la comunicazione tra due computer in rete. In questo capitolo mi sofferò sull'analisi dei metodi di comunicazione tramite TCP (Trasmission Control Protocol).

Server
Il server, nel nostro caso, è il computer sul quale risiede l'applicazione principale deputata alla gestione delle connessioni e dei servizi dei client esterni. Più in generale è una componente informatica che fornisce servizi ad altre componenti attraverso una rete. Per implementare un'applicazione Server da codice dobbiamo far sì che essa possa accettare connessioni da parte di altri. Per far questo è necessario usare la classe TcpListener, che si mette in ascolto su di una porta, e riferisce quando ci sono richieste di connessioni in attesa su di essa.
Private Listener As New Sockets.TcpListener(25) 
Questa riga di codice inizializza un TcpListener sulla porta 25 (quella di default per i server di posta :P). I metodi che comunicano ad esso di iniziare o terminare l'ascolto sono Start() e Stop(), mentre la funzione che comunica se ci sono richieste in attesa di essere accettate è Pending() (restituisce un valore Booleano). Per accettare la connessione è necessario richiamare la funzione AcceptTcpClient(), la quale restituisce un oggetto di tipo TcpClient connesso al rispettivo oggetto sul Client. In questo modo, Server e Client risultano legati da una connessione.

Client
La classe fondamentalmente usata per un client è TcpClient. I suoi membri più significativi sono:
  • Avaiable: restituisce il numero di bytes ricevuti e pronti per la lettura
  • Close: chiude la connessione
  • Connect(IP, P): tenta una connessione verso il server identificato da IP sulla porta P. IP può essere sia un indirizzo IP che DNS
  • Connected: restituisce True se è connesso, altrimento False
  • GetStream: funzione importantissima che restituisce un oggetto di tipo Sockets.NetworkStream su cui e da cui si scrivono e leggono tutti i dati scambiati tra client e server
  • ReceiveBufferSize: imposta la grandezza del buffer di bytes ricevuti
  • SendBufferSize: imposta la grandezza del buffer di bytes inviati

Costruire l'applicazione
Le classi TcpListener e TcpClient non possiedono eventi, perciò, per sapere quando ci sono connessioni in attesa o se si sono ricevuti dati, è necessario controllare con un timer. Sarà tmrControlConnection il timer che controlla se ci sono richieste di connessione sul Server, mentre tmrGetData quello che controlla la presenza di dati ricevuti. Ci sarà poi un pulsante cmdSend per inviare dati e una textbox txtSend contenente il messagio da inviare. Di seguito il codice. Solo per il server:
Imports System.Net.Sockets

'...

Public Listener As New TcpListener(25)

Private Sub tmrControlConnection_Tick(ByVal sender As Object, _ 
  ByVal e As EventArgs) Handles tmrControlConnection.Tick
  'Se ci sono connessioni in attesa
  If Listener.Pending Then
    'Inizializza il client
    Client = Listener.AcceptTcpClient
    'Inizializza lo stream
    NetStr = Client.GetStream
    'Termina il controllo del timer
    tmrControlConnection.Stop()
    'Termina l'ascolto del TcpListener
    Listener.Stop()
    'Attiva il timer per la ricezione di dati
    tmrGetData.Start()
  End If
End Sub

Private Sub Form1_Load(ByVal sender As Object, _ 
  ByVal e As EventArgs) Handles MyBase.Load
  'Esiste anche una versione di Start che accetta come parametro
  'il massimo numero di connessioni accettabili
  Listener.Start()
End Sub 
Per server e client:
Public Client As TcpClient
Public NetStr As NetworkStream

Private Sub tmrGetData_Tick(ByVal sender As Object, _ 
   ByVal e As EventArgs) Handles tmrGetData.Tick
  'Se il client è connesso
  If Client.Connected Then
    'Se ci sono dati da leggere, che possono essere letti
    If Client.Available > 0 And NetStr.CanRead Then
      'Legge i dati come array di bytes
      Dim Bytes(Client.ReceiveBufferSize) As Byte
      'Legge Client.ReceiveBufferSize bytes a partire dal primo
      'dallo stream e li deposita in Bytes
      'se ci sono bytes nulli, non verranno contati
      'di default, Client.ReceiveBufferSize = 8129
      NetStr.Read(Bytes, 0, Client.ReceiveBufferSize)

      'Trasforma i bytes ricevuti in stringa
      Dim S As String = System.Text.ASCIIEncoding.ASCII.GetString(Bytes)
      'Visualizza il messaggio
      MsgBox(S, MsgBoxStyle.Information)
    End If
  End If
End Sub

Private Sub cmdSend_Click(ByVal sender As Object, _ 
  ByVal e As EventArgs) Handles cmdSend.Click
  'Se il client è connesso
  If Client.Connected Then
    'Se si può scrivere sullo stream
    If NetStr.CanWrite Then
      'Converte il messaggio in bytes
      Dim Bytes() As Byte = _
      System.Text.ASCIIEncoding.ASCII.GetBytes(txtSend.Text)
      'E li scrive sullo stream
      NetStr.Write(Bytes, 0, Bytes.Length)
    End If
  End If
End Sub 
Solo per il client:
Private Sub Form1_Shown(ByVal sender As Object, _ 
  ByVal e As EventArgs) Handles MyBase.Shown
  'Prova a connettersi al server
  Client.Connect("127.0.0.1", 25)
  'Se è avvenuta la connessione
  If Client.Connected Then
    'Inizializza lo stream
    NetStr = Client.GetStream
  End If
End Sub 
127.0.0.1 è il localhost, ossia la stessa macchina su cui viene eseguito il software.


Esempio: File Sender
Fino ad ora si è parlato di inviare semplici messaggi sotto forma di stringhe, ma come ci si dovrebbe comportare nel caso il contenuto da inviare sia un file intero o, perchè no?, molti files? Il procedimento è lo stesso e con questo esempio fornirò una prova di come sia altrettanto semplice questo compito. L'applicazione File Sender si basa su un semplice scambio di interrogazioni tra i due computer, al termine delle quali si inizia l'invio effettivo del file. Per prima cosa il client comunica al server che sta per cominciare il flusso di dati; il server deve perciò rispondere in caso affermativo se l'utente è disposto al trasferimento: in questo caso, rimanda indietro un messaggio di conferma, e apre una nuova porta per i dati in arrivo; parallelamente, il client si connette alla porta aperta e inizia il trasferimento.


File Sender: server
Ho strutturato l'interfaccia del server in questo modo:
  • Label1 : una label esplicativo con il testo "Progresso:"
  • prgProgress : la barra del progresso
  • cmdListen : il pulsante "Ascolta"
  • strStatus : la status strip sul lato basso del form
  • lblStatus : la label contenuta in strStatus, con il compito di informare l'utente sullo stato dell'applicazione
  • tmrControlConnection : timer con Interval = 100 che ha il compito di controllare se ci sono richieste in attesa
  • tmrControlFile : timer con Interval = 100 con il compito di controllare se ci sono richieste in attesa sulla porta 1001, deputata in questo caso alla ricezione del file dal client
  • tmrGetData : timer con Interval = 100 con il compito di ottenere i messaggi inviati dal client e di rispondervi
  • bgReceiveFile : BackgroundWroker con WrokerReportProgress = True che ha il compito di ricevere il file dal client
E si presenta graficamente così:


Server

Ed ecco il codice:
Imports System.Net.Sockets
Imports System.Text.ASCIIEncoding
Imports System.ComponentModel
Public Class Form1
    'Listener: attende una connessione sulla porta 25
    'FileListener: attende una connessione sulla porta 1001. Questa
    '   ha il compito di trasferire i bytes del file
    Private Listener, FileListener As TcpListener
    'Client: l'oggetto che ha il compito di dialogare con
    '   il client e confermarne le operazioni
    'FileReceiver: l'oggetto che ha il compito di ricevere le
    '   informazioni contenute nel file e scriverle sulla macchina
    '   in forma di file concreto
    Private Client, FileReceiver As TcpClient
    'NetStream: lo stream su cui si scrivono i dati di comunicazione
    'NetFile: lo stream da cui si leggono i dati del file
    Private NetStream, NetFile As NetworkStream
    'Percorso su cui salvare il file
    Private FileName As String
    'Dimensione del file
    Private FileSize As Int64

    'I seguenti metodi semplificano le operazioni di invio e
    'ricezione di stringhe

    'Invia un messaggio su uno stream di rete
    Private Sub Send(ByVal Msg As String, ByVal Stream As NetworkStream)
        'Se si può scrivere
        If Stream.CanWrite Then
            'Converte il messaggio in binario
            Dim Bytes() As Byte = ASCII.GetBytes(Msg)
            'E lo scrive sul network stream
            Stream.Write(Bytes, 0, Bytes.Length)
        End If
    End Sub

    'Ottiene un messaggio dallo stream di rete
    Private Function GetMessage(ByVal Stream As NetworkStream) As String
        'Se si può leggere
        If Stream.CanRead Then
            Dim Bytes(Client.ReceiveBufferSize) As Byte
            Dim Msg As String
            'Legge i bytes arrivati
            Stream.Read(Bytes, 0, Bytes.Length)
            'Li converte in una stringa leggibile
            Msg = ASCII.GetString(Bytes)
            'E restituisce la stringa
            Return Msg.Normalize
        Else
            Return Nothing
        End If
    End Function

    Private Sub cmdListen_Click(ByVal sender As Object, _ 
        ByVal e As EventArgs) Handles cmdListen.Click
        If cmdListen.Text = "Ascolta" Then
            'Inizia ad ascoltare sulla porta 25
            Listener = New TcpListener(25)
            Listener.Start()
            'Attiva il timer per controllare le richieste di connesione
            tmrControlConnection.Start()
            'Cambia il testo e la funzione del pulsante
            cmdListen.Text = "Stop"
        Else
            'Ferma l'operazione di ascolto
            Listener.Stop()
            'Ripristina il testo
            cmdListen.Text = "Ascolta"
        End If
    End Sub

    Private Sub tmrControlConnection_Tick(ByVal sender As Object, _ 
        ByVal e As EventArgs) Handles tmrControlConnection.Tick
        'Se ci sono connessioni in attesa...
        If Listener.Pending Then
            'Ferma il timer per eseguire le operazioni
            tmrControlConnection.Stop()
            lblStatus.Text = "È stata ricevuta una richiesta"
            'Richiede all'utente se accettare la connessione
            If MessageBox.Show("È stata ricevuta una richiesta di connessione. Accettare?", _
                Me.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question) = _
                Windows.Forms.DialogResult.Yes Then
                'Acceta la connessione
                Client = Listener.AcceptTcpClient
                'Apre lo stream di rete condiviso
                NetStream = Client.GetStream
                'Termina l'ascolto
                Listener.Stop()
                'Rende il pulsante cmdListen inutilizzabile, poiché
                'una connessione è già stata aperta
                cmdListen.Enabled = False
                'Inizia la ricezione di messaggi
                tmrGetData.Start()
                lblStatus.Text = "Connessione riuscita!"
            Else
                'Altrimenti si rimette in attesa per altre connessioni
                tmrControlConnection.Start()
                lblStatus.Text = "In attesa di connessioni..."
            End If
        End If
    End Sub

    Private Sub tmrControlFile_Tick(ByVal sender As Object, _
        ByVal e As EventArgs) Handles tmrControlFile.Tick
        'Se c'è una richiesta, l'accetta subito
        If FileListener.Pending Then
            tmrControlFile.Stop()
            FileReceiver = FileListener.AcceptTcpClient
            NetFile = FileReceiver.GetStream
            'Ferma il listener
            FileListener.Stop()
            lblStatus.Text = "Flusso di informazioni aperto"
            'Attiva la ricezione di dati attraverso un background worker
            bgReceiveFile.RunWorkerAsync()
        End If
    End Sub

    Private Sub tmrGetData_Tick(ByVal sender As Object, _
        ByVal e As EventArgs) Handles tmrGetData.Tick
        If Client.Connected And Client.Available Then
            'Ferma il timer mentre si eseguono le operazioni
            tmrGetData.Stop()
            'Legge il messaggio
            Dim Msg As String = GetMessage(NetStream)

            If Msg.StartsWith("ConfirmTransfer") Then
                'Divide il messagio in parti in base al carattere pipe
                Dim Parts() As String = Msg.Split("|")
                'La prima parte è "ConfirmTransfer"
                'La seconda è il percorso del file sull'altro computer
                Dim File As String = Parts(1)
                'La terza è la dimensione
                Dim Size As Int64 = CType(Parts(2), Int64)
                'Ottiene solo il nome del file, senza percorso
                File = IO.Path.GetFileName(File)
                'Costruisce il percorso del file su questo computer,
                'salvandolo nella cartella del progetto (bin\Debug)
                FileName = Application.StartupPath & "\" & File
                'Imposta Size come variabile globale
                FileSize = Size
                'Richiede se accettare il trasferimento
                If MessageBox.Show(String.Format( _
                "È stata ricevuta una richiesta di trasferimento di {0} ({1} bytes). Acettare?", _
                File, Size), Me.Text, MessageBoxButtons.YesNo, _
                MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then
                    'Manda OK al client
                    Send("OK", NetStream)
                    'Intanto si mette in attesa sulla porta 1001 per
                    'l'invio dei bytes del file
                    FileListener = New TcpListener(1001)
                    FileListener.Start()
                    'E attiva il timer di controllo
                    tmrControlFile.Start()
                Else
                    'Altrimenti, risponde di no
                    Send("NO", NetStream)
                End If
            End If
            'Riprende il controllo
            tmrGetData.Start()
        End If
    End Sub

    Private Sub bgReceiveFile_DoWork(ByVal sender As Object, _ 
        ByVal e As DoWorkEventArgs) Handles bgReceiveFile.DoWork
        'Apre un nuovo stream in base al percorso costruito
        'nella procedura precedente
        Dim Stream As New IO.FileStream(FileName, IO.FileMode.Create)
        'Crea un indice che indica il progresso
        Dim Index As Int64 = 0

        lblStatus.Text = "In ricezione..."
        Do
            If FileReceiver.Available Then
                'Riceve i bytes necessari
                Dim Bytes(4096) As Byte
                Dim Msg As String = ASCII.GetString(Bytes)
                'Se i bytes sono un messaggio stringa e contengono
                '"END", oppure la dimensione giusta è già stata
                'raggiunta, allora si ferma
                If Msg.Contains("END") Or Index >= FileSize Then
                    Exit Do
                End If
                'Preleva i bytes dallo stream di rete
                NetFile.Read(Bytes, 0, 4096)
                'E li scrive sul file fisico
                Stream.Write(Bytes, 0, 4096)
                'Incrementa l'indice di 4096
                Index += 4096
                'E notifica il progresso
                bgReceiveFile.ReportProgress(Index * 100 / FileSize)
            End If
        Loop

        lblStatus.Text = "File ricevuto!"
        Stream.Close()
        MessageBox.Show("File ricevuto con successo!", Me.Text, _
            MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

    Private Sub bgReceiveFile_ProgressChanged(ByVal sender As Object, _
        ByVal e As ProgressChangedEventArgs) _ 
        Handles bgReceiveFile.ProgressChanged
        prgProgress.Value = e.ProgressPercentage
    End Sub
End Class 


File Sender: client
Ho struttura l'interfaccia del client in questo modo:
  • grpTrasnfer : un GroupBox con Text = "Trasferimento" che contiene tutti i controlli sul trasferimento del file
  • txtFile : una TextBox che contiene il percorso del file da inviare
  • cmdBrowse : un pulsante con Text = "Sfoglia" per permettere all'utente di selezionare un file in maniera semplice
  • cmdSend : un pulsante con Text = "Invia" che ha il compito di inoltrare la richiesta al server
  • prgProgress : una barra di progresso
  • cmdConnect : un pulsante con Text = "Connetti" con il compito di connettersi al server
  • strStatus : una StatusStrip nel lato inferiore del form
  • lblStatus : la label con il compito di tenere l'utente al corrente dello stato dell'applicazione
  • tmrGetData : un timer con Interval = 100 per ricevere e inviare messaggi al server
  • bgSendFile : un BackgroundWroker con WrokerReportProgress = True che ha il compito di inviare il file
L'interfaccia si presenta così:


Client

E questo è il codice:
Imports System.Net.Sockets
Imports System.Text.ASCIIEncoding
Imports System.ComponentModel
Public Class Form1
    'Client: il client che si dovrà connettere al server
    'FileSender: il client che ha il compito di trasferire i
    '   pacchetti di informazioni al server
    Private Client, FileSender As TcpClient
    'NetStream: lo stream su cui scrivere i dati di comunicazione
    'NetFile: lo stream per inviare i dati da scrivere sul file
    Private NetStream, NetFile As NetworkStream
    'L'IP del server a cui connettersi
    Private IP As String

    'I seguenti metodi semplificano le operazioni di invio e
    'ricezione di stringhe

    'Invia un messaggio su uno stream di rete
    Private Sub Send(ByVal Msg As String, ByVal Stream As NetworkStream)
        'Se si può scrivere
        If Stream.CanWrite Then
            'Converte il messaggio in binario
            Dim Bytes() As Byte = ASCII.GetBytes(Msg)
            'E lo scrive sul network stream
            Stream.Write(Bytes, 0, Bytes.Length)
        End If
    End Sub

    'Ottiene un messaggio dallo stream di rete
    Private Function GetMessage(ByVal Stream As NetworkStream) As String
        'Se si può leggere
        If Stream.CanRead Then
            Dim Bytes(Client.ReceiveBufferSize) As Byte
            Dim Msg As String
            'Legge i bytes arrivati
            Stream.Read(Bytes, 0, Bytes.Length)
            'Li converte in una stringa leggibile
            Msg = ASCII.GetString(Bytes)
            'E restituisce la stringa
            Return Msg.Normalize
        Else
            Return Nothing
        End If
    End Function

    Private Sub cmdConnect_Click(ByVal sender As Object, _ 
        ByVal e As EventArgs) Handles cmdConnect.Click
        'Ottiene l'IP del server
        IP = InputBox("Inserire l'IP del server:", Me.Text)

        'Controlla che l'IP non sia nullo o vuoto
        If String.IsNullOrEmpty(IP) Then
            MessageBox.Show("Connessiona annullata!", Me.Text, _
                MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        'Inizializza un nuovo client
        Client = New TcpClient
        'E tenta la connessione all'IP dato, sulla porta 25
        lblStatus.Text = "Connessione in corso..."
        Try
            Client.Connect(IP, 25)
        Catch SE As SocketException
            MessageBox.Show("Impossibile stabilire una connessione!", _
            Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End Try
        'Se la connessione è riuscita, ottiene lo
        'stream condiviso di rete direttamente collegato con
        'il networkstream del server
        If Client.Connected Then
            'Ora si è sicuri di essere connessi:
            'sblocca i comandi per il trasferimento
            NetStream = Client.GetStream
            grpTransfer.Enabled = True
            lblStatus.Text = "Connessione riuscita!"
        End If
    End Sub

    Private Sub cmdBrowse_Click(ByVal sender As Object, _   
        ByVal e As EventArgs) Handles cmdBrowse.Click
        Dim Open As New OpenFileDialog
        Open.Filter = "Tutti i file|*.*"
        If Open.ShowDialog = Windows.Forms.DialogResult.OK Then
            txtFile.Text = Open.FileName
        End If
    End Sub

    Private Sub cmdSend_Click(ByVal sender As Object, _ 
        ByVal e As EventArgs) Handles cmdSend.Click
        'Controlla che il file esista
        If Not IO.File.Exists(txtFile.Text) Then
            MessageBox.Show("Il file non esiste!", Me.Text, _
            MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        'Se si è connessi e si può scrivere
        'sullo stream di rete...
        If Client.Connected AndAlso NetStream.CanWrite Then
            'Manda un messaggio al server, chiedendo
            'conferma del trasferimento. Nel messaggio immette anche
            'alcune informazioni riguardo il nome e la
            'dimensione del file
            Dim Msg As String = _
                String.Format("ConfirmTransfer|{0}|{1}", txtFile.Text, _ 
                FileLen(txtFile.Text))
            'Invia il messaggio con la procedura scritta sopra
            Send(Msg, NetStream)

            'Attiva il timer per controllare i dati arrivati
            tmrGetData.Start()
            'Disattiva il pulsante per evitare più azioni
            'contemporanee indesiderate
            cmdSend.Enabled = False
            lblStatus.Text = "In attesa di conferma dal server..."
        End If
    End Sub

    Private Sub tmrGetData_Tick(ByVal sender As Object, _ 
        ByVal e As EventArgs) Handles tmrGetData.Tick
        If Client.Connected AndAlso Client.Available Then
            'Ferma il timer mentre si eseguono le operazioni
            tmrGetData.Stop()
            'Legge il messaggio
            Dim Msg As String = GetMessage(NetStream)

            'Uso Contains per un semplice motivo. Quando si converte
            'un array di bytes in una stringa, ci possono essere
            'caratteri speciali successivi a questa, come ad esempio
            'il NULL terminator (carattere 00), che ne compromettono
            'la struttura. 
            If Msg.Contains("OK") Then
                'Termina questa connessione e si connette
                'alla porta deputata alla ricezione dei file
                FileSender = New TcpClient
                FileSender.Connect(IP, 1001)
                If FileSender.Connected Then
                    'Ottiene lo stream associato a questa operaizone
                    NetFile = FileSender.GetStream
                    'E inizia la trasmissione dei dati
                    bgSendFile.RunWorkerAsync(txtFile.Text)
                End If
            ElseIf Msg.Contains("NO") Then
                MessageBox.Show("Il server ha rifiutato il trasferimento!", _
                Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                cmdSend.Enabled = True
            End If

            'Riprende il controllo dei dati
            tmrGetData.Start()
        End If
    End Sub

    Private Sub bgSendFile_DoWork(ByVal sender As Object, _ 
        ByVal e As DoWorkEventArgs) Handles bgSendFile.DoWork
        'Ottiene il nome del file dall'argomento passato al metodo
        'RunWorkerAsync nella procedura precedente
        Dim FileName As String = e.Argument
        'Crea un nuovo lettore del file a basso livello, così
        'da poter ottenere bytes di informazione anziché caratteri
        'come nello StreamReader
        Dim Reader As New IO.FileStream(FileName, IO.FileMode.Open)
        'Calcola la grandezza del file, per poter poi tenere
        'l'utente al corrente della percentuale di completamento
        Dim Size As Int64 = FileLen(FileName)
        'Un blocco di bytes da 4096 posti. Il file viene spedito in
        '"pacchettini" per evitare di sovraccaricare la connessione
        Dim Bytes(4095) As Byte

        'Se il file è più grande di 4KiB, lo divide
        'in blocchi di dati da 4096 bytes
        If Size > 4096 Then
            For Block As Int64 = 0 To Size Step 4096
                'Se i bytes rimanenti sono più di 4096,
                'ne legge un blocco intero
                If Size - Block >= 4096 Then
                    Reader.Read(Bytes, 0, 4096)
                Else
                    'Altrimenti un blocco più piccolo
                    Reader.Read(Bytes, 0, Size - Block)
                End If
                'Scrive i dati prelevati sullo stream di rete,
                'inviandoli così al server
                NetFile.Write(Bytes, 0, 4096)
                'Riporta la percentuale all'utente
                bgSendFile.ReportProgress(Block * 100 / Size)
                'Smette per 30ms, così da dare tempo dal
                'server di poter processare i pacchetti uno per
                'uno, evitando confusione
                Threading.Thread.Sleep(30)
            Next
        Else
            'Se il file è minore di 4KiB, lo invia tutto
            'direttamente dal server
            Reader.Read(Bytes, 0, Size)
            NetFile.Write(Bytes, 0, Size)
        End If
        Reader.Close()

        'Percentuale massima: lavoro terminato
        bgSendFile.ReportProgress(100)
        Threading.Thread.Sleep(100)
        'Comunica la fine delle operazioni
        NetFile.Write(ASCII.GetBytes("END"), 0, 3)
        MessageBox.Show("File inviato con successo!", Me.Text, _
            MessageBoxButtons.OK, MessageBoxIcon.Information)
        cmdSend.Enabled = True
    End Sub

    Private Sub bgSendFile_ProgressChanged(ByVal sender As Object, _ 
        ByVal e As ProgressChangedEventArgs) _
        Handles bgSendFile.ProgressChanged
        'Aggiorna la progressbar
        prgProgress.Value = e.ProgressPercentage
    End Sub
End Class 




 

The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.