Another possible model
Another interface approach would be to have a very minimalist callback interface where the socket ID is handed back in the callback.
This should be enough to make the different operating systems look the same but avoid the problem with not separating the concern of dispatch callbacks to the lifetime of the sockets.
The interface could look something like this:
typedef void (*NETsocketCallback)(int SocketId);
void NETsetSocketWriteCallback(NETloopHandle LoopHane, NETsocketCallback pCallback);
void NETsetSocketReadCallback(NETloopHandle LoopHane, NETsocketCallback pCallback);
void NETsetSocketErrorCallback(NETloopHandle LoopHane, NETsocketCallback pCallback);
// NETpoll stays like this:
void NETpoll(NETloopHandle* pLoopHandle, int TimeoutInMilliseconds);
// And the read/write API stays like this:
int NETsocketRead(int SocketId, COLstring* pData);
int NETsocketWrite(int SocketId, const char* pData, int Size);
int NETsocketClose(int SocketId);
Advantage of this model is possibly being more compatible with how IOCP works? Questions remain about how errors and socket closing would work in this model.
One might consider using COLclosures or similar callback mechanisms?
The general idea is that if a SocketId is no longer valid because the socket is closed that NETsocketRead/Write/Close would behave gracefully.