0
votes

i've started doing a project with networking. But after setting up a the std::thread, the while(true)-loop wont start.

The first thing i have done is setting up the std::thread which is listening for new clients. In the function which the thread is using is a while(true)-loop, which perfectly works. In in the main thread after initializing the serverListener std::thread, is again a while(true)-loop which tries to recieve data, but this loop wont start, except when I put a std::cout in the while-loop before the for-loop, but this spams my console.

Variables and inclusions:

#include <SFML/Network.hpp>
#include <iostream>
#include <list>
#include <thread>

#define PORT 1337

unsigned int clientCounter = 0;

sf::TcpSocket clients[5];
sf::Packet packet;

The function which the std::thread is using:

void serverListener() {

    sf::TcpListener listener;
    listener.listen(PORT);

    while (true) {
        if (listener.accept(clients[clientCounter]) == sf::Socket::Status::Done) {
            std::cout << "New client connected: " 
                      << clients[clientCounter].getRemoteAddress() << std::endl;
            clientCounter++;
        }
    }
}

Main thread:

int main()
{
    std::thread serverListenerThread(&serverListener);

    while(true) {
        //std::cout << "Some message"; <----- when uncomment, the loop works?

        for (int i = 0; i < clientCounter; i++) {

            std::string message = "";
            packet >> message;

            if (clients[i].receive(packet) == sf::Socket::Status::Done) {

                message += std::to_string(i);
                std::cout << message << std::endl;
            }
        }

    }

    return 0;
}
1
I believe your logic is incorrect on the accept test. accept() blocks until a client connects. sf::Socket::Status::Done would be an indication that your listener or client connect had closed already. you should change that == to a != if (listener.accept(clients[clientCounter]) != sf::Socket::Status::Done) { as shown in the tcp example here - sfml-dev.org/tutorials/2.5/network-socket.php - estabroo
Have you tried attaching a debugger? Where does the second thread get stuck? Do the listen or accept calls return the expected status codes> - Botje
FWIW, there is no guarantee that updates to the value of clientCounter in one thread will be seen in another. Change its type to std::atomic<unsigned int>. - Pete Becker
Have you tried "nc localhost 1337" ? - ppetraki
Off-topic: If you want to prevent exceptional programme exit caused by your thread object running out of scope, you should either join or detach the thread; joining, though requires additional synchronisation to make your thread function leave its while loop. - Aconcagua

1 Answers

2
votes

Think for a moment about what the first thread sees. Since you have no syncronisation between the two threads, the compiler assumes they don't communicate and so the value of clientCounter will never change while the inner for loop is being executed. Thus the loop will always see clientCounter=0 and so the loop doesn't run.

unsigned int clientCounter = 0;

int main()
{
    std::thread serverListenerThread(&serverListener);

    while(true) {
        for (int i = 0; i < clientCounter; i++) {
            // Never executed, since clientCounter = 0
        }
    }
}

You should make clientCounter an std::atomic<unsigned int>. This lets the compiler know to synchronise the variable between threads. In general, anything that you share between threads should either be atomic or should be protected by locking a mutex.

Adding a sleep may help but only by pure luck; there is absolutely no reason why it should continue to work. In particular, there is no requirement to load the value of clientCounter from memory between while loop iterations, since the compiler 'knows' it is never written to in the main thread. Thus, reloading it would be redundant.