Suggested potential modification to juce_Socket.cpp


#1

I'm working on a project where I was originally using winsock but wanted to do everything in Juce possible so I transitioned it to StreamingSockets instead; however I've run into an issue.  Previously when I was using getaddrinfo the ai_next pointer would be populated and that is where I needed to make my connection but Juce does not iterate through those pointers.  I've made a modification to juce_Socket.cpp to handle this case but I'm no Juce or Socket master so I just wanted to post the code here for review.  The base Juce code does not successfully make a connection but the modified code below does.


static bool connectSocket (int volatile& handle,
                           CriticalSection& readLock,
                           const String& hostName,
                           const int portNumber,
                           const int timeOutMillisecs) noexcept
{
    struct addrinfo* info = getAddressInfo (false, hostName, portNumber);
    if (info == nullptr)
        return false;
    bool retval = false;
    struct addrinfo* iterator;
    for (iterator = info; iterator != NULL ; iterator = iterator->ai_next)
    {
        handle = (int) socket (iterator->ai_family, iterator->ai_socktype, 0);
        if (handle < 0)
        {
            freeaddrinfo (info);
            return false;
        }
        setSocketBlockingState (handle, false);
        const int result = ::connect (handle, iterator->ai_addr, (socklen_t) iterator->ai_addrlen);
        retval = (result >= 0);
        if (result < 0)
        {
#if JUCE_WINDOWS
            if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
#else
            if (errno == EINPROGRESS)
#endif
            {
                if (waitForReadiness (handle, readLock, false, timeOutMillisecs) == 1)
                    retval = true;
            }
            continue;
        }
        break;
    }
    freeaddrinfo (info);
    setSocketBlockingState (handle, true);
    if (retval)
        resetSocketOptions (handle, false, false);
    return retval;
}

Just want to make sure this isn't going to cause some issue down the line I don't forsee or if there is a better way to achieve my desired effect.


#2

OK, thanks - didn't realise you could iterate those objects. Will take a look at add something asap!


#3

Actually, I spotted some other problems with that code and have had a go at cleaning it up.. As I don't have a test for the use-case that you hit, could you see if this does the trick?

    static bool connectSocket (int volatile& handle,
                               CriticalSection& readLock,
                               const String& hostName,
                               const int portNumber,
                               const int timeOutMillisecs) noexcept
    {
        bool success = false;
        if (struct addrinfo* info = getAddressInfo (false, hostName, portNumber))
        {
            for (struct addrinfo* i = info; i != nullptr; i = i->ai_next)
            {
                const SocketHandle newHandle = socket (i->ai_family, i->ai_socktype, 0);
                if (newHandle >= 0)
                {
                    setSocketBlockingState (newHandle, false);
                    const int result = ::connect (newHandle, i->ai_addr, (socklen_t) i->ai_addrlen);
                    success = (result >= 0);
                    if (! success)
                    {
                       #if JUCE_WINDOWS
                        if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
                       #else
                        if (errno == EINPROGRESS)
                       #endif
                        {
                            if (waitForReadiness (newHandle, readLock, false, timeOutMillisecs) == 1)
                                success = true;
                        }
                    }
                    if (success)
                    {
                        handle = (int) newHandle;
                        break;
                    }
                   #if JUCE_WINDOWS
                    closesocket (newHandle);
                   #else
                    ::close (newHandle);
                   #endif
                }
            }
            freeaddrinfo (info);
            
            if (success)
            {
                setSocketBlockingState (handle, true);
                resetSocketOptions (handle, false, false);
            }
        }
        return success;
    }

#4

You weren't kidding about asap.

Yes, the new code does the trick.  Socket works perfectly now.

Thanks Jules.