Introdução
Em computação sockets são basicamente o elo de ligação entre duas máquinas na rede. O socket pode ser entendido como sendo IP:Porta (Protocolo TCP) onde os dados são transmitidos entre cliente e servidor como uma cadeia de bytes. Existe também a transmissão em forma de datagramas não confiáveis (Protocolo UDP).
O endereço IP identifica a máquina à qual eu quero me conectar e a porta o serviço executando nesta máquina. Para que um cliente se conecte à determinada máquina em determinada porta é preciso que um servidor esteja 'escutando' nesta porta.
Em wxWidgets nós temos algumas classes usadas com o propósito de comunicação em rede através de sockets. A classe wxSocketBase, de onde descendem as demais classes relacionadas com sockets, como wxSocketClient e wxSocketServer. Para mais informações acerca destas classes visite: http://docs.wxwidgets.org/stable/wx_wxsocketbase.html#wxsocketbase.
Exemplo
Em nosso exemplo vamos escrever uma aplicação que será o cliente e outra que será o servidor. Para que o cliente se conecte é preciso que o servidor esteja em execução, 'escutando' na porta definida. O cliente e o servidor podem ser executados na mesma máquina ou em máquinas separadas, conectadas à mesma rede. No primeiro caso fornecemos para o cliente como nome do host servidor a string 'localhost', no segundo caso fornecemos uma string com o número ip da máquina onde está o aplicativo servidor. A aplicação cliente enviará uma string ao servidor que retornará esta mesma string para o cliente, então, o cliente compara a string recebida de volta pelo servidor com a string enviada. Uma mensagem é mostrada dependendo do resultado da comparação (sucesso ou falha).
Vamos ao exemplo:
//cliente.cpp
#include <wx/wx.h>#include <wx/socket.h>
#include <wx/wfstream.h>
class MeuFrame : public wxFrame {
public:
MeuFrame(const wxString &titulo);
void Conectar(wxCommandEvent &evt);
void Enviar(wxCommandEvent &evt);
void Desconectar(wxCommandEvent &evt);
void EscreveStatus(wxString status, int onde);
private:
wxBoxSizer *box;
wxButton *btn_conectar;
wxButton *btn_enviar;
wxButton *btn_desconectar;
wxTextCtrl *txt_status;
wxSocketClient *m_socket;
};
MeuFrame::MeuFrame(const wxString &titulo):
wxFrame(NULL, wxID_ANY, titulo, wxDefaultPosition, wxSize(360,200))
{
wxPanel *painel = new wxPanel(this);
box = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *box_texto = new wxBoxSizer(wxVERTICAL);
wxGridSizer *grid_botoes = new wxGridSizer(1,3,5,5);
txt_status = new wxTextCtrl(painel, -1, _(""),wxDefaultPosition,wxDefaultSize,wxTE_MULTILINE | wxTE_READONLY);
box_texto->Add(txt_status,1,wxEXPAND);
btn_conectar = new wxButton(painel,1000,_("Conectar"));
Connect(1000,wxEVT_COMMAND_BUTTON_CLICKED,wxCommandEventHandler(MeuFrame::Conectar));
btn_enviar = new wxButton(painel,1001,_("Enviar"));
Connect(1001,wxEVT_COMMAND_BUTTON_CLICKED,wxCommandEventHandler(MeuFrame::Enviar));
btn_desconectar = new wxButton(painel,1002,_("Desconectar"));
Connect(1002,wxEVT_COMMAND_BUTTON_CLICKED,wxCommandEventHandler(MeuFrame::Desconectar));
grid_botoes->Add(btn_conectar,1,wxALL,5);
grid_botoes->Add(btn_enviar,1,wxALL,5);
grid_botoes->Add(btn_desconectar,1,wxALL,5);
box->Add(box_texto,1,wxEXPAND);
box->Add(grid_botoes,1,wxEXPAND);
painel->SetSizer(box);
//criamos o socket
m_socket = new wxSocketClient();
//definimos o manipulador de eventos para o socket
m_socket->SetEventHandler(*this,1003);
//
m_socket->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
m_socket->Notify(true);
#if wxUSE_STATUSBAR
//barra de status da aplicação
CreateStatusBar(2);
#endif
EscreveStatus(_("Desconectado"),1);
Centre();
}
void MeuFrame::Conectar(wxCommandEvent &evt)
{
wxIPV4address end;
wxString host = wxGetTextFromUser(_("Digite o endereço do servidor"),
_("Conectar"),
_("localhost")
);
//atribuimos o nome do host digitado pelo usuario
//no nosso caso 'localhost' (o host no qual o servidor está executando),
//mas poderia ser um numero de ip de uma máquina na rede
end.Hostname(host);
//o numero da porta do serviço ao qual queremos nos conectar
//utilizamos um numero alto para evitar conflitos com portas que já são utilizadas por outros serviços
end.Service(3000);
//fazemos uma tentativa de conexão
//quando o segundo parametro é true a aplicação é paralisada
//enquantro a conexão não se estabelece
//quando é false a conexão é tentada e retorna-se logo em seguida
//mesmo sem sucesso
//em caso de sucesso o método retorna true, caso contrário, retorna false
m_socket->Connect(end,false);
//esperamos 10 segundos tentando estabelecer a conexão
m_socket->WaitOnConnect(10);
if(m_socket->IsConnected())
{
EscreveStatus(_("Conexão realizada com sucesso!"),0);
EscreveStatus(_("Conectado"),1);
}
else
{
EscreveStatus(_("Não foi possível realizar a conexão!"),0);
EscreveStatus(_("Desconectado"),1);
wxMessageBox(_("Não é possível estabelecer uma conexão com o host especificado!"),_("Alerta"));
}
}
void MeuFrame::Enviar(wxCommandEvent &evt)
{
unsigned char tam;
wxString str_envio;
wxChar *str_resposta;
m_socket->SetFlags(wxSOCKET_WAITALL);
str_envio = wxGetTextFromUser(_("Digite um texto para enviar ao servidor"),
_("Texto"),
_("")
);
str_resposta = new wxChar[wxStrlen(str_envio)+1];
//tamanho da string enviada em bytes
tam = (unsigned char) ((wxStrlen(str_envio)+1) * sizeof(wxChar));
txt_status->AppendText(_("\nEnviando um buffer de texto para o servidor..."));
//o primeiro byte é o tamanho da string o restante é a string em si
m_socket->Write(&tam,1);
m_socket->Write(str_envio,tam);
if(m_socket->Error())
{
txt_status->AppendText(_("\nFalha!"));
return;
}
else
{
txt_status->AppendText(_("\nSucesso!"));
}
txt_status->AppendText(_("\nRecebendo o texto de volta do servidor..."));
//armazenamos a string de resposta do servidor em str_resposta
m_socket->Read(str_resposta,tam);
txt_status->AppendText(_("\nComparando as duas strings..."));
//comparamos a string enviada com a string de resposta do servidor
if(memcmp(str_resposta,str_envio,tam) == 0)
{
wxString s;
s.Printf(_("\nSucesso! String Recebida: %s"), str_resposta);
txt_status->AppendText(s);
}
else
{
txt_status->AppendText(_("\nFalha na recepção!"));
}
delete[] str_resposta;
}
void MeuFrame::Desconectar(wxCommandEvent &evt)
{
//desconectamos do servidor
m_socket->Close();
EscreveStatus(_(""),0);
EscreveStatus(_("Desconectado"),1);
}
void MeuFrame::EscreveStatus(wxString status, int onde)
{
#if wxUSE_STATUSBAR
SetStatusText(status,onde);
#endif
}
class MinhaApp : public wxApp
{
public:
virtual bool OnInit();
};
IMPLEMENT_APP(MinhaApp)
bool MinhaApp::OnInit()
{
MeuFrame *frame = new MeuFrame(_("Sockets com wxWidgets: Cliente"));
frame->Show(true);
return true;
}
//servidor.cpp
#include <wx/wx.h>
#include <wx/socket.h>
enum
{
ID_SERVER = 1000,
ID_SOCKET
};
class MeuFrame : public wxFrame
{
public:
MeuFrame(const wxString titulo);
void OnSocketEvent(wxSocketEvent &evt);
void OnServerEvent(wxSocketEvent &evt);
void Recebe(wxSocketBase *socket);
void EscreveStatus();
private:
wxTextCtrl *texto;
wxSocketServer *m_server;
int num_clientes;
};
MeuFrame::MeuFrame(const wxString titulo):
wxFrame(NULL, wxID_ANY, titulo, wxDefaultPosition, wxSize(360,200))
{
texto = new wxTextCtrl(this,-1,_(""), wxDefaultPosition,wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY);
wxIPV4address end;
end.Service(3000);
m_server = new wxSocketServer(end);
//verificamos se o servidor está 'escutando'
if(m_server->Ok())
{
texto->AppendText(_("Servidor escutando...\n"));
}
else
{
texto->AppendText(_("Não é possével escutar na porta especificada\n"));
return;
}
//conectamos os eventos de socket aos seus respectivos manipuladores
Connect(ID_SERVER,wxEVT_SOCKET,wxSocketEventHandler(MeuFrame::OnServerEvent));
Connect(ID_SOCKET,wxEVT_SOCKET,wxSocketEventHandler(MeuFrame::OnSocketEvent));
//configura manipulador de eventos do servidor
m_server->SetEventHandler(*this,ID_SERVER);
//especifica quais eventos de socket serão enviados ao manipulador de eventos
m_server->SetNotify(wxSOCKET_CONNECTION_FLAG);
//Notify(true) habilita os eventos de socket especificados
m_server->Notify(true);
num_clientes = 0;
#if wxUSE_STATUSBAR
//barra de status da aplicação
CreateStatusBar(1);
#endif
EscreveStatus();
}
void MeuFrame::Recebe(wxSocketBase *socket)
{
unsigned char tam;
char *str;
texto->AppendText(_("Recebendo dados...\n"));
socket->SetFlags(wxSOCKET_WAITALL);
//lemos o tamanho da string
socket->Read(&tam,1);
str = new char[tam];
//lemos a string recebida e armazenamos em str
socket->Read(str,tam);
texto->AppendText(_("Enviando a string de volta...\n"));
//enviamos a string recebida de volta ao cliente
socket->Write(str,tam);
delete[] str;
texto->AppendText(_("String enviada ao cliente...\n"));
}
void MeuFrame::OnServerEvent(wxSocketEvent &evt)
{
wxSocketBase *sock;
//aceita uma nova conexão e cria um novo objeto wxSocketBase que representa o lado servidor da conexão
sock = m_server->Accept(false);
if(sock)
{
texto->AppendText(_("Novo cliente aceito\n"));
}
else
{
texto->AppendText(_("Não foi possível aceitar uma nova conexão\n"));
return;
}
sock->SetEventHandler(*this, ID_SOCKET);
sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
sock->Notify(true);
num_clientes++;
EscreveStatus();
}
void MeuFrame::OnSocketEvent(wxSocketEvent &evt)
{
wxSocketBase *sock = evt.GetSocket();
switch(evt.GetSocketEvent())
{
case wxSOCKET_INPUT:
{
//desabilitamos novos eventos de entrada temporariamente
sock->SetNotify(wxSOCKET_LOST_FLAG);
//recebemos e reenviamos a string para o cliente
Recebe(sock);
//habilitamos os eventos de entrada novamente
sock->SetNotify(wxSOCKET_LOST_FLAG | wxSOCKET_INPUT_FLAG);
break;
}
case wxSOCKET_LOST:
{
num_clientes--;
EscreveStatus();
texto->AppendText(_("Deletando socket.\n\n"));
//destruimos o socket
sock->Destroy();
break;
}
default: ;
}
}
void MeuFrame::EscreveStatus()
{
wxString s;
s.Printf(_("Numero de clientes conectados: %d"), num_clientes);
#if wxUSE_STATUSBAR
SetStatusText(s,0);
#endif
}
class MinhaApp : public wxApp
{
public:
virtual bool OnInit();
};
IMPLEMENT_APP(MinhaApp)
bool MinhaApp::OnInit()
{
MeuFrame *frame = new MeuFrame(_("Sockets com wxWidgets: Servidor"));
frame->Show(true);
return true;
}
Nenhum comentário:
Postar um comentário