Search code examples
socketsasynchronousd

Async sockets in D


Okay this is my first question here on Stack Overflow, so bare over with it if I'm not asking properly.

Basically I'm trying to code some asynchronous sockets using std.socket, but I'm not sure if I've understood the concept correct. I've only ever worked with asynchronous sockets in C# and in D it seem to be on a much lower level. I've researched a lot and looked up a lot of code, documentation etc. both for D and C/C++ to get an understanding, however I'm not sure if I understand the concept correctly and if any of you have some examples. I tried looking at splat, but it's very outdated and vibe seems to be too complex just for a simple asynchronous socket wrapper.

If I understood correctly there is no poll() function in std.socket so you'd have to use SocketSet with a single socket on select() to poll the status of the socket right?

So basically how I'd go about handling the sockets is polling to get the read status of the socket and if it has a success (value > 0) then I can call receive() which will return 0 for disconnection else the received value, but I'd have to keep doing this until the expected bytes are received.

Of course the socket is set to nonblocked!

Is that correct?

Here is the code I've made up so far.

void HANDLE_READ()
{
    while (true)
    {
        synchronized
        {
            auto events = cast(AsyncObject[int])ASYNC_EVENTS_READ;
            foreach (asyncObject; events)
            {
                int poll = pollRecv(asyncObject.socket.m_socket);
                switch (poll)
                {
                    case 0:
                    {
                        throw new SocketException("The socket had a time out!");
                        continue;
                    }
                    default:
                    {
                        if (poll <= -1)
                        {
                            throw new SocketException("The socket was interrupted!");
                            continue;
                        }                    

                        int recvGetSize = (asyncObject.socket.m_readBuffer.length - asyncObject.socket.readSize);
                        ubyte[] recvBuffer = new ubyte[recvGetSize];
                        int recv = asyncObject.socket.m_socket.receive(recvBuffer);

                        if (recv == 0)
                        {
                            removeAsyncObject(asyncObject.event_id, true);
                            asyncObject.socket.disconnect();
                            continue;
                        }

                        asyncObject.socket.m_readBuffer ~= recvBuffer;                      
                        asyncObject.socket.readSize += recv;

                        if (asyncObject.socket.readSize == asyncObject.socket.expectedReadSize)
                        {
                            removeAsyncObject(asyncObject.event_id, true);
                            asyncObject.event(asyncObject.socket);
                        }
                        break;
                    }
                }
            }
        }
    }
}

Solution

  • So basically how I'd go about handling the sockets is polling to get the read status of the socket

    Not quite right. Usually, the idea is to build an event loop around select, so that your application is idle as long as there are no network or timer events that need to be handled. With polling, you'd have to check for new events continuously or on a timer, which leads to wasted CPU cycles, and events getting handled a bit later than they occur.

    In the event loop, you populate the SocketSets with sockets whose events you are interested in. If you want to be notified of new received data on a socket, it goes to the "readable" set. If you have data to send, the socket should be in the "writable" set. And all sockets should be on the "error" set.

    select will then block (sleep) until an event comes in, and fill the SocketSets with the sockets which have actionable events. Your application can then respond to them appropriately: receive data for readable sockets, send queued data for writable sockets, and perform cleanup for errored sockets.

    Here's my D implementation of non-fiber event-based networking: ae.net.asockets.