272 lines
6.6 KiB
C++
272 lines
6.6 KiB
C++
|
#include "tcpclient.h"
|
||
|
|
||
|
#define BUF_SIZE 1024
|
||
|
|
||
|
const long SimpleTCPClient::SOCKET_ID = wxNewId();
|
||
|
|
||
|
BEGIN_EVENT_TABLE(SimpleTCPClient, wxEvtHandler)
|
||
|
EVT_SOCKET(SOCKET_ID, SimpleTCPClient::OnSocketEvent)
|
||
|
END_EVENT_TABLE()
|
||
|
|
||
|
|
||
|
DEFINE_EVENT_TYPE(EV_CONNECTED);
|
||
|
DEFINE_EVENT_TYPE(EV_CLIENTONDATA);
|
||
|
|
||
|
SimpleTCPClient::SimpleTCPClient(wxEvtHandler* parent, wxWindowID id)
|
||
|
{
|
||
|
|
||
|
m_id = id;
|
||
|
m_parent = parent;
|
||
|
logBox = NULL;
|
||
|
Client = NULL;
|
||
|
|
||
|
}
|
||
|
|
||
|
SimpleTCPClient::~SimpleTCPClient()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
// Read specified number of bytes to specified buffer from socket and return actual number of read bytes
|
||
|
int SimpleTCPClient::Read( void *buffer, unsigned int nbytes )
|
||
|
{
|
||
|
|
||
|
int result = 0;
|
||
|
|
||
|
// Read data
|
||
|
if (Client != NULL)
|
||
|
{
|
||
|
if ( Client->IsConnected() && !disconnected)
|
||
|
{
|
||
|
Client->Read(buffer, nbytes);
|
||
|
result = Client->LastCount(); // Set number of actually read bytes as result
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// Peek specified number of bytes to specified buffer from socket and return actual number of peeked bytes
|
||
|
// This doesn't remove the data from socket and it can be read afterwards
|
||
|
int SimpleTCPClient::Peek( void *buffer, unsigned int nbytes )
|
||
|
{
|
||
|
int result = 0;
|
||
|
// Peek data
|
||
|
if (Client != NULL && !disconnected)
|
||
|
{
|
||
|
if ( Client->IsConnected() )
|
||
|
{
|
||
|
Client->Peek(buffer, nbytes);
|
||
|
result = Client->LastCount(); // Set number of actually peeked bytes as result
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Read from socket and return a string (this should be mostly used for "human-readable" data)
|
||
|
wxString SimpleTCPClient::ReadStr()
|
||
|
{
|
||
|
wxString readString = "";
|
||
|
|
||
|
char buff[BUF_SIZE + 1]; // Buffer with byte for zero termination (end of string)
|
||
|
memset(buff, 0, sizeof(buff));
|
||
|
|
||
|
// Read data
|
||
|
if (Client != NULL) Client->Read(&buff, BUF_SIZE);
|
||
|
|
||
|
readString = buff;
|
||
|
|
||
|
return readString;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Peek data in socket without clearing the data and return a string (this should be mostly used for "human-readable" data)
|
||
|
wxString SimpleTCPClient::PeekStr()
|
||
|
{
|
||
|
wxString readString = "";
|
||
|
|
||
|
char buff[BUF_SIZE + 1]; // Buffer with byte for zero termination (end of string)
|
||
|
memset(buff, 0, sizeof(buff));
|
||
|
|
||
|
// Read data
|
||
|
if (Client != NULL) Client->Peek(&buff, BUF_SIZE);
|
||
|
|
||
|
readString = buff;
|
||
|
|
||
|
return readString;
|
||
|
}
|
||
|
|
||
|
void SimpleTCPClient::LogClient( wxString logText )
|
||
|
{
|
||
|
// Append text to provided wxTextCtrl (if exists)
|
||
|
// "(internal)" is added just to make it easier to understand how the application works
|
||
|
// It will show which messages came from the "inside" of tcpclient.cpp file
|
||
|
if (logBox != NULL) logBox->WriteText("(internal) " + logText + "\n");
|
||
|
}
|
||
|
|
||
|
void SimpleTCPClient::Connect(wxString hostname, unsigned int port)
|
||
|
{
|
||
|
if (Client == NULL)
|
||
|
{
|
||
|
wxIPV4address ipaddr;
|
||
|
ipaddr.Hostname(hostname);
|
||
|
ipaddr.Service(port);
|
||
|
|
||
|
LogClient("Opening client");
|
||
|
Client = new wxSocketClient();
|
||
|
Client->SetFlags(wxSOCKET_NOWAIT); // wxSOCKET_NONE is default but produced some segfaults on retry
|
||
|
|
||
|
Client->SetEventHandler(*this, SOCKET_ID);
|
||
|
Client->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
|
||
|
Client->Notify(true);
|
||
|
|
||
|
Client->Connect(ipaddr, false);
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
if ( Client->IsConnected() )
|
||
|
{
|
||
|
LogClient( "Already connected" );
|
||
|
}
|
||
|
else if ( !Client->IsConnected() )
|
||
|
{
|
||
|
LogClient( "Connection in progress" );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void SimpleTCPClient::WriteStr( wxString str_in )
|
||
|
{
|
||
|
if (Client != NULL) // No point in trying if client is not active
|
||
|
{
|
||
|
// Send string to server
|
||
|
if ( Client->IsConnected() )
|
||
|
{
|
||
|
Client->Write(str_in.c_str(), str_in.Length());
|
||
|
LogClient("Sent \"" + str_in + "\"");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SimpleTCPClient::Write( const void *data, unsigned int dataBytes )
|
||
|
{
|
||
|
if (Client != NULL) // No point in trying if client is not active
|
||
|
{
|
||
|
// Write data
|
||
|
if ( Client->IsConnected() )
|
||
|
{
|
||
|
Client->Write(data, dataBytes);
|
||
|
LogClient("Sent binary data");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SimpleTCPClient::WriteWait( const void *data, unsigned int dataBytes )
|
||
|
{
|
||
|
if (Client != NULL) // No point in trying if client is not active
|
||
|
{
|
||
|
// Write data
|
||
|
if ( Client->IsConnected() )
|
||
|
{
|
||
|
//wxSocketFlags tempFlags = Client->GetFlags();
|
||
|
|
||
|
//Client->SetFlags(wxSOCKET_BLOCK);
|
||
|
|
||
|
Client->Write(data, dataBytes);
|
||
|
Client->WaitForWrite(0,10);
|
||
|
|
||
|
//Client->SetFlags(tempFlags);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void SimpleTCPClient::OnRecv(wxSocketBase *sock)
|
||
|
{
|
||
|
|
||
|
LogClient("Incoming data");
|
||
|
|
||
|
// Put current socket in event data, so main application will be able to read it
|
||
|
wxCommandEvent myevent(EV_CLIENTONDATA, m_id);
|
||
|
myevent.SetClientData( sock );
|
||
|
wxPostEvent(m_parent, myevent);
|
||
|
|
||
|
|
||
|
//**** Data must be handled in main application.
|
||
|
//**** If main application will only read partial data,
|
||
|
//**** next events will be fired until all data is read.
|
||
|
//**** But if application does not read any data,
|
||
|
//**** data receive event will get stuck until it is manually read
|
||
|
|
||
|
//if ( sock->IsData() ) OnRecv( sock ); // Keep receiving if more data is available (this is default behavior anyway)
|
||
|
|
||
|
}
|
||
|
|
||
|
void SimpleTCPClient::Disconnect()
|
||
|
{
|
||
|
if (Client != NULL)
|
||
|
{
|
||
|
Client->Destroy();
|
||
|
Client->WaitForLost(0,50);
|
||
|
Client = NULL;
|
||
|
LogClient("Disconnecting from server");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SimpleTCPClient::OnSocketEvent(wxSocketEvent &event)
|
||
|
{
|
||
|
|
||
|
LogClient("Socket event");
|
||
|
|
||
|
wxSocketBase *sock = event.GetSocket();
|
||
|
|
||
|
switch(event.GetSocketEvent())
|
||
|
{
|
||
|
case wxSOCKET_CONNECTION:
|
||
|
{
|
||
|
LogClient("Successfully connected");
|
||
|
|
||
|
|
||
|
wxCommandEvent myevent(EV_CONNECTED, m_id);
|
||
|
myevent.SetClientData( sock );
|
||
|
wxPostEvent(m_parent, myevent);
|
||
|
disconnected = false;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case wxSOCKET_INPUT:
|
||
|
{
|
||
|
// Redirect to OnRecv function
|
||
|
OnRecv(event.GetSocket());
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case wxSOCKET_LOST:
|
||
|
{
|
||
|
LogClient("Disconnected");
|
||
|
sock->Close();
|
||
|
sock->Discard();
|
||
|
sock->Destroy();
|
||
|
Client->Close();
|
||
|
Client->Discard();
|
||
|
Client->Destroy();
|
||
|
Client = NULL;
|
||
|
disconnected = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
LogClient("Unknown event");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|