recv не возвращается после закрытия сокета udp в linux - PullRequest
0 голосов
/ 23 сентября 2018

Я пытаюсь написать клиент-серверное приложение, используя протокол udp, но у меня проблема с окончанием соединения.
Я открываю два сокета (один - «сервер», а другой - «клиент»), и в то время каксервер, получающий от клиента асинхронный клиент, отправляет ему одно простое сообщение, которое выводится на консоль.
После некоторого сна (чтобы убедиться, что сервер снова вызовет recv) сокет клиента и сокета сервера будет закрыт.В этот момент я ожидал, что recv вернет -1 и асинхронность закончится.
Но на самом деле происходит то, что recv застрял навсегда *.
Если перед закрытием сокета я отправляю пустой пакет (переменная sendToMakeExit установленаtrue в коде), recv возвращается с этим пустым пакетом и только после следующего вызова возвращает -1, хотя сокет был закрыт при первом вызове.

    const bool sendToMakeExit = false;
    const int port = 2000;
    const auto addr = "127.0.0.1";
    int serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    sockaddr_in target;
    target.sin_family = AF_INET;
    target.sin_port = htons(port);
    inet_pton(AF_INET, addr, &target.sin_addr);
    bind(serverSocket, (sockaddr *) &target, sizeof(target));
    auto readAsync = std::async(std::launch::async, [&serverSocket] {
        const int MAX_READ = 4096;
        char readBuf[MAX_READ];
        ssize_t actualRead;
        do {
            actualRead = recv(serverSocket, readBuf, MAX_READ, 0);
            if (actualRead > 0) {
                cout << readBuf << endl;
            }
        } while (actualRead != -1);
    });
    int clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    connect(clientSocket, (sockaddr *) &target, sizeof(target));
    this_thread::sleep_for(chrono::seconds(1));
    send(clientSocket, "test", 5, 0);
    this_thread::sleep_for(chrono::seconds(1));
    close(clientSocket);
    if (sendToMakeExit) {
        sendto(serverSocket, nullptr, 0, 0, (sockaddr *) &target, sizeof(target));
    }
    close(serverSocket);

* Если язапустите этот код в режиме отладки и создайте новую точку останова, когда recv застрял, что удивительно, что recv возвращает -1.
Как я могу получить recv вернет -1, когда я закрою сокет?

1 Ответ

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

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

} while (actualRead > 0);

Однако я бы порекомендовал иметьпеременная флага, которая указывает, должен ли поток продолжать работать или нет, например:

volatile bool running = true;

auto readAsync = std::async(std::launch::async, [&serverSocket, &running] {
    ...
    while (running) {
        ...recv()...
    }
});

...
running = false;
sendto(serverSocket, ...);
readAsync.wait();
close(serverSocket);

Обратите внимание, что я добавил строку, ожидающую завершения readAsync перед закрытием сокета, чтобы предотвратить любые авариииз случившегося: есть маленькое окно, в котором сокет признан недействительным, но readAsync может по-прежнему вызывать recv() для него.Если у вас еще больше потоков, может также случиться, что вы закроете сокет в этом потоке, другой поток откроет новый сокет и получит тот же номер файлового дескриптора, что и тот, который вы только что закрыли, и тогда поток readAsync будет использовать неправильныйрозетка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...