SFML Обнаружение нескольких соединений с сервером и их подсчет - PullRequest
0 голосов
/ 23 ноября 2018

Я создаю очень простое клиент-серверное приложение.Я хочу, чтобы мой сервер обнаружил, сколько клиентов подключилось, и, если их больше 2, просто распечатайте сообщение.

Код сервера, который у меня есть, очень минимален:

std::vector<sf::TcpSocket*> clients;

sf::TcpListener listener;

if (listener.listen(SERVERPORT) != sf::Socket::Done)
{
    printf("Error\n");
}

printf("Waiting for first connection...\n");

sf::TcpSocket *socket = new sf::TcpSocket;

if (listener.accept(*socket) != sf::Socket::Done)
{
    printf("Error\n");
}

clients.push_back(socket);

while (clients.size() < 2)
{
    printf("Waiting for second connection...\n");
}

Проблема, с которой я столкнулся, заключается в том, что он обнаруживает первое соединение без проблем, но не обнаруживает второе, даже если мой второй клиент подключен.Для клиентского соединения я просто использую очень простой код, описанный в документации SFML.Я очень запутался, так как client.size () всегда возвращает 1.

1 Ответ

0 голосов
/ 23 ноября 2018

Поскольку вы не принимаете второе соединение ...

Немного измените порядок кодов:

std::vector<sf::TcpSocket*> clients;

sf::TcpListener listener;

if (listener.listen(SERVERPORT) != sf::Socket::Done)
{
    printf("Error\n");
}

printf("Waiting for first connection...\n");

while (clients.size() < 2)
{
    sf::TcpSocket *socket = new sf::TcpSocket;
    if (listener.accept(*socket) != sf::Socket::Done)
    {
        printf("Error\n");
    }

    clients.push_back(socket);

    // OK, that one would be printed twice!!!
    printf("Waiting for second connection...\n");
}

Это не будет последний цикл, хотя это простопредназначен для демонстрации.

Имейте в виду, что вы не удаляете созданные клиенты (-> утечка памяти!).Вы можете подумать об использовании интеллектуальных указателей вместо необработанных указателей.

ОК, так что теперь: вы хотите принять произвольное количество клиентов, и, как только у вас будет больше двух клиентов, запустите какое-то приложение.

Вначале вам нужно продолжить принимать новых клиентов в цикле (я немного изменил протоколирование сообщений ...:

std::vector<sf::TcpSocket*> clients;
sf::TcpListener listener;
if (listener.listen(SERVERPORT) != sf::Socket::Done)
{
    printf("Error\n");
    return; // no need to go on on error!
}

printf("start waiting for clients...\n");

for(;;) // endless loop...
{
    sf::TcpSocket* socket = new sf::TcpSocket;
    if (listener.accept(*socket) != sf::Socket::Done)
    {
        printf("Error\n");
        // TODO: consider some appropriate error handling
        // minimally:
        delete socket; // otherwise, you produce memory leaks!
    }

    clients.push_back(socket);
    printf
    (
        "new client accepted; number of clients now: %llu\n"
        "continue waiting...\n",
        clients.size()
    );
    // llu: most likely on 64-bit machine, on 32 bit either lu or u,
    // depending on type of size_t; you get around having to decide
    // if using C++ streams instead...
}

Проблема теперь заключается в следующем: вам дополнительно нужно обрабатывать соединенияПосмотрите на этот SFML-учебник , особенно на раздел Блокировка группы сокетов . Описанный здесь селектор - именно то, что вам нужно. Самое важное:

Селектор может контролировать все типы сокетов: sf :: TcpSocket, sf :: UdpSocket и sf :: TcpListener.

Таким образом, вы можете добавить все свои сокеты и слушатель. Вам все равно придется обслуживать свои экземпляры самостоятельно, хотя:

Селектор не является контейнером сокетов. Он только ссылается (указывает на) на сокеты, которые вы добавляете, он не хранит их.Нет способа извлечь или сосчитать сокеты, которые вы вставили внутрь.Вместо этого [...].

Ну, вы уже закрыли часть «вместо» своим вектором.Обычно я бы рекомендовал хранить объекты в векторе напрямую;однако вы не можете, так как вам нужны ссылки в селекторе.Таким образом, вам нужно для хранения указателей в векторе - или вы можете использовать вместо него std::list, который не сделает недействительными ссылки или указатели на сохраненные данные при вставке или удалении объектов (кроме указателей на удаленный объект, конечно).

Если вы хотите остаться с вектором, вы можете рассмотреть возможность использования умных указателей, так что вам не нужно заботиться об управлении памятью (например, std::vector<std::unique_ptr<sf::TcpSocket>> clients).

Я лично предпочел бы std::list:

std::list<sf::TcpSocket> clients; // notice: no pointers...
sf::TcpListener listener;
sf::SocketSelector selector;
selector.add(listener); // you want to listen as well...
for(;;)
{
    // wait on selector as described in tutorial, I'd recommend only
    // waiting for one second: you might want to handle the application
    // exiting at some point of time!
}

Если ожидание прошло успешно, вы сначала проверите сокет сервера, а затем клиентов):

if(listener.isReady())
{
    // OK, we now know that a client is waiting, so next steps won't block:
    clients.emplace_back(); // new client
    if(listener.accept(clients.back()) != sf::Socket::Done)
    {
        // OK, something went wrong, current client us valueless...
        clients.pop_back();
        // adding and then removing? well, normally, this case here
        // should not occur anyway, so we optimized for the far more
        // common case.
    }
    else
    {
        // want to listen on as well
        selector.add(clients.back());
    }
}
for(auto& c : clients)
{
    if(c.isReady())
    {
        // handle communication
    }
}

Вам решать коммуникацию ... Я бы просто не общался, если подключены хотя бы два клиента.Если вы оставите сообщения от одного клиента необработанными, вам может понадобиться обработать довольно много устаревших сообщений, как только появится другой клиент, поэтому я бы предпочел обрабатывать все поступающие сообщения немедленно, даже с одним единственным клиентом.Если это кажется вам более подходящим, вы все равно можете поместить if(clients.size() >= 2) вокруг (на основе диапазона) для цикла.

Если клиент отключается, не забудьте удалить его из селектора!Сделав это, вы также можете безопасно удалить его из списка клиентов.

Наконец: бесконечный цикл!

Возможно, вы захотите заменить это через некоторый условный цикл, проверяя, должен ли сервервсе еще бегать или нет.Тогда вам еще понадобится какой-то механизм, чтобы установить это условие в falseСуществуют различные варианты, например, отдельный поток, проверяющий определенный вход, сигналы (особенно в системах POSIX), ...

...