2017-12-29 11:42:03 +00:00
|
|
|
|
#include <winsock2.h>
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#include "UDPSend.h"
|
|
|
|
|
#include <WS2tcpip.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
2018-01-22 21:01:35 +00:00
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
extern "C" {
|
|
|
|
|
WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, PCSTR pszAddrString, PVOID pAddrBuf);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-12-29 11:42:03 +00:00
|
|
|
|
namespace SMUDP
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
UDPSend::UDPSend()
|
|
|
|
|
{
|
2018-01-02 18:54:34 +00:00
|
|
|
|
m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // create the socket
|
|
|
|
|
m_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
|
m_exitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
|
m_dataReady = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
|
m_sendComplete = CreateEvent(NULL, FALSE, TRUE, NULL); // start off ready
|
|
|
|
|
|
|
|
|
|
m_sendThread = std::thread(&UDPSend::SendThread, this);
|
2017-12-29 11:42:03 +00:00
|
|
|
|
|
|
|
|
|
WSAEventSelect(m_socket, m_event, FD_READ | FD_WRITE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UDPSend::~UDPSend()
|
|
|
|
|
{
|
2018-01-02 18:54:34 +00:00
|
|
|
|
SetEvent(m_exitEvent); // trigger thread to exit
|
|
|
|
|
m_sendThread.join(); // block until thread has exited
|
|
|
|
|
CloseHandle(m_exitEvent); // clean up the rest of the resources
|
|
|
|
|
CloseHandle(m_dataReady);
|
|
|
|
|
CloseHandle(m_sendComplete);
|
|
|
|
|
|
2017-12-29 11:42:03 +00:00
|
|
|
|
closesocket(m_socket);
|
|
|
|
|
CloseHandle(m_event);
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-02 18:54:34 +00:00
|
|
|
|
bool UDPSend::SendAsync(const char* address, int port, int length, const void *data, int timeout)
|
|
|
|
|
{
|
|
|
|
|
WaitForSingleObject(m_sendComplete, INFINITE); // block until previous sends have completed, don't want overlapping
|
|
|
|
|
|
|
|
|
|
m_data.clear();
|
|
|
|
|
m_data.insert(m_data.end(), (UINT8*)data, (UINT8*)data + length);
|
|
|
|
|
|
|
|
|
|
m_address = address;
|
|
|
|
|
m_port = port;
|
|
|
|
|
m_timeout = timeout;
|
|
|
|
|
|
|
|
|
|
SetEvent(m_dataReady);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDPSend::SendThread()
|
|
|
|
|
{
|
|
|
|
|
HANDLE events[2];
|
|
|
|
|
|
|
|
|
|
events[0] = m_exitEvent;
|
|
|
|
|
events[1] = m_dataReady;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
|
|
auto result = WaitForMultipleObjects(_countof(events), events, FALSE, INFINITE);
|
|
|
|
|
|
|
|
|
|
if (result == WAIT_OBJECT_0) { // exit event triggered
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (result == (WAIT_OBJECT_0 + 1)) { // data ready
|
|
|
|
|
Send(m_address.c_str(), m_port, (int)m_data.size(), m_data.data(), m_timeout);
|
|
|
|
|
SetEvent(m_sendComplete);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-29 11:42:03 +00:00
|
|
|
|
bool UDPSend::Send(const char* ip, int port, int length, const void *data, int timeout)
|
|
|
|
|
{
|
|
|
|
|
UINT8* pData = (UINT8*)data;
|
|
|
|
|
|
|
|
|
|
Packet packet;
|
|
|
|
|
packet.CalcTotalIDs(length);
|
|
|
|
|
|
|
|
|
|
SOCKADDR_IN address = {};
|
|
|
|
|
address.sin_family = AF_INET; // address family Internet
|
|
|
|
|
address.sin_port = htons(port); // set server<65>s port number
|
|
|
|
|
inet_pton(AF_INET, ip, &address.sin_addr);
|
|
|
|
|
|
|
|
|
|
while (length > 0) {
|
|
|
|
|
|
|
|
|
|
packet.flags = 0; // reset the flags (not used currently)
|
|
|
|
|
|
2018-01-03 16:19:10 +00:00
|
|
|
|
if (length > packet.BUFFER_SIZE_UDP) {
|
|
|
|
|
packet.length = packet.BUFFER_SIZE_UDP;
|
2017-12-29 11:42:03 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
packet.length = length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(packet.data, pData, packet.length);
|
|
|
|
|
|
|
|
|
|
int sent = SendDataPacket(packet, m_socket, address, m_event);
|
|
|
|
|
|
|
|
|
|
if (sent == SOCKET_ERROR) {
|
|
|
|
|
return false; // send failure
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ProcessReply(m_socket, m_event, timeout)) {
|
|
|
|
|
return false; // reply failure
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
length -= packet.length;
|
|
|
|
|
pData += packet.length;
|
|
|
|
|
|
|
|
|
|
packet.currentID++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UDPSend::WaitForEvent(SOCKET s, HANDLE hEvent, long nEvents, int timeout)
|
|
|
|
|
{
|
|
|
|
|
//========
|
|
|
|
|
DWORD res;
|
|
|
|
|
//========
|
|
|
|
|
|
|
|
|
|
res = WaitForSingleObject(hEvent, timeout);
|
|
|
|
|
|
|
|
|
|
if (res == WAIT_OBJECT_0) {
|
|
|
|
|
|
|
|
|
|
WSANETWORKEVENTS events = {};
|
|
|
|
|
WSAEnumNetworkEvents(s, hEvent, &events);
|
|
|
|
|
|
|
|
|
|
if (events.lNetworkEvents & nEvents) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int UDPSend::SendDataPacket(Packet &p, SOCKET s, SOCKADDR_IN& address, HANDLE events)
|
|
|
|
|
{
|
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
|
|
int sent = sendto(s, p, p.Size(), 0, (struct sockaddr *)&address, sizeof(SOCKADDR_IN));
|
|
|
|
|
|
|
|
|
|
if (sent == SOCKET_ERROR) {
|
|
|
|
|
|
|
|
|
|
int error = WSAGetLastError(); // clear error code
|
|
|
|
|
|
|
|
|
|
if (error == WSAEWOULDBLOCK) {
|
|
|
|
|
WaitForEvent(s, events, FD_WRITE, INFINITE); // wait until write event is triggered
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sent; // send failure
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p.Size();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UDPSend::ProcessReply(SOCKET s, HANDLE event, int timeout)
|
|
|
|
|
{
|
|
|
|
|
//=================
|
|
|
|
|
int result;
|
|
|
|
|
PacketReply rp;
|
|
|
|
|
//=================
|
|
|
|
|
|
|
|
|
|
if (WaitForEvent(s, event, FD_READ, timeout)) {
|
|
|
|
|
|
|
|
|
|
result = recv(s, &rp, sizeof(rp), 0);
|
|
|
|
|
|
|
|
|
|
if (result == SOCKET_ERROR) {
|
|
|
|
|
auto error = WSAGetLastError(); // clear error code
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|