Ok. As promised, here is some of the sample code I ripped from my application. This isn't expected to compile and run, this is an example of how I did it. You may have to do yours completely different. Plus, this was written for Windows, and as you'll see in the code, it uses Windows Messages to send data across between the server thread and the main application, but that all depends on how YOU plan to use it. I left some of the more interesting parts in for you to reference.
As for the security part, well, I figure you can handle that part. That's just a simple matter of encrypting the data before it goes over the wire, using some well know cipher, so I didn't think I had to include any of that. For example, you can see how I constructed the packet headers, and then there is a payload that usually consists of another structure. So, encrypt that structure, send it as data, and then decrypt it on the other end, and copy it to the proper structure.
// Some defines that you may see in the code, all of which are user defined...
#define ADVERTISE_SERVER 0x12345678 // Some unique ID for your advertisement server
#define ACTIVITY_NONE 0x00000000
#define ACTIVITY_LOGON 0x00000001
#define ACTIVITY_LOGOFF 0x00000002
#define ACTIVITY_RUNNING 0x00000004
#define ACTIVITY_IDLE 0x00000005
#define ACTIVITY_SPECIFIC 0x00000006
enum Advertisements {
ADVERTISE_SHUTDOWN,
ADVERTISE_MESSAGE,
ADVERTISE_DEBUG,
ADVERTISE_OVERLAPPED,
ADVERTISE_BROADCAST_IDENTITY,
ADVERTISE_IDENTITY,
ADVERTISE_PARAMETER_CHANGE
};
struct TAdvertiseServerPacket {
UINT uiAdvertisePacketType;
DWORD dwPacketLength;
bool bRequestReply;
UINT uiReplyType;
bool bOverlappedResult;
int iPacketId;
bool bBroadcast;
char GuidHash[35];
BYTE PacketData[1024];
};
struct TAdvertiseIdentity {
TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
char szUserName[LEN_APPL_USERNAME + 1];
char szDatabase[MAX_PATH];
char szConfiguration[MAX_PATH];
char szVersion[16];
long nUserId;
char szApplication[MAX_PATH];
char szActivity[33];
UINT uiStartupIndc;
};
struct TAdvertiseMessage {
char MessageFrom[LEN_APPL_USERNAME + 1];
char MessageText[512];
};
struct TAdvertiseItemUpdate {
NMHDR pNMHDR;
long nItemId;
long nItemTypeId;
char szItemName[LEN_ITEM_NAME + 1];
bool bState;
};
struct TAdvertiseItemUpdateEx {
NMHDR pNMHDR;
long nItemId;
bool bState;
bool bBroadcast;
DWORD dwDataSize;
void *lpBuffer;
};
struct TOverlappedAdvertisement {
int iPacketId;
BYTE Data[1020];
};
DWORD WINAPI CAdvertiseServer::Go(void* tptr)
{
CAdvertiseServer *pThis = (CAdvertiseServer*)tptr;
/* Used and reused for Overlapped results, */
DWORD BufferSize = 0;
BYTE *OverlappedBuffer = NULL;
bool bOverlapped = false;
int iOverlappedId = 0;
DWORD BufferPosition = 0;
DWORD BytesRecieved = 0;
TAdvertiseItemUpdateEx *itemex = NULL;
UINT uiPacketNumber = 0;
bool Debug = false;
#ifdef _DEBUG
Debug = true;
#endif
{
DWORD dwDebug = 0;
dwDebug = GetParameter(ADVERTISE_SERVER_DEBUG); // GetParameter is part of the main program used to store running config values.
if(dwDebug > 0)
{
Debug = true;
}
}
WSAData wsaData;
WSAStartup(MAKEWORD(1,1), &wsaData);
ServerSocket = socket(PF_INET, SOCK_DGRAM, 0);
if(ServerSocket == INVALID_SOCKET)
{
CLogging Log("Client.log");
ServerSocket = NULL;
Log.Log("Could not create server advertisement socket: %d", GetLastError());
return -1;
}
sockaddr_in sin;
ZeroMemory(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(Port);
sin.sin_addr.s_addr = INADDR_ANY;
if(bind(ServerSocket, (sockaddr *)&sin, sizeof(sin)) != 0)
{
CLogging Log("Client.log");
Log.Log("Could not bind server advertisement socket on port: %d Error: %d", Port, GetLastError());
DWORD dwPort = 0;
dwPort = GetParameter(ADVERTISE_SERVER_PORT); // Again, used to set the port number, if one could not be figured out.
if(dwPort > 0)
{
return -1;
}
Port = 36221;
sin.sin_port = htons(Port);
if(bind(ServerSocket, (sockaddr *)&sin, sizeof(sin)) != 0)
{
CLogging Log("Client.log");
Log.Log("Could not bind server advertisement socket on port: %d Error: %d Could not start AdvertiseServer after two attempts. Server failed.", Port, GetLastError());
return -1;
}
}
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
HANDLE mutex = CreateMutex(NULL, FALSE, "Client.Mutex"); // Used to keep and eye on the main program, if it shuts down, or dies, we need to die.
while (1)
{
TAdvertiseServerPacket ap;
sockaddr_in sin;
int fromlen = sizeof(sin);
fd_set fds;
FD_ZERO(&fds);
FD_SET(ServerSocket, &fds);
timeval tv;
tv.tv_sec = 15;
tv.tv_usec = 0;
int err = select(0, &fds, NULL, NULL, &tv);
if(err == SOCKET_ERROR)
{
CLogging Log("Client.log");
Log.Log("Advertise: Winsock error: %d", WSAGetLastError());
Beep(800, 100);
break;
}
if(err == 0)
{
if(WaitForSingleObject(mutex, 0) != WAIT_OBJECT_0)
{
continue; // Main app is still running
}
else
{
Beep(800, 100); // Main app has died, so exit our listen thread.
break;
}
}
int r = recvfrom(ServerSocket, (char *)&ap, sizeof(ap), 0, (sockaddr *)&sin, &fromlen);
if(r != sizeof(TAdvertiseServerPacket))
{
continue;
}
switch(ap.uiAdvertisePacketType)
{
// This is where you respond to all your various broadcasts, etc.
case ADVERTISE_BROADCAST_IDENTITY:
{
// None of this code is important, however you do it, is up to you.
CDataAccess db(CDataAccess::DA_NONE);
TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
ZeroMemory(ComputerName, sizeof(ComputerName));
DWORD len = MAX_COMPUTERNAME_LENGTH;
GetComputerName(ComputerName, &len);
if(pThis->szActivity) {
CAdvertiseServer::AdvertiseIdentity(ComputerName, CDataAccess::GetLoggedInUserName(), CDataAccess::DatabaseConfiguration(), CDataAccess::DatabaseConfiguration(), ACTIVITY_SPECIFIC, pThis->szActivity, false);
} else {
CAdvertiseServer::AdvertiseIdentity(ComputerName, CDataAccess::GetLoggedInUserName(), CDataAccess::DatabaseConfiguration(), CDataAccess::DatabaseConfiguration(), ACTIVITY_RUNNING, NULL, false);
}
}
case ADVERTISE_IDENTITY:
{
TAdvertiseIdentity ident;
memcpy((void*)&ident, (void*)ap.PacketData, ap.dwPacketLength);
Listener::iterator theIterator;
theIterator = pThis->m_Listeners.find(ap.uiAdvertisePacketType);
if(theIterator == pThis->m_Listeners.end())
{
//We got an Identity Broadcast, but we're not listening for them.
continue;
}
{
itemex = new TAdvertiseItemUpdateEx;
ZeroMemory(itemex, sizeof(TAdvertiseItemUpdateEx));
memcpy((void*)&ident, ap.PacketData, ap.dwPacketLength);
itemex->pNMHDR.code = (*theIterator).first;
itemex->pNMHDR.hwndFrom = (*theIterator).second;
itemex->pNMHDR.idFrom = ADVERTISE_SERVER;
itemex->dwDataSize = sizeof(TAdvertiseIdentity);
itemex->lpBuffer = (void*)&ident;
SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)itemex);
delete itemex;
}
}
case ADVERTISE_SHUTDOWN:
{
TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
ZeroMemory(ComputerName, sizeof(ComputerName));
DWORD len = MAX_COMPUTERNAME_LENGTH;
GetComputerName(ComputerName, &len);
CString guid;
guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);
if(stricmp(ap.GuidHash, CDataAccess::HashPassword(guid)) == 0)
{
return 1;
}
}
case ADVERTISE_MESSAGE:
{
TAdvertiseMessage msg;
memcpy((void*)&msg, (void*)ap.PacketData, ap.dwPacketLength);
CString msgtext;
msgtext.Format("Message from: %s\r\n\r\n%s", msg.MessageFrom, msg.MessageText);
::MessageBox(NULL, msgtext, "Broadcast Message", MB_ICONINFORMATION | MB_SYSTEMMODAL);
break;
}
case ADVERTISE_OVERLAPPED:
{
// I left this code in here, as it's a good example of how you can send large amounts of data over a UDP socket, should you need to do it.
BufferPosition = (1020 * ((ap.uiReplyType - 1) - 1));
if(BufferPosition > BufferSize) {
BufferPosition -= 1020;
}
TOverlappedAdvertisement item;
ZeroMemory(&item, sizeof(TOverlappedAdvertisement));
memcpy((void*)&item, (void*)ap.PacketData, ap.dwPacketLength);
if(item.iPacketId == iOverlappedId)
{
DWORD ToCopy = (sizeof(item.Data) > (BufferSize - BytesRecieved) ? BufferSize - BytesRecieved : sizeof(item.Data));
memcpy((void*)&OverlappedBuffer[BufferPosition], (void*)item.Data, ToCopy);
BytesRecieved += ToCopy;
if(BytesRecieved < BufferSize)
{
continue;
}
}
}
default:
{
// What do we do if we get an advertisement we don't know about?
Listener::iterator theIterator;
if(bOverlapped == false)
{
theIterator = pThis->m_Listeners.find(ap.uiAdvertisePacketType);
if(theIterator == pThis->m_Listeners.end())
{
continue;
}
}
// Or it could be a data packet
TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
ZeroMemory(ComputerName, sizeof(ComputerName));
DWORD len = MAX_COMPUTERNAME_LENGTH;
GetComputerName(ComputerName, &len);
CString guid;
guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);
bool FromUs = stricmp(ap.GuidHash, CDataAccess::HashPassword(guid)) == 0;
if(((FromUs && Debug) || !FromUs) || ap.bBroadcast)
{
if(ap.bOverlappedResult)
{
if(ap.uiReplyType == 1)
{
itemex = new TAdvertiseItemUpdateEx;
ZeroMemory(itemex, sizeof(TAdvertiseItemUpdateEx));
memcpy(itemex, ap.PacketData, ap.dwPacketLength);
OverlappedBuffer = (BYTE*)malloc(itemex->dwDataSize);
BufferSize = itemex->dwDataSize;
ZeroMemory(OverlappedBuffer, itemex->dwDataSize);
bOverlapped = true;
iOverlappedId = ap.iPacketId;
uiPacketNumber = ap.uiReplyType;
}
continue;
}
if(bOverlapped)
{
itemex->pNMHDR.code = (*theIterator).first;
itemex->pNMHDR.hwndFrom = (*theIterator).second;
itemex->pNMHDR.idFrom = ADVERTISE_SERVER;
itemex->dwDataSize = BufferSize;
itemex->lpBuffer = (void*)OverlappedBuffer;
SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)itemex);
delete itemex;
free(OverlappedBuffer);
BufferSize = 0;
OverlappedBuffer = NULL;
bOverlapped = false;
iOverlappedId = 0;
BufferPosition = 0;
BytesRecieved = 0;
itemex = NULL;
uiPacketNumber = 0;
break;
}
TAdvertiseItemUpdate *item = new TAdvertiseItemUpdate;
ZeroMemory(item, sizeof(TAdvertiseItemUpdate));
memcpy(item, ap.PacketData, ap.dwPacketLength);
item->pNMHDR.code = (*theIterator).first;
item->pNMHDR.hwndFrom = (*theIterator).second;
item->pNMHDR.idFrom = ADVERTISE_SERVER;
SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)item);
delete item;
}
break;
}
}
}
try {
ResetEvent(ServerMutex);
CloseHandle(pThis->ServerMutex);
closesocket(ServerSocket);
return 0;
}
catch(...) {
closesocket(ServerSocket);
return -2;
}
}
// Here's a couple of the helper functions that do the sending...
bool CAdvertiseServer::SendAdvertisement(TAdvertiseServerPacket packet)
{
TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
ZeroMemory(ComputerName, sizeof(ComputerName));
DWORD len = MAX_COMPUTERNAME_LENGTH;
GetComputerName(ComputerName, &len);
CString guid;
guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);
strcpy(packet.GuidHash, CDataAccess::HashPassword(guid));
bool bRetval = false;
SOCKET s = socket(PF_INET, SOCK_DGRAM, 0);
if(s != INVALID_SOCKET)
{
BOOL tru = TRUE;
setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&tru, sizeof(tru));
sockaddr_in sin;
ZeroMemory(&sin, sizeof(sin));
sin.sin_family = PF_INET;
sin.sin_port = htons(Port);
sin.sin_addr.s_addr = INADDR_BROADCAST;
if(sendto(s, (char *)&packet, sizeof(packet), 0, (sockaddr *)&sin, sizeof(sin)) > 0)
{
bRetval = true;
if(packet.bRequestReply)
{
// Here is where your work comes in, in setting up a reply, or making a TCP connection back to the other client.
}
}
closesocket(s);
}
return bRetval;
}
bool CAdvertiseServer::Advertise(UINT uiAdvertisement, long nItemId, bool bState, void *lpBuffer, DWORD dwDataSize, bool bBroadcast)
{
TAdvertiseServerPacket packet;
ZeroMemory(&packet, sizeof(packet));
TAdvertiseItemUpdateEx item;
ZeroMemory(&item, sizeof(item));
UINT packetnum = 1;
packet.bOverlappedResult = true;
packet.bRequestReply = false;
packet.uiAdvertisePacketType = uiAdvertisement;
packet.dwPacketLength = sizeof(item);
packet.uiReplyType = packetnum;
packet.bBroadcast = bBroadcast;
item.nItemId = nItemId;
item.bState = bState;
item.dwDataSize = dwDataSize;
memcpy((void*)packet.PacketData, (void*)&item, sizeof(item));
packet.iPacketId = GetTickCount();
if(SendAdvertisement(packet))
{
BYTE *TempBuf = new BYTE[dwDataSize];
memcpy(TempBuf, lpBuffer, dwDataSize);
DWORD pos = 0;
DWORD BytesLeft = dwDataSize;
while(BytesLeft)
{
TOverlappedAdvertisement item;
packet.uiAdvertisePacketType = ADVERTISE_OVERLAPPED;
packet.bOverlappedResult = BytesLeft > 1020;
item.iPacketId = packet.iPacketId;
memcpy((void*)item.Data, (void*)&TempBuf[pos], (BytesLeft >= 1020 ? 1020 : BytesLeft));
memcpy((void*)packet.PacketData, (void*)&item, sizeof(item));
packet.dwPacketLength = sizeof(item);
packet.uiReplyType++;
if(SendAdvertisement(packet))
{
if(BytesLeft >= 1020)
{
BytesLeft -= 1020;
pos += 1020;
}
else
{
BytesLeft = 0;
}
}
}
delete TempBuf;
}
return true;
}
void CAdvertiseServer::Shutdown()
{
TAdvertiseServerPacket packet;
packet.uiAdvertisePacketType = ADVERTISE_SHUTDOWN;
SendAdvertisement(packet);
}