Сервер не будет подключаться более чем к одному клиенту?

#c #multithreading #networking #sfml

#c #многопоточность #сеть #sfml

Вопрос:

Проблема в том, что он подключается только к одному клиенту вместо двух. Кто-нибудь может помочь мне выяснить, почему?

Сервер:

 #include <SFML/System.hpp>
#include <SFML/Network.hpp>
#include <iostream>

void sendInfo(void *UserData)
{
    sf::IPAddress* ip = static_cast<sf::IPAddress*>(UserData);
    // Print something...
    while(true){
        // Create the UDP socket
        sf::SocketUDP Socket;

        // Create bytes to send
        char Buffer[] = "sending info.";

        // Send data to "192.168.0.2" on port 4567
        if (Socket.Send(Buffer, sizeof(Buffer), *ip, 4444) != sf::Socket::Done)
        {
            // Error...
        }
    }
}

void receiveInfo(void *userData)
{
    // Print something...
    while(true){
        // Create the UDP socket
        sf::SocketUDP Socket;

        // Bind it (listen) to the port 4567
        if (!Socket.Bind(4444))
        {
            // Error...
        }

        char Buffer[128];
        std::size_t Received;
        sf::IPAddress Sender;
        unsigned short Port;
        if (Socket.Receive(Buffer, sizeof(Buffer), Received, Sender, Port) != sf::Socket::Done)
        {
            // Error...
        }

        // Show the address / port of the sender
        std::cout << Buffer << std::endl;

        Socket.Close();

    }
}

int main()
{
    sf::IPAddress client[2];
    int connected = 0;
    while(connected < 2){
        // Create the UDP socket
        sf::SocketUDP Socket;

        // Bind it (listen) to the port 4567
        if (!Socket.Bind(4444))
        {
            // Error...
        }

        char Buffer[128];
        std::size_t Received;
        sf::IPAddress Sender;
        unsigned short Port;
        if (Socket.Receive(Buffer, sizeof(Buffer), Received, Sender, Port) != sf::Socket::Done)
        {
            // Error...
        }

        // Show the address / port of the sender
        client[connected] = Sender;

        Socket.Close();

        sf::Thread* send = new sf::Thread(amp;sendInfo, amp;client[connected]);
        sf::Thread* receive = new sf::Thread(amp;receiveInfo, amp;client[connected]);
        // Start it !
        send->Launch();
        receive->Launch();
        connected  ;
    }

    while(true){

    }

    return EXIT_SUCCESS;
}
  

Клиент:

 #include <SFML/System.hpp>
#include <SFML/Network.hpp>
#include <iostream>

void sendInfo(void *UserData)
{
    // Print something...
    while(true){
        // Create the UDP socket
        sf::SocketUDP Socket;

        // Create bytes to send
        char Buffer[] = "client sending info.";

        // Send data to "192.168.0.2" on port 4567
        if (Socket.Send(Buffer, sizeof(Buffer),  "127.0.0.1", 4444) != sf::Socket::Done)
        {
            // Error...
        }
    }
}

void receiveInfo(void *userData)
{
    // Print something...
    while(true){
        // Create the UDP socket
        sf::SocketUDP Socket;

        // Bind it (listen) to the port 4567
        if (!Socket.Bind(4444))
        {
            // Error...
        }

        char Buffer[128];
        std::size_t Received;
        sf::IPAddress Sender;
        unsigned short Port;
        if (Socket.Receive(Buffer, sizeof(Buffer), Received, Sender, Port) != sf::Socket::Done)
        {
            // Error...
        }

        // Show the address / port of the sender
        std::cout << Buffer << std::endl;

        Socket.Close();

    }
}

int main()
{
    // Create the UDP socket
    sf::SocketUDP Socket;

    // Create bytes to send
    char Buffer[] = "Client Joined.";

    // Send data to "192.168.0.2" on port 4567
    if (Socket.Send(Buffer, sizeof(Buffer), "127.0.0.1", 4444) != sf::Socket::Done)
    {
        // Error...
    }

    sf::Thread* send = new sf::Thread(amp;sendInfo);
    sf::Thread* receive = new sf::Thread(amp;receiveInfo);
    // Start it !
    send->Launch();
    receive->Launch();


    while(true){

    }

    return EXIT_SUCCESS;
}
  

Комментарии:

1. Кажется, ваш код использует UDP-сокеты с TCP-подобной логикой. На стороне сервера у вас должен быть только 1 сокет для обоих клиентов. Я подозреваю, что происходят ошибки, но вы ничего не диагностируете своими комментариями «Ошибка …»!

2. Почему вы используете разные сокеты для каждого вызова Send и Recv?

Ответ №1:

Перво-наперво: это сервер чата или «более типичный» сервер?

Если это сервер чата, то вам нужно либо иметь список сокетов, которые подключены к клиентам (вы можете подключать сокеты UDP с помощью connect() вызова, что очень удобно, и это также помогает снизить вероятность подделки одноранговых узлов), либо список всех адресов клиентов, которые вы можете указать sendto() или sendmsg() .

Более «типичные» серверы не будут пытаться отправлять сообщения какому-либо клиенту, кроме того, который последним отправил запрос: эти серверы обычно не сохраняют ничего от клиентов, а вместо этого используют recvfrom() or recvmsg() для получения адреса однорангового узла для использования в последующих вызовах sendto() or sendmsg() .

Кроме того, большинство протоколов полагаются только на один хорошо известный порт; сервер использует один конкретный порт по соглашению, но клиенты выбирают любой порт, который открыт и свободен. FTP также сильно зависит от хорошо известных портов на стороне клиента, и в результате туннелирование через брандмауэры с трансляцией сетевых адресов сопряжено с огромными трудностями.

Это не просто академический вопрос: и ваш клиент, и ваш сервер пытаются bind() выполнить перенос 4444 . Это означает, что вам нужно по крайней мере два IP-адреса на одной машине для тестирования, или использовать программное обеспечение виртуализации для запуска совершенно отдельной машины на том же оборудовании, или просто иметь в наличии две машины. Это требует больше работы, чем нужно, и у клиентов нет причин заботиться о номерах своих локальных портов:

Сервер:

     // Bind it (listen) to the port 4567
    if (!Socket.Bind(4444))
    {
        // Error...
    }
  

Клиент:

     // Bind it (listen) to the port 4567
    if (!Socket.Bind(4444))
    {
        // Error...
    }
  

Пуф!Эти два устройства никогда не будут запускаться на одном хосте без существенных ухищрений. Я ожидаю, что ваше «он подключается к одному», вероятно, просто сервер или клиент, подключающийся к самому себе, но без какого-либо кода для заполнения этих // Error блоков, было бы трудно сказать наверняка.

(И пока мы здесь, я хотел бы отвести разговор в сторону о комментариях; комментарии, которые просто повторяют, что делает код, не очень полезны. Вы заметите, что большинство ваших комментариев на самом деле неверны, ссылаясь на неправильные IP-адреса или порты. Некоторые просто не добавляют никакой информации:

     // Create the UDP socket
    sf::SocketUDP Socket;
  

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

     // udp doesn't require listen or accept
    if (!Socket.Bind(4444))
  

Это не очевидно при чтении кода и не будет ошибкой, когда номер порта считывается из переменной среды, параметра командной строки, файла конфигурации или реестра. (Это может показаться излишним для команды людей, знакомых с sockets API, но может оказаться полезным для программиста, не слишком знакомого с различиями между UDP и TCP.)

Хорошие названия функций, имена переменных и т.д. Почти каждый раз будут вызывать комментарии. Конец в сторону. 🙂

И теперь, более мелкая придирка: ваши обработчики потоков выполняют некоторые задачи, подобные этой:

 while(1) {
    socket s;
    bind s;
    r = recv s;
    print r;
    close s;
}
  

Все эти ненужные процессы создания, привязки и закрытия являются пустой тратой энергии, как энергии компьютера, так и (что гораздо важнее) вашей энергии. Рассмотрите следующие два варианта перезаписи:

 recv_thread() {
    socket s;
    bind s;
    while (1) {
        r = recv s;
        print r;
    }
    close s;
}
  

или

 recv_thread(s) {
    while (1) {
        r = recv s;
        print r;
    }
}
/* ... */
socket s;
bind s;
sf::Thread* rt = new sf::Thread(amp;recv_thread);
rt->Launch(s);
  

Первый вариант — это простой рефакторинг вашего существующего кода; он сохраняет создание и уничтожение сокета в функции потока, но выводит инварианты цикла из цикла. Код внутри цикла теперь выполняет только то, что необходимо.

Второй вариант — это более радикальная переработка: он переносит создание сокета в основной поток, где обработка ошибок, вероятно, намного проще, а функция thread выполняет только то, что требуется от удаленного узла, чтобы этот поток выполнял. (Если бы вы хотели перейти с UDP на TCP, второй вариант был бы намного проще — ваш многопоточный код мог бы вообще не нуждаться в каких-либо изменениях.)

Надеюсь, это поможет. 🙂