//metodo eseguito al click sul pulsante connetti
        private void btnConnect_Click(object sender, EventArgs e)
        {
            //vengono memorizzati il nome utente e
            //viene aggiornata la casella di testo
            _userName = txtUserName.Text.Trim();
            _setText = SetText;

            //viene creato e riempito un pacchetto di richiesta di join alla chat
            Packet sendData = new Packet();
            sendData.DataIdentifier = DataTypeIdentifier.Login;
            sendData.UserName = _userName;

            //viene creato un Socket UDP
            _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            //viene creato un oggetto contenente l'indirizzo IP e la porta del server
            IPAddress serverIP = IPAddress.Parse(txtServerAddress.Text);
            int serverPort = int.Parse(txtServerPort.Text);
            _epServer = new IPEndPoint(serverIP, serverPort);

            //il pacchetto creato viene convertito in un'array di byte
            _dataStream = sendData.ToByteArray();

            //il pacchetto creato viene spedito al server
            _clientSocket.BeginSendTo(_dataStream, 0, _dataStream.Length, SocketFlags.None, _epServer, SendData, null);

            //tutti gli oggetti vengono sempre passati per referenza
            //il client si mette ora in ascolto dei messaggi provenienti dal server
            EndPoint ep = _epServer;
            _dataStream = new byte[Properties.Settings.Default.MAX_PACKET_LENGTH];
            _clientSocket.BeginReceiveFrom(_dataStream, 0, _dataStream.Length, SocketFlags.None, ref ep, ReceiveData, null);
        }
        //metodo eseguito quando l'utente clicca sul pulsante invia
        private void btnSend_Click(object sender, EventArgs e)
        {
            if (txtMessage.Text == "")  //si controlla se il campo del messaggio è stato riempito
                return; //da implementare il messaggio d'errore

            //viene creato e riempito un nuovo pacchetto
            Packet sendData = new Packet();
            sendData.DataIdentifier = DataTypeIdentifier.Message;
            sendData.UserName = _userName;
            sendData.Message = txtMessage.Text;

            //il pacchetto viene convertito in un array di bytes
            byte[] data = sendData.ToByteArray();

            //metodo non bloccante, preferibile alle altre chiamate che sono invece bloccanti
            //questo è il modo più pulito di programmare perchè non viene occupata
            //la CPU e non occorre preoccuparsi della gestione dei thread
            _clientSocket.BeginSendTo(data, 0, data.Length, SocketFlags.None, _epServer, SendData, _epServer);

            //la casella di testo del messaggio viene svuotata
            txtMessage.Clear();
        }
        void ReceiveData(IAsyncResult result)
        {
            //i byte ottenuti vengono deserializzati
            Packet receivedData = new Packet(_dataStream);

            //si controlla il contenuto del campo Message del pacchetto ricevuto
            //un puntatore a null è uguale a ""
            if (!string.IsNullOrEmpty(receivedData.Message))
            {
                //per scrivere su un elemento grafico da un thread diverso da
                //quello che ha generato l'elemento, si usano la Invoke e le Delegate
                //txtConversation.Text += receivedData.Message; //infatti dà ERRORE
                Invoke(_setText, receivedData.Message);
            }

            EndPoint ep = _epServer;
            _clientSocket.BeginReceiveFrom(_dataStream, 0, _dataStream.Length, SocketFlags.None, ref ep, ReceiveData, _epServer);
        }
        //metodo eseguito alla chiusura della finestra
        private void frmClientUDP_FormClosing(object sender, FormClosingEventArgs e)
        {
            //viene visualizzata una MessageBox di conferma
            //di solito è meglio mettere più istruzioni su una riga che annidare troppo
            if (MessageBox.Show(this, "Vuoi terminare la sessione?", "Domanda", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.No)
            {
                e.Cancel = true;
                return;
            }

            //viene creato e riempito un pacchetto di tipologia Logout
            Packet sendData = new Packet();
            sendData.DataIdentifier = DataTypeIdentifier.Logout;
            sendData.UserName = _userName;

            //il pacchetto viene convertito in un array di bytes
            byte[] bytes = sendData.ToByteArray();

            //in questo caso si usa il socket in modo sincrono
            //con il metodo SendTo() per inviare il pacchetto
            _clientSocket.SendTo(bytes, 0, bytes.Length, SocketFlags.None, _epServer);

            //il Socket viene chiuso
            _clientSocket.Close();
        }