Idov
Idov

Reputation: 5124

C++ select stops accepting connections


I'm trying to make a select-server in order to receive connection from several clients (all clients will connect to the same port).
The server accepts the first 2 clients, but unless one of them disconnects, it will not accept a new one. I'm starting to listen the the server port like this:

listen(m_socketId, SOMAXCONN);  

and using the select command like this:

int selected = select(m_maxSocketId + 1, &m_socketReadSet, NULL, NULL, 0);

I've added some code.

bool TcpServer::Start(char* ipAddress, int port)
{
    m_active = true;
    FD_ZERO(&m_socketMasterSet);
    bool listening = m_socket->Listen(ipAddress, port);
    // Start listening.

    m_maxSocketId = m_socket->GetId();
    FD_SET(m_maxSocketId, &m_socketMasterSet);
    if (listening == true)
    {
        StartThread(&InvokeListening);
        StartReceiving();
        return true;
    }
    else
    {
        return false;
    }
}

void TcpServer::Listen()
{
    while (m_active == true)
    {
        m_socketReadSet = m_socketMasterSet;
        int selected = select(m_maxSocketId + 1, &m_socketReadSet, NULL, NULL, 0);
        if (selected <= 0)
            continue;
        bool accepted = Accept();
        if (accepted == false)
        {
            ReceiveFromSockets();
        }
    }
}

bool TcpServer::Accept()
{
    int listenerId = m_socket->GetId();
    if (FD_ISSET(listenerId, &m_socketReadSet) == true)
    {
        struct sockaddr_in remoteAddr;
        int addrSize = sizeof(remoteAddr);
        unsigned int newSockId = accept(listenerId, (struct sockaddr *)&remoteAddr, &addrSize);
        if (newSockId == -1) // Invalid socket...
        {
            return false;
        }
        if (newSockId > m_maxSocketId)
        {
            m_maxSocketId = newSockId;
        }

        m_clientUniqueId++;
        // Remembering the new socket, so we'll be able to check its state
        // the next time.
        FD_SET(newSockId, &m_socketMasterSet);

        CommEndPoint remote(remoteAddr);
        CommEndPoint local = m_socket->GetLocalPoint();

        ClientId* client = new ClientId(m_clientUniqueId, newSockId, local, remote);
        m_clients.Add(client);
        StoreNewlyAcceptedClient(client);
        char acceptedMsg = CommInternalServerMsg::ConnectionAccepted;
        Server::Send(CommMessageType::Internal, client, &acceptedMsg, sizeof(acceptedMsg));
        return true;
    }

    return false;
}

I hope it's enough :) what's wrong with it?

Upvotes: 1

Views: 475

Answers (2)

Nikolai Fetissov
Nikolai Fetissov

Reputation: 84151

The by far most common error with select() is not re-initializing the fd sets on every iteration. The second, third, and forth arguments are updated by the call, so you have to populate them again every time.

Post more code, so people can actually help you.

Edit 0:

fd_set on Windows is a mess :)

Upvotes: 3

Ben Voigt
Ben Voigt

Reputation: 283614

It's not allowed to copy construct fd_set objects:

 m_socketReadSet = m_socketMasterSet;

This combined with Nikolai's correct statement that select changes the set passed in probably accounts for your error.

poll (On Windows, WSAPoll) is a much friendlier API.

Windows also provides WSAEventSelect and (Msg)WaitForMultipleObjects(Ex), which doesn't have a direct equivalent on Unix, but allows you to wait on sockets, files, thread synchronization events, timers, and UI messages at the same time.

Upvotes: 0

Related Questions