Intereting Posts
При обнулении структуры, такой как sockaddr_in, sockaddr_in6 и addrinfo перед использованием, что является правильным: memset, инициализатор или либо? Как читать массив, сохраненный в двоичном режиме, в текстовый файл в C Скопируйте файл, пропустив первые n строк и последние m строк Быстрое целочисленное преобразование в десятичное целое Входы для улучшения отладки кода, кроме журналов и кодов ошибок Я просто не могу понять strcat Как читать данные из неизвестного типа ввода (filestream или stdin) C как генерировать предупреждение о ненулевом аргументе для моей пользовательской функции? C printf float rounding Постефильная эвауляция с использованием стеков и C netbeans c автоматическое заполнение кода Можно определить функционально-подобный макрос с переменным телом? Функция для вычисления контрольной суммы CRC16 C – структура, отмеченная как не объявленная в компиляторе Преобразование IP-адреса в целое число

Разблокировать recvfrom, когда сокет закрыт

Предположим, я начинаю stream, который можно получить на порте. Вызов сокета будет заблокирован на recvfrom. Затем, как-то в другом streamе, я закрываю сокет.

В Windows это разблокирует recvfrom, и выполнение моего streamа завершится.

В Linux это не разблокирует recvfrom, и, как результат, мой stream сидит ничего не делает навсегда, и выполнение streamа не завершается.

Может ли кто-нибудь помочь мне в том, что происходит в Linux? Когда гнездо закрыто, я хочу, чтобы recvfrom разблокировал

Я продолжаю читать об использовании select (), но я не знаю, как использовать его для моего конкретного случая.

shutdown(sock, SHUT_RDWR) в гнезде, а затем дождаться завершения streamа. (т.е. pthread_join ).

Вы могли бы подумать, что close() будет разблокировать recvfrom() , но это не для linux.

Вот эскиз простого способа использования select () для решения этой проблемы:

 // Note: untested code, may contain typos or bugs static volatile bool _threadGoAway = false; void MyThread(void *) { int fd = (your socket fd); while(1) { struct timeval timeout = {1, 0}; // make select() return once per second fd_set readSet; FD_ZERO(&readSet); FD_SET(fd, &readSet); if (select(fd+1, &readSet, NULL, NULL, &timeout) >= 0) { if (_threadGoAway) { printf("MyThread: main thread wants me to scram, bye bye!\n"); return; } else if (FD_ISSET(fd, &readSet)) { char buf[1024]; int numBytes = recvfrom(fd, buf, sizeof(buf), 0); [...handle the received bytes here...] } } else perror("select"); } } // To be called by the main thread at shutdown time void MakeTheReadThreadGoAway() { _threadGoAway = true; (void) pthread_join(_thread, NULL); // may block for up to one second } в // Note: untested code, may contain typos or bugs static volatile bool _threadGoAway = false; void MyThread(void *) { int fd = (your socket fd); while(1) { struct timeval timeout = {1, 0}; // make select() return once per second fd_set readSet; FD_ZERO(&readSet); FD_SET(fd, &readSet); if (select(fd+1, &readSet, NULL, NULL, &timeout) >= 0) { if (_threadGoAway) { printf("MyThread: main thread wants me to scram, bye bye!\n"); return; } else if (FD_ISSET(fd, &readSet)) { char buf[1024]; int numBytes = recvfrom(fd, buf, sizeof(buf), 0); [...handle the received bytes here...] } } else perror("select"); } } // To be called by the main thread at shutdown time void MakeTheReadThreadGoAway() { _threadGoAway = true; (void) pthread_join(_thread, NULL); // may block for up to one second } 

Более элегантный способ состоял бы в том, чтобы избежать использования функции тайм-аута select и вместо этого создать пару сокетов (используя socketpair ()) и передать основной stream байту на его конце пары сокетов, когда он хочет, чтобы stream ввода-вывода чтобы уйти и выйти из streamа ввода / вывода, когда он получает байт на своем гнезде на другом конце сокета. Однако я оставлю это упражнение для читателя. 🙂

Также часто рекомендуется также установить сокет в неблокирующий режим, чтобы избежать (небольшой, но ненулевой) вероятности того, что вызов recvfrom () может блокироваться даже после того, как select () указал, что сокет готов к чтению , как описано здесь . Но режим блокировки может быть «достаточно хорош» для вашей цели.

Не ответ, но личная страница Linux содержит интересную цитату:

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

Вы просите о невозможности. Существует просто нет возможности для streamа, который вызывает close знать, что другой stream заблокирован в recvfrom . Попробуйте написать код, который гарантирует, что это произойдет, вы обнаружите, что это невозможно.

Независимо от того, что вы делаете, всегда будет возможно, чтобы звонок close к гонке с призывом к recvfrom . Призыв к close изменяет имя дескриптора сокета, поэтому он может изменить семантический смысл вызова для recvfrom .

Нет никакого способа, чтобы stream, который вводит recvfrom каким-то образом сигнализирует streamу, который вызывает close что он заблокирован (в отличие от того, что он собирается заблокировать или просто ввести системный вызов). Таким образом, в буквальном смысле нет никакого способа гарантировать, что поведение close и recvfrom предсказуемо.

Рассмотрим следующее:

  1. Поток собирается вызвать recvfrom , но он получает предварительные упущения другими вещами, которые должна выполнять система.
  2. Позже stream вызывает close .
  3. Поток, запущенный библиотекой ввода-вывода системы, вызывает socket и получает тот же decsriptor, что и тот, который вы close d.
  4. Наконец, stream вызывает recvfrom , и теперь он получает из сокета, который открыла библиотека.

К сожалению.

Никогда не делал ничего такого, даже отдаленно. Ресурс не должен быть освобожден, если другой stream используется или может быть использован. Период.

Когда гнездо закрыто, я хочу, чтобы recvfrom разблокировал

Функция recvfrom () является функцией, специфичной для сокетов UDP на Python. Вот краткий обзор того, как я решил проблему, используя идею, называемую «опрос» (попробуйте запустить программу, а также инструкции печати дадут вам четкое представление о том, что происходит):

 import socket import threading import signal import time # Custom class to create a socket, close the socket, and poll the socket class ServerSocket(): def __init__(self, addresses): # "Standard" way to create and preapare a working socket self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.bind(addresses) def poll(self): self.socket.settimeout(2) modifiedMsg, senderAddress = self.socket.recvfrom(1024) def close(self): self.socket.close() class ServiceExit(Exception): """ Custom exception which is used to trigger the clean exit of all running threads and the main program. """ pass def service_shutdown(signum, frame): raise ServiceExit # Custom class to create a UDP server on a separate thread. # This server will know to close the blocking UDP socket when the user # of the main program signals termination via typing CTRL-C into the terminal class Server(threading.Thread): def __init__(self, addresses): threading.Thread.__init__(self) self.mysocket = ServerSocket(addresses) # This flag below will help us determine when to stop the "run" loop below # The while loop below is where interrupt the blocking recvfrom() call by # timing out every 2 seconds and checking to see if the flag has been set # to discontinue the while loop self.shutdown_flag = threading.Event() def run(self): while not self.shutdown_flag.is_set(): try: print('socket blocking') self.mysocket.poll() except socket.timeout: print('socket unblocked') pass # as a final step, we close the socket self.mysocket.close() print('socket closed') def main(): # assign the methods that will be called when our main program receives a SIGTERM or SIGINT signal # You can send this main problem such a signal by typing CTRL-C after you run this program signal.signal(signal.SIGTERM, service_shutdown) signal.signal(signal.SIGINT, service_shutdown) # Start the server thread that will eventually block on recvfrom() try: print('starting udp server thread') udp_server = Server(('localhost', 5000)) udp_server.start() while True: time.sleep(0.5) # This server will accept UDP packets on the local host at port 5000 # Feel free to change these settings to fit your needs except ServiceExit: print('shutting down server thread') udp_server.shutdown_flag.set() udp_server.join() print('server thread shut down') if __name__ == '__main__': main()