Поскольку вы не принимаете второе соединение ...
Немного измените порядок кодов:
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), ...