Gniazda w linuxie

Jak komunikować się z procesami działającymi na innych hostach?

Gniazda sieciowe należą do najbardziej zaawansowanych i uniwersalnych możliwości komunikacji międzyprocesorowych w linuksie. Pociąga to za sobą rozmaite konsekwencje - ich obsługa nie jest tak prosta jak obsługa sygnałów, ale pozwalają za to na komunikację między procesami które nie są uruchomione na tym samym komputerze.

Gniazda stanowią końcówki kanału komunikacyjnego i muszą zostać skonfigurowane przed wysyłaniem komunikatów.

Sockety są instancją uniwersalną, wykorzystuje się je zarówno w trybie połączeniowym (TCP), bezpołączeniowym (UDP) jak i w trybie lokalnym (AF_UNIX). Co ciekawe, utworzonego i skojarzonego już gniazda będzie można używać jak zwykłego pliku, tzn. można do niego pisać (funkcja systemowa write() ) i z niego czytać (funkcja read() ).

Gniazda tworzone są za pomocą całkiem przyjaznego api:

  • Jako domain podajemy PF_LOCAL (zamiennie AF_LOCAL), gdy gniazdo obsługiwać procesy z tej samej maszyny lub PF_INET (zamiennie AF_INET) gdy ma obsługiwać procesy komputerów połączonych w sieć.
  • Argument type określa typ komunikacji: połączeniowy (TCP) - SOCK_STREAM lub bezpołączeniowy (UDP) - SOCK_DGRAM.
  • Za protocol podajemy typ protokołu jaki ma być obsługiwany (np TCP) lub NULL gdy chcemy, żeby system sam określił typ protokołu.


Socket który został stworzony musi zostać przypisany do konkretnego adresu i numeru portu by jednoznacznie określić drugi koniec kanału komunikacyjnego.

  • Jako sockfd podajemy deskryptor gniazda lokalnego (deskryptor jest identyfikatorem gniazda zwracanym przez funkcje socket podczas tworzenia)
  • W miejsce serv_addr podajemy wypełnioną strukturę opisującą adres serwera. Wypełniamy pole .sin_family (wpisując tą samą domenę co w przypadku struktry socket, a więc AF_INET) i .sin_port (podając numer portu).
  • Natomiast addrlen to po prostu sizeof() struktury serv_addr

Utworzone, ale nie skonfigurowane gniazdo jest w stanie nawiązać komunikację w trypie UDP, natomiast w trybie TCP musimy jeszcze nawiązać połączenie. Należy to uczynić za pomocą funkcji connect:

Składnia funkcji jest identyczna jak w przypadku funkcji bind.

Mając tak przygotowane gniazdo możemy rozpocząć komunikację - wysyłać lub odbierać dane. W przypadku połączenia UDP korzystamy z funkcji recvfrom, natomiast komunikacja TCP która ma już otwarte połączenie zadowala się uproszczoną funkcją recv W drugim przypadku nie musimy podawać adresu z którego chcemy odbierać dane, bo zadeklarowany został podczas tworzenia połączenia (connect).

  • sockfd deskryptor gniazda
  • buf to wskaźnik do bufora z którego będziemy wysyłąć dane
  • len jest długością komunikatu
  • flags - mamy do wyboru:
    • MSG_OOB - Onacza chęć odebrania danych pilnych, tzw out-of-band (przesyłane w innym logicznym kanale na tym samym łączu)
    • MSG_PEEK - Odbiera dane z początku kolejki, ale nie usuwa ich, więc pozostaje możliwość ich ponownego odebrania.
    • MSG_WAITALL - blokuje wywołania recv() do czasu zapełnienia całego zadeklarowanego bufora.
  • src_addr podajemy adres z którego chcemy odbierać dane.
  • addrlen to wielkość struktury adresu nadawcy.

Funkcja send jest analogiczna, podajemu tu jednak adres odbiorcy. Przykłady implementacji kienta i serwera UDP dostępne w moim repozytorium git

TAGS: socket gniazda linux