This repository has been archived on 2024-12-16. You can view files and clone it, but cannot push or open issues or pull requests.
cb-legacy-dev/Samples/tcptest/tcpserver.cpp
2023-11-18 17:56:10 +01:00

493 lines
13 KiB
C++

#include "tcpserver.h"
#define BUF_SIZE 1024
const long SimpleTCPServer::SERVER_ID = wxNewId();
const long SimpleTCPServer::CLIENT_ID = wxNewId();
BEGIN_EVENT_TABLE(SimpleTCPServer, wxEvtHandler)
EVT_SOCKET(SERVER_ID, SimpleTCPServer::OnServerEvent)
EVT_SOCKET(CLIENT_ID, SimpleTCPServer::OnClientEvent)
END_EVENT_TABLE()
DEFINE_EVENT_TYPE(EV_CLIENTCONNECTED);
DEFINE_EVENT_TYPE(EV_CLIENTDISCONNECTED);
DEFINE_EVENT_TYPE(EV_ONDATA);
DEFINE_EVENT_TYPE(EV_ONERROR);
DEFINE_EVENT_TYPE(EV_ONCLOSE);
SimpleTCPServer::SimpleTCPServer(wxEvtHandler* parent, wxWindowID id)
{
maxClients = 10; // Clients beyond that will be rejected by server (more than 1 is not handled well right now)
m_id = id;
m_parent = parent;
logBox = NULL;
logLevel = 0;
Listener = NULL;
currentClient = new wxSocketBase(); // Socket of current/most recent client will be stored here
}
SimpleTCPServer::~SimpleTCPServer()
{
}
void SimpleTCPServer::LogServer( 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 tcpserver.cpp file
if (logBox != NULL) logBox->WriteText("(internal) " + logText + "\n");
}
void SimpleTCPServer::Open(unsigned int port)
{
if (Listener == NULL)
{
wxIPV4address ipaddr;
ipaddr.AnyAddress();
if (port == NULL)
{
ipaddr.Service( srvPort );
}
else ipaddr.Service( port );
LogServer("Opening server");
Listener = new wxSocketServer(ipaddr);
connectedClients = 0;
currentClient = NULL;
if(Listener->Ok())
LogServer("Started listening");
else
LogServer("Could not start listening");
Listener->SetEventHandler(*this, SERVER_ID);
Listener->SetNotify(wxSOCKET_CONNECTION_FLAG);
Listener->Notify(true);
}
else
{
LogServer("Server already opened");
}
}
void SimpleTCPServer::Close()
{
if (Listener)
{
/** Removed in favor of map of clients
// Iterate over list of connected clients to disconnect them all
std::list<wxSocketBase*>::iterator it;
for (it = clients.begin(); it != clients.end(); ++it)
{
(*it)->Destroy(); // Disconnect client
}
clients.clear(); // Clear the list after everyone is disconnected
**/
if (connectedClients >0)
{
// Iterate over map of connected clients
for( std::map<wxSocketBase*, wxString>::const_iterator it = clientMap.begin();
it != clientMap.end(); ++it)
{
it->first->Destroy(); // Disconnect client ( ->first means first element of map, that is wxSocketBase )
}
clientMap.clear(); // Clear the map of clients
connectedClients = 0; // Clear number of clients
}
currentClient = NULL; // Clear current client
Listener->Destroy(); // Remove the server
Listener = NULL;
LogServer("Closing server");
// Post event about server closing
//wxCommandEvent myevent(EV_ONCLOSE, m_id);
//wxPostEvent(m_parent, myevent);
}
else LogServer("Server already closed");
}
// Peek buffer, provide socket to read or use default one
int SimpleTCPServer::Peek( void *buffer, unsigned int nbytes, wxSocketBase* sock )
{
int result = 0;
// Read from specified socket or default socket if not specified
if (sock != NULL)
{
sock->Peek(buffer, nbytes);
result = sock->LastCount();
}
else if (currentClient != NULL)
{
currentClient->Peek(buffer, nbytes);
result = currentClient->LastCount();
}
return result;
}
// Peek buffer as string, provide socket to read or use default one
wxString SimpleTCPServer::PeekStr(wxSocketBase* sock)
{
wxString readString = "";
char buff[BUF_SIZE + 1]; // Buffer with byte for zero termination (end of string)
memset(buff, 0, sizeof(buff));
if (sock != NULL)
{
sock->Peek(&buff, BUF_SIZE);
}
else if (currentClient != NULL)
{
currentClient->Peek(&buff, BUF_SIZE);
}
readString = buff;
return readString;
}
// Peek buffer, provide socket to read or use default one
int SimpleTCPServer::Read( void *buffer, unsigned int nbytes, wxSocketBase* sock )
{
int result = 0;
// Read from specified socket or default socket if not specified
if (sock != NULL)
{
sock->Read(buffer, nbytes);
result = sock->LastCount();
}
else if (currentClient != NULL)
{
currentClient->Read(buffer, nbytes);
result = currentClient->LastCount();
}
return result;
}
// Read from socket and return a string (this should be mostly used for "human-readable" data)
wxString SimpleTCPServer::ReadStr( wxSocketBase* sock )
{
wxString readString = "";
char buff[BUF_SIZE + 1]; // Buffer with byte for zero termination (end of string)
memset(buff, 0, sizeof(buff));
// Read from specified socket or default socket if not specified
if (sock != NULL) sock->Read(&buff, BUF_SIZE);
else if (currentClient != NULL) currentClient->Read(&buff, BUF_SIZE);
readString = buff;
return readString;
}
void SimpleTCPServer::WriteStr( wxString str_in, wxSocketBase* sock )
{
if (Listener != NULL) // No point in trying if server doesn't exist
{
// Write string to specified socket
if (sock != NULL)
{
sock->Write(str_in.c_str(), str_in.Length());
}
else // Send to everyone
{
// Iterate over map of connected clients
for( std::map<wxSocketBase*, wxString>::const_iterator it = clientMap.begin();
it != clientMap.end(); ++it)
{
WriteStr(str_in, it->first);
}
LogServer("Sent \"" + str_in + "\"");
}
//else if (currentClient != NULL) currentClient->Write(str_in.c_str(), str_in.Length());
}
}
void SimpleTCPServer::WriteStr( wxString str_in, wxString ip_port )
{
bool success = false;
// Iterate over map of connected clients
for( std::map<wxSocketBase*, wxString>::const_iterator it = clientMap.begin();
it != clientMap.end(); ++it)
{
if (it->second == ip_port) // If provided address matches one of connected sockets
{
WriteStr(str_in, it->first);
success = true;
LogServer("Sent \"" + str_in + "\"");
}
if (success) return;
}
}
void SimpleTCPServer::Write( const void *data, unsigned int dataBytes, wxSocketBase* sock )
{
if (Listener != NULL) // No point in trying if server doesn't exist
{
// Write data to specified socket or default socket
if (sock != NULL) sock->Write(data, dataBytes);
else if (currentClient != NULL) currentClient->Write(data, dataBytes);
LogServer("Sent binary data");
}
}
void SimpleTCPServer::Write( const void *data, unsigned int dataBytes, wxString ip_port )
{
bool success = false;
// Iterate over map of connected clients
for( std::map<wxSocketBase*, wxString>::const_iterator it = clientMap.begin();
it != clientMap.end(); ++it)
{
if (it->second == ip_port) // If provided address matches one of connected sockets
{
Write(data, dataBytes, it->first);
success = true;
}
if (success) return;
}
}
void SimpleTCPServer::OnServerEvent(wxSocketEvent &event)
{
if(event.GetSocketEvent() == wxSOCKET_CONNECTION)
{
wxSocketBase *clientSock = Listener->Accept(false);
// Drop client if exceeded maximum count of clients
if (connectedClients >= maxClients)
{
clientSock->Destroy();
LogServer("Client rejected (limit exceeded)");
}
else
{
// Proceed normally if limit not exceeded
clientSock->SetEventHandler(*this, CLIENT_ID);
clientSock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
clientSock->Notify(true);
// Get IP address and port of connected client
wxIPV4address clientAddr;
clientSock->GetPeer(clientAddr);
// Prepare a string with address:port
wxString clientAddStr = clientAddr.IPAddress() + ":" + wxString::Format(wxT("%i"),clientAddr.Service());
// Store client data in map with pair of socket and string with address:port
clientMap.insert( std::pair<wxSocketBase*, wxString>(clientSock, clientAddStr) );
currentClient = clientSock; // Set this client as current client
connectedClients++;
LogServer("Client connected (" + clientAddStr + ")"); // Print log with address of client
wxCommandEvent myevent(EV_CLIENTCONNECTED, m_id);
wxPostEvent(m_parent, myevent);
}
}
}
void SimpleTCPServer::OnClientEvent(wxSocketEvent &event)
{
switch(event.GetSocketEvent())
{
case wxSOCKET_INPUT: OnRecv(event.GetSocket()); break;
case wxSOCKET_LOST: OnConnLost(event.GetSocket()); break;
}
}
unsigned int SimpleTCPServer::Port(unsigned int port)
{
// Return current port number if no arguments or invalid value specified
if ( (port < 1) || (port > 65535) || port == NULL )
{
return port;
}
else // Set new port number and reopen the server if needed
{
// Proceed only if port is different
if (port != srvPort)
{
srvPort = port;
LogServer("New port: " + wxString::Format(wxT("%i"),port) );
// Restart server with new port (only if it was running before)
if (Listener != NULL)
{
Close();
Open();
}
}
}
return port;
}
void SimpleTCPServer::OnRecv(wxSocketBase *sock)
{
LogServer("Incoming data");
currentClient = sock; // Set data sender as current client (so it will be easy to respond to it later)
// Put current socket in event data, so main application will be able to read it
wxCommandEvent myevent(EV_ONDATA, 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 SimpleTCPServer::OnConnLost(wxSocketBase *sock)
{
LogServer("Client disconnected");
wxCommandEvent myevent(EV_CLIENTDISCONNECTED, m_id);
myevent.SetClientData( sock );
wxPostEvent(m_parent, myevent);
connectedClients--;
clientMap.erase( sock ); // Remove client from map of connected clients
// Removed in favor of map
// clients.remove(sock); // Remove client from list of clients
if (currentClient == sock)
{
currentClient = NULL; // Clear current client if this is the one disconnecting
LogServer("Clearing current client");
}
sock->Destroy();
}
void SimpleTCPServer::DropClient(wxSocketBase *sock)
{
//LogServer("Dropping client");
connectedClients--;
wxCommandEvent myevent(EV_CLIENTDISCONNECTED, m_id);
myevent.SetClientData( sock );
wxPostEvent(m_parent, myevent);
clientMap.erase( sock ); // Remove client from map of connected clients
// Get IP address and port of connected client
wxIPV4address clientAddr;
sock->GetPeer(clientAddr);
// Prepare a string with address:port
wxString clientAddStr = clientAddr.IPAddress() + ":" + wxString::Format(wxT("%i"),clientAddr.Service());
LogServer("Dropping client " + clientAddStr);
if (currentClient == sock)
{
currentClient = NULL; // Clear current client if this is the one disconnecting
}
sock->Destroy();
}
void SimpleTCPServer::DropClient(wxString ip_port)
{
bool success = false;
// Iterate over map of connected clients
for( std::map<wxSocketBase*, wxString>::const_iterator it = clientMap.begin();
it != clientMap.end(); ++it)
{
if (it->second == ip_port) // If provided address matches one of connected sockets
{
//LogServer("Dropping client");
connectedClients--;
wxCommandEvent myevent(EV_CLIENTDISCONNECTED, m_id);
myevent.SetClientData( it->first );
wxPostEvent(m_parent, myevent);
// Get IP address and port of connected client
wxIPV4address clientAddr;
it->first->GetPeer(clientAddr);
// Prepare a string with address:port
wxString clientAddStr = clientAddr.IPAddress() + ":" + wxString::Format(wxT("%i"),clientAddr.Service());
LogServer("Dropping client " + clientAddStr);
if (currentClient == it->first)
{
currentClient = NULL; // Clear current client if this is the one disconnecting
}
clientMap.erase( it->first ); // Remove client from map of connected clients
it->first->Destroy();
}
}
}