Skip to content

Instantly share code, notes, and snippets.

@Romain-P
Last active June 7, 2020 12:55
Show Gist options
  • Save Romain-P/b9dd9e9e880b49294504ab18d0398576 to your computer and use it in GitHub Desktop.
Save Romain-P/b9dd9e9e880b49294504ab18d0398576 to your computer and use it in GitHub Desktop.
non-blocking sockets
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <vector>
#include <string>
#include <iostream>
namespace AsyncSocket {
inline std::vector<WSAPOLLFD> watchList;
enum PollCallMethod { BON_BLOCKING = 0, BLOCKING = -1 };
/**
* Non-blocking function
* Detects whenever sockets passed as arguments are ready to read some incoming data.
*
* If read events are detected, the callback handler is fired with the concerned socket
*
* @return [[
* 0: no event detected
* -1: error
* >1: events detected, next read won't be blocking
* ]]
*/
inline int socketPollEvents(void (*readEventHandler)(unsigned int socket)) {
int result = WSAPoll(&watchList[0], watchList.size(), PollCallMethod::BON_BLOCKING);
if (result > 0)
for (auto &it: watchList)
if ((it.revents & POLLRDNORM) || (it.revents & POLLRDBAND)) /* on read event */
readEventHandler(it.fd);
return result;
}
/**
* Initializes a socket with a given ip and port
* @returns [[
* -1: error
* +1: socket descriptor (unsigned int)
* ]]
*/
inline unsigned int socketConnect(char const *ip, int port) {
SOCKET descriptor;
SOCKADDR_IN hints = {0};
hints.sin_addr.s_addr = inet_addr(ip);
hints.sin_family = AF_INET;
hints.sin_port = htons(port);
descriptor = descriptor(AF_INET, SOCK_STREAM, 0);
bind(descriptor, (SOCKADDR *) &hints, sizeof(hints));
if (connect(descriptor, (SOCKADDR *) &hints, sizeof(hints)) == SOCKET_ERROR)
return -1;
/* add the socket to watch list for read operations */
watchList.push_back((WSAPOLLFD) {
.fd = descriptor,
.events = POLLIN /* specify we only want to watch read operations */
});
return descriptor;
}
/**
* @param descriptor a socket ready to read on
* @param handler lua handler in order to process the data
*
* Handler params
* @param data received data
* @param bytes -1 on error, 0 if socket closed, > 0 for the number of bytes in the buffer
*/
inline void socketRead(unsigned int descriptor, void (*handler)(char const data[], unsigned int bytes)) {
char buffer[1024];
unsigned int bytes = recv((SOCKET) descriptor, buffer, sizeof(buffer), 0);
buffer[bytes] = 0;
handler(buffer, bytes);
}
/**
*
* @param descriptor Socket to write on
* @param data Data to send
* @param length Length of the buffer
*/
inline void socketWrite(unsigned int descriptor, char const *data, int length) {
send((SOCKET) descriptor, data, length, 0);
}
/**
* @param descriptor socket to close
*/
inline void socketClose(unsigned int descriptor) {
/* remove socket from watchlist */
watchList.erase(std::remove_if(watchList.begin(), watchList.end(), [descriptor](WSAPOLLFD &pollFd) {
return pollFd.fd == descriptor;
}), watchList.end());
closesocket((SOCKET) descriptor);
}
}
local socketHandlers = {} //table mapping [socket] to [readHandler]
local socketFrame = CreateFrame("socketFrame", nil, UIParent)
socketFrame:SetScript("OnEvent",
function()
SocketPollEvents(
function(socket)
SocketRead(socket, socketHandlers[socket])
end
)
end
)
function CreateSocket(ip, port, readHandler)
local socket = SocketConnect(ip, port)
socketHandlers[socket] = readHandler
return socket
end
---------------------------------------------------------------------
-- Usage example
local socket = CreateSocket("127.0.0.1", 123,
function(data)
print("data received: " .. data)
end
)
-- later
CloseSocket(socket)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment