wxWidgets

Socket

Il progetto che si propone di sviluppare è suddiviso in tre parti:

  1. Una comunicazione tra un server e uno o più client. In questo primo progetto il Server è in grado di inviare messaggi solo all'ultimo client da cui ha ricevuto un pacchetto.

  2. Una gestione di connessioni multiple da parte del server.

  3. Un gioco in rete.

Nota: per la realizzazione di questo progetto si assume di avere completato il progetto delle applicazioni Client e Server, quindi le modifiche indicate di seguito, si intendono apportate all'applicazione server già collaudata.

L'interfaccia per gestire più connessioni:

L'elenco degli utenti connessi veniva mostrato in un componente wxMemo. Sostituire questo componente con un wxListBox, in questo modo, sull'interfaccia del server sarà possibile selezionare una riga e inviare un messaggio al client selezionato.

Aggiungere due wxStaticText, uno per specificare il ruolo della casella di testo, l'altra per indicare a chi verrà inviato il messaggio.

Inoltre aggiungere una casella di testo wxEdit e un pulsante con Caption=Invia.

L'applicazione Server

In questo esempio si stabilisce un numero massimo di connessioni che si prevede di accettare. Il server resta sempre in ascolto sulla stessa porta e, per ogni richiesta di apertura connessione, crea un oggetto socket.

Aprire la scheda del file ServerFrm.h.


class serverFrm : public wxFrame
{
    private:
        wxSocketServer *server;
        wxSocketBase *client[20];
        int nClient;
    private:
        DECLARE_EVENT_TABLE();

La classe contiene un array di 20 puntatori a oggetti di classe wxSocketBase.

Aprire il file ServerFrm.cpp.

Nel Distruttore della classe aggiungere l'istruzione che serve a rilasciare lo spazio riservato per creare l'oggetto server:


serverFrm::~serverFrm()
{
    delete server;
}

Adesso il gestore dell'evento onServerEvent viene modificato come segue:


void serverFrm::OnServerEvent(wxSocketEvent& event) {
    switch(event.GetSocketEvent()) {
        case wxSOCKET_CONNECTION: {
          nClient++;
          client[nClient] = server->Accept(false);
          WxMemo2->AppendText(_("\nAccettata connessione da:"));

          wxString msg;
          msg.Printf(_("%d Client Connessi"), ++nClient);
          WxStatusBar1->SetStatusText(msg, 1);
      
          wxIPV4address ipClient;
          client[nClient]->GetPeer(ipClient);
          wxString ipV4Client = ipClient.IPAddress();
          WxListBox1->InsertItems(1, &ipV4Client, 0);
          WxStaticText5->SetLabel(ipV4Client);
          
          client[nClient]->SetEventHandler(*this, SOCKET_ID);
          client[nClient]->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG | wxSOCKET_CONNECTION_FLAG);
          client[nClient]->Notify(true);
        }
    }

Il gestore onServerEvent viene richiamato quando si genera un evento "Richiesta di apertura connessione".

Ad ogni richiesta di connessione che riceve, il server crea un nuovo oggetto di classe wxSocketBase e ne assegna il riferimento all'elemento corrispondente nell'array client, quindi risponde al client che la connessione è stata accettata.

L'indirizzo del client viene aggiunto alla List Box con il metodo InsertItems. Lo stesso indirizzo, viene aggiornato nello StaticText che indica il destinatario corrente del messaggio da inviare.

Infine per questo nuovo oggetto vngono abilitati gli eventi che esso deve gestire.

Gestione degli eventi Ricezione Dati


void serverFrm::OnSocketEvent(wxSocketEvent& event) {
    client[0] = event.GetSocket();
    switch(event.GetSocketEvent()) {
        case wxSOCKET_INPUT: {
            char buf[100];
            client[0]->Read(buf, sizeof(buf));
            wxIPV4address ipClient;
            client[0]->GetPeer(ipClient);
            wxString ipV4Client = ipClient.IPAddress();
            WxMemo1->AppendText(_("\nMessaggio da: ") + ipV4Client + _("\n") + _(buf));
            wxString risposta = wxGetTextFromUser(
                "Rispondi al messaggio", "Test", "ciao"
            );
            client[0]->Write(risposta, risposta.Len());
            break;
        }
        case wxSOCKET_LOST: {
            WxMemo1->AppendText(_("\nSocket eliminato"));
            for (int i=0; i<nClient; i++)
              client[i]->Destroy();
            break;
        }
    }
}

Il gestore dell'evento elemento selezionato

Il metodo GetSelection() restituisce l'indice dell'elemento selezionato nella ListBox, mentre il metodo GetString() restituisce la stringa selezionata. La stringa viene anche riscritta nella casella StaticText per evidenziare il destinatario del prossimo messaggio.

void serverFrm::WxListBox1Selected(wxCommandEvent& event)
{
	// insert your code here
	int i = WxListBox1->GetSelection();
	wxString item;
	item = WxListBox1->GetString(i);
	WxStaticText5->SetLabel(item);
}

Gestore dell'evento generato dal pulsante Invia.

void serverFrm::WxButton2Click(wxCommandEvent& event)
{
	// insert your code here
	wxString risposta = WxEdit1->GetValue();

	int i = WxListBox1->GetSelection();
	if (i==wxNOT_FOUND) {
        wxMessageBox(_("Seleziona il destinatario"));
        return;
    }
    client[i]->Write(risposta, risposta.Len());
}