giovedì 19 agosto 2010

Intercettare gli eventi socket in un thread secondario

Questa seconda puntata è dedicata a un'altro argomento spinoso delle wxwidgets. In particolare vi fornirò un listato minimale sull'intercettazione di eventi socket in un thread secondario.
Purtroppo i thread non sono in grado di gestire eventi ed è del tutto inutile fare una doppia derivazione delle classi wxthread e wxEvtHandler. I listati sono piuttosto grezzi, incompleti, magari conterranno pure qualche errore, ma sono il primo prototipo funzionante che sono riuscito a scrivere.

Per raggiungere lo scopo occorre:
1) inserire la funzione non documentata wxSocketBase::Initialize(); prima di utilizzare le funzioni socket. E' consigliato inserirla all''inizio del software, in particolare io l'ho inserita all'interno della OnInit() dell'applicazione:

IMPLEMENT_APP(labApp);

bool labApp::OnInit()
{
    bool wxsOK = true;
    wxSocketBase::Initialize();

    if ( wxsOK )
    {
        labFrame* Frame = new labFrame(0);
        Frame->Show();
        SetTopWindow(Frame);
    }
    return wxsOK;
}

In secondo luogo dobbiamo creare una classe derivata da wxEvtHandler che permette di intercettare e gestire gli eventi. Questa classe l'ho chiamata SS_SocketEvents (NB: SS deriva dal mio nome e cognome). Contiene due funzioni, la OnSocketEvent e la OnServerEvent deputate rispettivamente ad intercettare gli eventi dei singoli socket che vengono aperti con la connessione dei vari client e gli eventi del server (fondalmentemente le nuove connessioni).

In terzo luogo dobbiamo creare il thread. Per brevità riporto il mio esempio così come l'ho scritto, in particolare ho chiamato la classe derivata IPCServer e riceve come argomento anche un puntatore di tipo wxEvtHandler per mantenere traccia dell'handler richiedente che ho utilizzato per inviare eventi dal thread alla gui principale, in particolare ho usato la funzione SendThreadMessage già vista in questo blog nella puntata precedente.

Infine scriviamo la EVENT_TABLE per intercettare gli eventi e lanciamo il thread:


file: sys.h

class SS_IPCServer;

class SS_SocketEvents : public wxEvtHandler
{
  public:
    SS_SocketEvents( SS_IPCServer * );
    void OnSocketEvent(wxSocketEvent& event);
    void OnServerEvent(wxSocketEvent& event);
    class SS_IPCServer * Parent;
  protected:
  DECLARE_EVENT_TABLE()
};

#define SS_IPC_Port 3000

enum
{
  IPC_SOCKET_ID = 100,
  IPC_SERVER_ID
};


class SS_IPCServer : public wxThread
{
public:
  SS_IPCServer(wxEvtHandler *);
  ~SS_IPCServer();

  void SetPort(int);
  void SendThreadMessage(wxString,int);
  virtual void * Entry();
  SS_SocketEvents * SoketEventHandler;

protected:

private:
    int s_TCP_port;
    wxSocketServer * s_server;
    int Start_TCP_Server();
    int s_numClients;
    wxEvtHandler * EH;
};


file: sys.cpp


BEGIN_EVENT_TABLE(SS_SocketEvents, wxEvtHandler)
EVT_SOCKET(IPC_SOCKET_ID, SS_SocketEvents::OnSocketEvent)
EVT_SOCKET(IPC_SERVER_ID, SS_SocketEvents::OnServerEvent)

END_EVENT_TABLE()



SS_SocketEvents::SS_SocketEvents(SS_IPCServer * Server) :wxEvtHandler()
{
  Parent=Server;
}

//============================================================

void SS_SocketEvents::OnSocketEvent(wxSocketEvent& event)
{
/*wxSOCKET_INPUT = 0
  wxSOCKET_OUTPUT = 1
  wxSOCKET_CONNECTION = 2
  wxSOCKET_LOST = 3*/

  this->Parent->SendThreadMessage(_("SOCKET EVENT !!!!"),IPCDebug);

  wxSocketBase * sock = (wxSocketBase*)event.GetClientData();

  int a=event.GetSocketEvent();
  this->Parent->SendThreadMessage((_("Received event code ")+IntTowxT(a)),IPCDebug);

if(sock)
{

  if ( event.GetSocketEvent() == wxSOCKET_INPUT ) {
    this->Parent->SendThreadMessage(_("SK wxSOCKET_INPUT !!!!!"),IPCDebug);
  }
  else if ( event.GetSocketEvent() == wxSOCKET_LOST ) {
    this->Parent->SendThreadMessage(_("SK wxSOCKET_LOST !!!!!"),IPCDebug);
  }
  else if ( event.GetSocketEvent() == wxSOCKET_CONNECTION ) {
    this->Parent->SendThreadMessage(_("SK wxSOCKET_CONNECTION !!!!!"),IPCDebug);
  }
  else this->Parent->SendThreadMessage(_("UNKNOWED SOCKET EVENT"),IPCDebug);
}

}


//============================================================

void SS_SocketEvents::OnServerEvent(wxSocketEvent& event)
{
/*wxSOCKET_INPUT = 0   wxSOCKET_OUTPUT = 1   wxSOCKET_CONNECTION = 2   wxSOCKET_LOST = 3*/

  this->Parent->SendThreadMessage(_("SERVER EVENT !!!!"),IPCDebug);

  wxSocketBase * sock = (wxSocketBase*)event.GetClientData();

  int a=event.GetSocketEvent();
  this->Parent->SendThreadMessage((_("Received event code ")+IntTowxT(a)),IPCDebug);

if(sock)
{

  if ( event.GetSocketEvent() == wxSOCKET_INPUT ) {
    this->Parent->SendThreadMessage(_("SK wxSOCKET_INPUT !!!!!"),IPCDebug);
  }
  else if ( event.GetSocketEvent() == wxSOCKET_LOST ) {
    this->Parent->SendThreadMessage(_("SK wxSOCKET_LOST !!!!!"),IPCDebug);
   }
  else if ( event.GetSocketEvent() == wxSOCKET_CONNECTION ) {
    this->Parent->SendThreadMessage(_("SK wxSOCKET_CONNECTION !!!!!"),IPCDebug);
  }
  else this->Parent->SendThreadMessage(_("UNKNOWED SOCKET EVENT"),IPCDebug);
}

}

//============================================================

SS_IPCServer::SS_IPCServer(wxEvtHandler *ev): wxThread(wxTHREAD_JOINABLE)
{
  s_TCP_port=0;
  s_server=NULL;
  EH=ev;
  SoketEventHandler=NULL;
}

//============================================================
SS_IPCServer::~SS_IPCServer()
{
  //Disconnect();
}

//============================================================
void* SS_IPCServer::Entry()
{
wxSocketBase * tempsok=NULL;
if(Start_TCP_Server()<0)
{
  SendThreadMessage(_("ERROR STARTING SOCKET THREAD"),IPCDebug);
  return(NULL);
}
while(1)
{
    if (TestDestroy())
    {
      delete s_server;
      break; // qui ci devi mettere la deallocazione
    }
    tempsok=s_server->Accept(false);
    if(tempsok) // new socketbase for client communication
    {
      SendThreadMessage(_("...incoming connection..."),IPCDebug);
      tempsok->SetClientData( (void*)this );
      tempsok->SetEventHandler(*SoketEventHandler, IPC_SOCKET_ID);
      tempsok->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG | wxSOCKET_CONNECTION); //
      tempsok->Notify(true);
    }
    tempsok=NULL;
    wxThread::Sleep(100);
}

}

//============================================================
void SS_IPCServer::SetPort(int port)
{
    s_TCP_port=port;
}

//============================================================

int SS_IPCServer::Start_TCP_Server()
{
  wxIPV4address Addr;
  Addr.Service(s_TCP_port);

  // Create the socket
  if(s_server) return(-1); // only 1 instance
  s_server = new wxSocketServer(Addr);
  if(!s_server) return(-2);
  if (! s_server->Ok()) return(-3); // We use Ok() here to see if the server is really listening
  s_server->SetClientData( (void*)this );
  SoketEventHandler=new SS_SocketEvents(this);
  if(!SoketEventHandler) SendThreadMessage(_("error creating SocketEventHandler"),IPCDebug);
  else{
    s_server->SetEventHandler(*SoketEventHandler, IPC_SERVER_ID);
    s_server->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG | wxSOCKET_CONNECTION);
    s_server->Notify(true);
  }
  s_numClients = 0;
  return(1);
}


file main.cpp // da qui viene lanciato il thread vero e proprio ed inizializzato il server.


    MyIPCServer=new SS_IPCServer(GetEventHandler());
    MyIPCServer->SetPort(SS_IPC_Port);
    if(MyIPCServer->Create((unsigned int)0)!=wxTHREAD_NO_ERROR) // lancia il thread che parte in modalità “suspended”. Lo 0 indica di usare lo stack di default
    {
      //ERROR creating IPC server
      delete MyIPCServer;
      return;
    }
    MyIPCServer->SetPriority(75); // setta la priorità del thread  (da 0 a 100 dove lo 0 indica la priorità minore.
    MyIPCServer->Run(); // questo lancia il thread*/


Spero di esservi stato utile anche se probabilmente ci sarà ancora qualche errore e qualche finezza da mettere a posto.

1 commento: