SFML 多个客户端未连接到服务器

问题描述 投票:0回答:1

我有一个可以处理多个客户端的 SFML 服务器的以下代码。服务器功能在不同的线程中运行,以便它可以并行获取用户输入并进行相应的操作。服务器可以打印客户端列表或显示最后收到的消息。客户端还接收用户输入并将数据发送到服务器。请暂时忽略用户输入,因为我相信这工作正常。下面的功能有什么问题吗?当我尝试打印客户端时,服务器不显示任何输出。即使我将套接字和侦听器设置为非阻塞,它也不会连接。可能是什么错误?

服务器-

    struct tcpMessage
    {
        unsigned char nVersion;
        unsigned char nType;
        unsigned short nMsgLen;
        char chMsg[1000];
    };
    
    void runTcpServer(unsigned short port, std::atomic<bool>& running, std::atomic<int>& command)
    {
        // Create a server socket to accept new connections
        sf::TcpListener listener;
    
        // Listen to the given port for incoming connections
        if (listener.listen(port) != sf::Socket::Done)
            return;
    
        sf::SocketSelector selector;
        std::list<sf::TcpSocket*> clients;
        selector.add(listener);
        tcpMessage msg;
        std::string userInput;
    
        while (running)
        {
            // Make the selector wait for data on any socket
            if (selector.wait())
            {
                // Test the listener
                if (selector.isReady(listener))
                {
                    // The listener is ready: there is a pending connection
                    sf::TcpSocket* client = new sf::TcpSocket;
                    if (listener.accept(*client) == sf::Socket::Done)
                    {
                        // Add the new client to the clients list
                        clients.push_back(client);
    
                        // Add the new client to the selector so that we will
                        // be notified when he sends something
                        selector.add(*client);
                    }
                    else
                    {
                        // Error, we won't get a new connection, delete the socket
                        delete client;
                    }
                }
                else
                {
                    // The listener socket is not ready, test all other sockets (the clients)
                    for (std::list<sf::TcpSocket*>::iterator it = clients.begin(); it != clients.end(); ++it)
                    {
                        sf::TcpSocket& client = **it;
                        if (selector.isReady(client))
                        {
                            // The client has sent some data, we can receive it
                            sf::Packet packet;
                            if (client.receive(packet) == sf::Socket::Done)
                            {
                                packet >> msg;
    
                                if (msg.nVersion == char(102))
                                {
                                    if (msg.nType == char(77))
                                    {
                                        for (std::list<sf::TcpSocket*>::iterator it1 = clients.begin(); it1 != clients.end(); ++it1)
                                        {
                                            if (it1 == it)
                                                continue;
                                            else
                                            {
                                                sf::TcpSocket& c = **it1;
                                                c.send(packet);
                                            }
                                        }
                                    }
                                    else if (msg.nType == char(20))
                                    {
                                        strcpy(msg.chMsg, strrev(msg.chMsg));
    
                                        sf::Packet packetReversed;
                                        packetReversed << msg;
                                        client.send(packetReversed);
                                    }
                                }
    
                            }
                        }
                    }
                }
            }
    
            if (command == 1)
            {
                long unsigned int number = clients.size();
                std::cout << "Number of Clients: " << number << std::endl;
    
                for (std::list<sf::TcpSocket*>::iterator it = clients.begin(); it != clients.end(); ++it)
                {
                    sf::TcpSocket& client = **it;
    
                    std::cout << "IP Address: " << client.getRemoteAddress()
                        << " | Port: " << client.getRemotePort() << std::endl;
                }
                command = 0;
            }
    
            else if (command == 2)
            {
                std::cout << msg.nVersion << " " << msg.nType << " " << msg.nMsgLen << " " << msg.chMsg;
                command = 0;
            }
        }
    }
    
    
    int main()
    {
        unsigned short port = 50000;
        std::atomic<bool> running{ true };
        std::atomic<int> command{ 0 };
    
        std::thread serverThread(runTcpServer, port, std::ref(running), std::ref(command));
    
        std::string userInput;
        do
        {
            std::cout << "Please enter command: ";
            std::cin >> userInput;
    
            if (userInput == "clients")
                command = 1;
            else if (userInput == "msg")
                command = 2;
    
        } while (userInput != "exit");
    
        running = false;
        serverThread.join();
    }

客户-

void runTcpClient(unsigned short port, tcpMessage& msgOut, std::atomic<int>& inputComplete,
    std::atomic<bool>& running)
{
    // Ask for the server address
    sf::IpAddress server;

    // Create a socket for communicating with the server
    sf::TcpSocket socket;
    socket.setBlocking(false);

    const char out[] = "lmao";
    socket.send(out, sizeof(out));

    if (socket.connect(server, port) != sf::Socket::Done)
    {
        std::cout << "Could not connect to server " << server << std::endl;
    }

    while (running)
    {
        if (inputComplete == 2)
        {
            sf::Packet packetOut;
            packetOut << msgOut;
            socket.send(packetOut);
            std::cout << "Message sent to the server: \"" << msgOut.chMsg << "\"" << std::endl;
            inputComplete = 0;
        }

        // Receive a message from the server
        sf::Packet packetIn;
        if (socket.receive(packetIn) == sf::Socket::Done)
        {
            tcpMessage msgIn;
            packetIn >> msgIn;
            // std::cout << "Message received from the server: \"" << msgIn.chMsg << "\"" << std::endl;
            std::cout << "Received Msg Type: " << msgIn.nType << "; Msg: " << msgIn.chMsg << std::endl;
        }
    }

    if (running == false)
        socket.disconnect();

int main(int argc, char* argv[])
{
    std::string ip = argv[1];
    unsigned short port = static_cast<unsigned short>(std::stoi(argv[2]));
    
    std::atomic<bool> running{ true };
    std::atomic<int> inputComplete{ 0 };

    std::string input, strCommand;
    tcpMessage msgOut;
    long lVersion, lType;
    char lMsg[1000];

    /*
    msgOut.nVersion = char(102);
    msgOut.nType = char(20);
    msgOut.nMsgLen = 67;
    strcpy(msgOut.chMsg, "LMAO");
    */

    std::thread clientThread(runTcpClient, port, std::ref(msgOut), std::ref(inputComplete), std::ref(running));

    do
    {
        std::cout << "Please enter command: ";
        std::getline(std::cin, input);
        std::istringstream iss(input);

        if (iss >> strCommand)
        {
            if (strCommand == "v")
            {
                iss >> lVersion;
                msgOut.nVersion = static_cast<unsigned char>(lVersion);
                inputComplete++;
            }

            if (strCommand == "t")
            {
                iss >> lType;
                msgOut.nType = static_cast<unsigned char>(lType);
                iss >> lMsg;
                strcpy(msgOut.chMsg, lMsg);
                msgOut.nMsgLen = static_cast<unsigned short>(strlen(lMsg));
                inputComplete++;
            }

            if (strCommand == "q")
            {
                running = false;
            }
        }

    } while (input != "q");

    clientThread.join();
c++ sockets networking sfml
1个回答
0
投票

服务器不显示任何输出,因为它正忙于在

if (selector.wait())
内等待,当使用默认超时参数调用时,它会无限期地等待(docs)(顺便说一句,在这种情况下它总是返回
true
)。此外,您以不同步的方式从不同线程访问
std::cout
,特别是下一个
"Please enter command: "
消息可以(并且很可能)在上一个命令的输出完成之前输出(如果您的
serverThread
必须处理命令) .

如果你想正确处理命令,你有2个选择:

  1. (Harder)继续在
    serverThread
    中处理命令并输出,但是你应该为等待函数提供一个合理的超时参数(如
    selector.wait(sf::seconds(0.1f))
    ),这样它就不会阻塞你的
    serverThread
    无限期地等待选择器并给它一个处理
    command
    的机会。此外,为了避免同步问题,例如在前一个命令完成之前在主线程中提示输入新命令,您必须在主线程中等待来自
    serverThread
    的信号,该信号表示
    command
    处理结束。您可以使用
    std::mutex
    std::condition_variable
    来完成此操作,如下所示:
    void runTcpServer(unsigned short port, std::atomic<bool>& running, std::atomic<int>& command,
        std::condition_variable& cv, std::mutex& command_mutex)
    {
        // ...
    
        while (running)
        {
            if (selector.wait(sf::seconds(0.1f))) // Wait at most 100ms
            {
                // ...
            }
    
            if (command == 0) // No command
                continue;
            // Command exists, so process it
            {
                std::unique_lock lock{command_mutex};
                if (command == 1)
                {
                    // ...
                }
                else if (command == 2)
                {
                    // ...
                }
                else
                    assert(false); // Unidentified command, shouldn't happen according to the main thread logic
                command = 0; // We don't need separate boolean varialbe to signify the command is finished, just set command to 0
            }
            cv.notify_one(); // Notify the main thread that the command is processed
        }
    }
    
    int main()
    {
        unsigned short port = 50000;
        std::atomic<bool> running{ true };
        std::atomic<int> command{ 0 };
        std::condition_variable cv;
        std::mutex command_mutex;
    
        std::thread serverThread(runTcpServer, port, std::ref(running), std::ref(command),
            std::ref(cv), std::ref(command_mutex));
    
        std::string userInput;
        do
        {
            std::cout << "Please enter command: ";
            std::cin >> userInput;
    
            if (userInput == "clients")
                command = 1;
            else if (userInput == "msg")
                command = 2;
            else
                continue; // Unidentified command, don't bother the serverThread
            std::unique_lock lock(command_mutex);
            // Wait for command == 0. Note that it will be rechecked either
            // due to cv.notify_one() from serverThread or possibly during spurious wakeups
            cv.wait(lock, [&]() {return command == 0; }); 
    
        } while (userInput != "exit");
    
        running = false;
        serverThread.join();
    }
    
  2. 更简单,不易出错)只需在主线程中处理命令即可。当然,然后
    clients
    msg
    以及可能的其他数据必须可以从主系统访问。您可以在
    main
    函数中创建它们,然后再次将
    std::ref
    作为参数传递给
    runTcpServer
    ,但此时最好创建一个类(我们称之为
    TcpServer
    )来保存共享状态和必要的同步基元。这次
    std::mutex
    足以保护对共享资源的访问,无需
    std::conditional_variable
    或担心需要不同步的控制台 IO(因为它仅通过一个线程完成):
    class Server
    {
        unsigned short port{ 50000 };
        std::atomic<bool> running{ true };
        std::vector<sf::TcpSocket*> clients;
        tcpMessage msg;
        std::mutex m;
    
        void runTcpServer()
        {
            sf::TcpListener listener;
            if (listener.listen(port) != sf::Socket::Done)
                return;
            sf::SocketSelector selector;
            selector.add(listener);
    
            while (running)
            {
                // We don't need to do anything other then process incoming packets here,
                // but we still set reasonable timeout to periodically wakeup
                // to check whether running == false and the thread needs to stop
                if (selector.wait(sf::seconds(0.1f)))
                {
                    // Block resource mutex while handling incoming packet
                    std::lock_guard lock{m};
                    // ...
                }
            }
        }
    
    public:
        void runMain()
        {
            std::thread serverThread(&runTcpServer, this);
    
            std::string userInput;
            do
            {
                std::cout << "Please enter command: ";
                std::cin >> userInput;
    
                // Block resource mutex while handling command
                std::lock_guard lock{m};
                if (userInput == "clients")
                {
                    // ...
                }
                else if (userInput == "msg")
                {
                    // ...
                }
            } while (userInput != "exit");
    
            running = false;
            serverThread.join();
        }
    };
    
    int main()
    {
        Server server;
        server.runMain();
        return 0;
    }
    
© www.soinside.com 2019 - 2024. All rights reserved.