Nie blokujący skrypt socketu UDP

0

Witam wszystkich - to mój pierwszy post tutaj a ponieważ jestem świeżakiem w pythonie to pewnie nie ostatni :-)
Otóż próbuję zrobić prosty interpreter poleceń na UDP, tak abym mógł "rozmawiać" z większym skryptem.
Chciałbym zawrzeć w głównym skrypcie odbieranie danych z socketa ale nie mogę sobie pozwolić na funkcje blokującą ponieważ główny skrypt również zajmuje się odbieraniem i przetwarzaniem danych (odbiera je z portu szeregowego co 1ms i po obróbce zapisuje do pliku). Poniższy kod działa (nie blokuje) ale zauważyłem duże zużycie procesora przez proces pythona z tym kodem.
Aby program działał, celowo nie obsługuję wyjątku, który funkcja serverSock.recvfrom(1024) zgłasza za każdym obiegiem pętli gdy nie ma odebranych danych. Czy duże zużycie procesora może być związane właśnie z występowaniem tego wyjątku w każdym obiegu pętli? Może ktoś zna ciekawsze rozwiązanie na odbieranie danych bez blokowania np. na zasadzie cyklicznego wywoływania funkcji, która zwraca True jak są dane i False jak nie ma?

import socket

UDP_IP = "192.168.1.21"
UDP_PORT = 5050
serverSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
serverSock.bind((UDP_IP, UDP_PORT))
serverSock.setblocking(False)
print('Nasłuchuję na: {}:{}'.format(UDP_IP, UDP_PORT))
while True:
    try:
        data, addr = serverSock.recvfrom(1024)
    except:
        pass
    else:
        sdata = data.decode()
        print('Odebrano: ' + sdata)
        print(addr)
        if 'start' in sdata:
            print('OK')
            serverSock.sendto('OK - START\n'.encode(), addr)
        if 'stop' in sdata:
            print('OK')
            serverSock.sendto('OK - STOP\n'.encode(), addr)
        if 'exit' in sdata:
            exit()
1

Poczytaj sobie o module select

1

Jeśli Twój program ma charakter sieciowy i docelowo mierzysz w obsługiwanie więcej niż jednego połączenia wówczas rozważ czy nie lepiej byłoby przejść na asyncio. Asyncio to to standardowa biblioteka, która oferuje prymitywy do takich zadań jakie własnie potrzebujesz.

Główne atuty:

  • nie musisz kodować samemu pętli do obsługi zdarzeń (tak jak w powyższym ciekawym artykule o select), ta pętla jest już w asyncio i dzięki dodatkowej wartwie abstrakcji bezproblemowo będziesz mógł do swojego projektu włączyć inne asynchroniczne bibloteki (np. do bazy)

  • piszesz kod asynchroniczny, ale zapis (dzięki async/await) sprawia, że kod da się w zasadzie czytać tak jakby wykonywał się synchronicznie.

  • jeśli z czasem zaczniesz uogólniać pętlę, to wówczas (z powodu braku odpowiednich abstrakcji) prawdopodobnie zaczniesz bazować na callbackach. Trudno powiedzieć, co zrobisz, ale bazowanie na callbackach to najprostsze rozwiązanie, które z czasem może sprawić Ci dodatkowe problemy: http://callbackhell.com/

Zerknij sobie na ten przykład, może uda Ci się go dopasować do swoich potrzeb:
https://gist.github.com/vxgmichel/e47bff34b68adb3cf6bd4845c4bed448

0

Witam, dzięki za odpowiedzi. Z asyncio spotkałem się przy odbieraniu danych z UART i choć w tym przypadku zrobiłem to na pyserial read_until() to spróbuję zrobić UDP już może na asyncio. Całe życie programowałem w C mikrokontrolery i tam nie miałem problemu z blokowaniem (polling i callbacki) dlatego moje przyzwyczajenia sprawiają że tu się trochę zaskoczyłem.

1 użytkowników online, w tym zalogowanych: 0, gości: 1