przejście do zawartości
Jan Kończak
Narzędzia użytkownika
Zaloguj
Narzędzia witryny
Narzędzia
Pokaż stronę
Poprzednie wersje
Odnośniki
Ostatnie zmiany
Menadżer multimediów
Indeks
Zaloguj
Ostatnie zmiany
Menadżer multimediów
Indeks
Jesteś tutaj:
start
»
sk2
»
sockets_concurrency
sk2:sockets_concurrency
Ta strona jest tylko do odczytu. Możesz wyświetlić źródła tej strony ale nie możesz ich zmienić.
====== Obsługa wielu strumieni naraz ====== Typowo aplikacje (jakiekolwiek, włączając sieciowe) muszą jednocześnie obsługiwać wiele źródeł zdarzeń (np. dane gotowe do odbioru, użytkownik kliknął na menu, naciśnięto enter). Domyślnie operacje na gniazdach (np. ''connect'', ''accept'', ''read'') blokują przetwarzanie. Można to zmienić (tak jak dla każdego innego pliku w Linuksie) na obsługę nieblokującą, ale oczywiście aktywne czekanie ([[https://en.wikipedia.org/wiki/Busy_waiting]]) jest bardzo głupim pomysłem((choć są od tego wyjątki - np. [[https://en.wikipedia.org/wiki/Spinlock|spinlock]] czy [[https://www.dpdk.org/|DPDK]])). Do obsługi wielu źródeł zdarzeń stworzono dedykowane metody, można też używać typowych metod pisania aplikacji współbieżnych. Zwolennicy SE nazwą to "wzorcami projektowymi". Przegląd typowych metod tworzenia aplikacji sieciowych: * Iteracyjnie – kiedy współbieżność jest zbędna. * Pętla zdarzeń (event loop, [[https://en.wikipedia.org/wiki/Event_loop|[1]]], [[https://web.archive.org/web/20190730174916/https://pl.wikipedia.org/wiki/Programowanie_sterowane_zdarzeniami|[2]]]) – programista wpierw przygotowuje kod (funkcje) obsługi możliwych zdarzeń, następnie w pętli czeka na zdarzenie i wywołuje kod powiązany ze zdarzeniem. Aplikacja może być jedno- lub wielowątkowa, muszą być dostępne funkcje czekające na zdarzenie – dla I/O pod Linuksem to ''select'', ''poll'' i ''epoll''. Praktycznie wszystkie programy z GUI wykorzystują pętlę zdarzeń przynajmniej do obsługi GUI. * Aplikacja wielowątkowa – każde źródło zdarzeń – np. gniazdo – jest obsługiwane w osobnym wątku. ====== Wiele wątków ====== [[sk2:cpp11_threads|Wątki w C++]] //Zadanie 1.// Napisz własną wersję programu ''netcat'' wspierającą tylko klienta TCP – program, który nawiąże połączenie TCP pod wskazany adres, następnie dane przychodzące na standardowe wejście będzie wysyłać przez to połączenie, a dane które przyszły z sieci wypisze na standardowe wyjście.\\ (Możesz skorzystać z {{:sk2:tcp_client_template.cpp|przykładowego kodu}}) //Zadanie 2.// Czat – napisz serwer, który każdą otrzymaną wiadomość przekaże wszystkim połączonym klientom. \\ (Możesz skorzystać z {{:sk2:tcp_server_template.cpp|przykładowego kodu}}) ====== Zdarzenia ====== ===== Funkcje biblioteczne ===== System Linux zawiera 3 podstawowe funkcje pozwalające na czekanie na przychodzące zdarzenia na deskryptorach plików: * ''select'' – "klasyczna" funkcja, ma kilka dziwnych ograniczeń. Dostaje zbiór deskryptorów, oczekuje na zdarzenie, modyfikuje przekazany zbiór deskryptorów zostawiając tylko te na których można wykonać read/write/lub na których wystąpił wyjątek. (POSIX) * ''poll'' – zbudowana podobnie jak ''select'', ale m.inn. nie ma ograniczenia na numery monitorowanych deskryptorów((Patrz [[https://man7.org/linux/man-pages/man2/select.2.html|man 2 select]].)), można użyć ponownie struktury opisującej deskryptory etc. (POSIX) * ''epoll'' – specyficzna dla Linuksa funkcja. Inny pomysł: program informuje jądro systemu na które deskryptory chce czekać, potem wywołuje funkcję czekającą na zdarzenie i dostaje deskryptory które są gotowe do pracy. Dłuższe porównanie: http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/ <small>Od kilku lat trwają prace nad mechanizmem ''[[https://en.wikipedia.org/wiki/Io_uring|io_uring]]'', pozwalającym na wykonywanie jednocześnie wielu operacji na plikach. Mechanizm pozwala na przekazanie do kernela żądań wykonania konkretnych operacji, oraz oferuje funkcję do czekania na wykonanie jakiejś ze zleconych wcześniej operacji.</small> ===== poll ===== Aby stworzyć program korzystający z funkcji ''poll'', należy:<html><div style="margin-top:-1.4em"></div></html> - dodać plik nagłówkowy ''poll.h'' - przygotować tablicę struktur ''pollfd'' i wypełnić: * ''.fd'' – deskryptor pliku do monitorowania, * ''.events'' – zbiór monitorowanych zdarzeń: * ''POLLIN'' – funkcja ''poll'' ma się przerwać, jeśli można wywołać bez czekania ''read()/…'' lub ''acccept()'' * ''POLLOUT'' – funkcja ''poll'' ma się przerwać, jeśli można wywołać bez czekania ''write()/…'' \\ //uwaga//: na możliwość wysłania danych czeka się wtedy kiedy ma się dane do wysłania \\ //uwaga//: próba zapisania większej liczby bajtów niż jest wolnego miejsca w buforze nadawczym i tak zablokuje ''write''<html></div></li></ul><div class="li"></html>//uwaga//: jeśli na gnieździe wystąpił błąd, to ''read()/write()/…'' też można wywołać bez czekania \\ reszta zdarzeń (''POLLHUP'', ''POLLERR'', ''POLLPRI'', …) opisana w ''man poll'' <html></div><!--</html> * <html>--><div class="li"></html> * zostawić w spokoju ''.revents'' – tam pojawi się informacja o tym co wystąpiło - w pętli wywoływać funkcję ''poll(…)''. \\ <small>ostatni argument funkcji ''poll'' to maksymalny czas oczekiwania; aby funkcja czekała bez limitu, należy podać tam dowolną ujemną wartość</small> - sprawdzać który deskryptor jest gotowy przeglądając pola ''.revents'' <html> <pre class="code cpp" style="user-select: none; line-height: 100%"> <span title="Tablica struktur 'pollfd' opisująca na których plikach i na co czekać" style="background-color:#fb02">pollfd pfds<span class="br0">[</span>COUNT<span class="br0">]</span><span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span></span> <span style="color:grey">─┐</span> <span title="poll() może monitorować dowolne pliki, np. standardowe wejście" style="background-color:#f002">pfds<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span>.<span class="me1">fd</span> <span class="sy1">=</span> STDIN_FILENO<span class="sy4">;</span></span> pfds<span class="br0">[</span><span class="nu0">2</span><span class="br0">]</span><span title="'fd' wskazuje na którm pliku czekać na zdarzena, a 'events' określa na które zdarzenia na wskazanym pliku" style="background-color:#0f02">.<span class="me1">fd</span> <span class="sy1">=</span></span> cliSock1<span class="sy4">;</span> <span style="color:grey">│</span> <span title="poll() może monitorować dowolne pliki, np. standardowe wejście" style="background-color:#f002">pfds<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span>.<span class="me1">events</span> <span class="sy1">=</span> POLLIN<span class="sy4">;</span></span> pfds<span class="br0">[</span><span class="nu0">2</span><span class="br0">]</span><span title="'fd' wskazuje na którm pliku czekać na zdarzena, a 'events' określa na które zdarzenia na wskazanym pliku" style="background-color:#0f02">.<span class="me1">events</span> <span class="sy1">=</span></span> POLLIN<span class="sy4">;</span> <span style="color:grey">├─ (2)</span> <span title="Na gnieździe nasłuchującym POLLIN oznacza możliwość wykonania accept() bez czekania" style="background-color:#00f2">pfds<span class="br0">[</span><span class="nu0">1</span><span class="br0">]</span>.<span class="me1">fd</span> <span class="sy1">=</span> serverSock<span class="sy4">;</span></span> pfds<span class="br0">[</span><span class="nu0">3</span><span class="br0">]</span>.<span class="me1">fd</span> <span class="sy1">=</span> cliSock2<span class="sy4">;</span> <span style="color:grey">│</span> <span title="Na gnieździe nasłuchującym POLLIN oznacza możliwość wykonania accept() bez czekania" style="background-color:#00f2">pfds<span class="br0">[</span><span class="nu0">1</span><span class="br0">]</span>.<span class="me1">events</span> <span class="sy1">=</span> POLLIN<span class="sy4">;</span></span> pfds<span class="br0">[</span><span class="nu0">3</span><span class="br0">]</span>.<span class="me1">events</span> <span class="sy1">=</span> <span title="Jeśli zostanie podane POLLIN, to 'poll()' zakończy się kiedy tylko będzie dało się bez czekania wykonać 'read' (w buforze są dane lub wystąpił błąd na gnieździe)" style="background-color:#0ff4">POLLIN</span><span class="sy3">|</span><span title="Jeśli zostanie podane POLLOUT, to 'poll()' zakończy się kiedy tylko będzie dało się bez czekania wykonać 'write' (tzn. da się wysłać przynajmniej jeden bajt lub wystąpił błąd na gnieździe)" style="background-color:#ff04">POLLOUT</span><span class="sy4">;</span> <span style="color:grey">─┘</span> ... ... <span class="kw1">while</span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="br0">{</span> <span class="kw4">int</span> ile_gotowych <span class="sy1">=</span> poll<span class="br0">(</span><span title="Tablica struktur 'pollfd' opisująca na których plikach i na co czekać" style="background-color:#fb02">pfds</span>, <span title="Wskazuje ile struktur pod wskaźnikiem 'pfds' ma odczytać funkcja 'poll'" style="background-color:#f082">liczbaStruktur</span>, <span title="Limit czasu oczekiwania na pierwsze zdarzenie: nieujemna wartość: maksymalny czas czekania w milisekundach, ujemna wartość: czekanie bez limitu czasowego" style="background-color:#0002"><span class="sy2">-</span><span class="nu0">1</span></span><span class="br0">)</span><span class="sy4">;</span> <span style="color:grey">═╾─ (3)</span> <span title="poll() może monitorować dowolne pliki, np. standardowe wejście" style="background-color:#f002"><span class="kw1">if</span><span class="br0">(</span>pfds<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span>.<span class="me1">revents</span> <span class="sy3">&</span> POLLIN<span class="br0">)</span> readingFromStdin<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span></span> <span style="color:grey">─┐</span> <span class="kw1">if</span><span class="br0">(</span>pfds<span class="br0">[</span><span class="nu0">1</span><span class="br0">]</span><span title="poll() wypełnia pole .revents, wpisując tam identyfikatory wszystkich zdarzeń które wystąpiły na pliku" style="background-color:#0f42">.<span class="me1">revents</span> <span class="sy3">&</span></span> POLLIN<span class="br0">)</span> acceptingNewClient<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span style="color:grey">│</span> <span class="kw1">for</span><span class="br0">(</span><span class="kw4">int</span> i <span class="sy1">=</span> <span class="nu0">2</span><span class="sy4">;</span> i <span class="sy1"><</span> liczbaStruktur<span class="sy4">;</span> <span class="sy2">++</span>i<span class="br0">)</span><span class="br0">{</span> <span style="color:grey">│</span> <span title="Jeśli zostanie podane POLLIN, to 'poll()' zakończy się kiedy tylko będzie dało się bez czekania wykonać 'read' (w buforze są dane lub wystąpił błąd na gnieździe)" style="background-color:#0ff4"><span class="kw1">if</span><span class="br0">(</span>pfds<span class="br0">[</span>i<span class="br0">]</span>.<span class="me1">revents</span> <span class="sy3">&</span> POLLIN<span class="br0">)</span></span> <span style="color:grey">├─ (4)</span> <span title="Jeśli zostanie podane POLLIN, to 'poll()' zakończy się kiedy tylko będzie dało się bez czekania wykonać 'read' (w buforze są dane lub wystąpił błąd na gnieździe)" style="background-color:#0ff4">readFromClient<span class="br0">(</span>pfds<span class="br0">[</span>i<span class="br0">]</span>.<span class="me1">fd</span><span class="br0">)</span><span class="sy4">;</span></span> <span style="color:grey">│</span> <span title="Jeśli zostanie podane POLLOUT, to 'poll()' zakończy się kiedy tylko będzie dało się bez czekania wykonać 'write' (tzn. da się wysłać przynajmniej jeden bajt lub wystąpił błąd na gnieździe)" style="background-color:#ff04"><span class="kw1">if</span><span class="br0">(</span>pfds<span class="br0">[</span>i<span class="br0">]</span>.<span class="me1">revents</span> <span class="sy3">&</span> POLLOUT<span class="br0">)</span></span> <span style="color:grey">│</span> <span title="Jeśli zostanie podane POLLOUT, to 'poll()' zakończy się kiedy tylko będzie dało się bez czekania wykonać 'write' (tzn. da się wysłać przynajmniej jeden bajt lub wystąpił błąd na gnieździe)" style="background-color:#ff04">writeToClient<span class="br0">(</span>pfds<span class="br0">[</span>i<span class="br0">]</span>.<span class="me1">fd</span><span class="br0">)</span><span class="sy4">;</span></span> <span style="color:grey">─┘</span> <span class="br0">}</span> <span class="br0">}</span> </pre> </html> //Zadanie 3.// Powtórz Zadanie 1, ale tym razem jako jednowątkowy program wykorzystujący ''poll''. ===== epoll ===== Mechanizm działania ''epoll'' jest inny niż ''select'' czy ''poll''. - Wpierw programista musi stworzyć interfejs do monitorowania plików funkcją <html><small></html>''epoll_create'' lub<html></small></html> ''epoll_create1'' – interfejs trafia do tablicy deskryptorów plików (analogicznie do ''open()'', ''pipe()'' czy ''socket()''). Po skończonej pracy zamyka się go podobnie jak każdy plik – funkcją ''close''. - Lista monitorowanych deskryptorów jest przechowywana w pamięci jądra systemu operacyjnego – aby ją uzupełnić, należy użyć funkcji ''epoll_ctl''. Pozwala ona powiązać deskryptor z listą zdarzeń na które oczekuje program oraz dowolnymi danymi mieszczącymi się w unii ''epoll_data_t''. - Funkcja czekająca na zdarzenia – ''epoll_wait'' – przyjmuje jako argumenty tylko deskryptor utworzony przez ''epoll_create'', tablicę zdarzeń do wypełnienia i czas oczekiwania (-1 = nieskończoność). \\ - ''epoll_wait'' nie przekazuje numerów gotowych deskryptorów – przekazuje tylko rodzaj zdarzenia i powiązane z nim wcześniej dane. Plik nagłówkowy: ''#include <sys/epoll.h>'' <html> <pre class="code cpp" style="user-select: none; line-height: 100%"> <span title="Pomocnicze zmienne, struktury i funkcje" style="opacity: 0.75; background-color:#0001"><span class="kw4">struct</span> Client <span class="br0">{</span> <span class="kw4">int</span> fd<span class="sy4">;</span> <span class="kw4">char</span> <span class="sy2">*</span>partialDataReceived, <span class="sy2">*</span>dataQueuedToSend<span class="sy4">;</span> <span class="br0">}</span> <span class="sy2">*</span>clients<span class="sy4">;</span> <span class="kw4">void</span> receiveFromCli<span class="br0">(</span>Client <span class="sy2">*</span>c<span class="br0">)</span><span class="sy4">;</span> <span class="kw4">void</span> sendQueuedData<span class="br0">(</span>Client <span class="sy2">*</span>c<span class="br0">)</span><span class="sy4">;</span></span> ... <span title="'epoll_create1' tworzy nowy plik do monitorowania zdarzeń na innych plikach" style="background-color:#ff02"><span class="kw4">int</span> epollDescr <span class="sy1">=</span> epoll_create1<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span></span> ═╾─ (1) <span title="Struktura epoll_event jest używana: 1) w epoll_ctl jako argument wejściowy, do określenia na jakie zdarzenia czekać i powiązania z nimi danych 2) w epoll_wait jako argument wyjściowy, określa jakie zdarzenia wystąpiły i przekazania powiązanych z nimi danych" style="background-color:#f802">epoll_event</span> ee<span class="br0">[</span><span class="nu0">2</span><span class="br0">]</span><span class="sy4">;</span> ─┐ ee<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span><span title="'.events' określa które zdarzenia mają być raportowane" style="background-color:#0f02">.<span class="me1">events</span> <span class="sy1">=</span></span> EPOLLIN<span class="sy4">;</span> │ ee<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span><span title="Jeśli wskazane zdarzenie wystąpi, to (skopiowana do pamięci systemu operacyjngo w momencie wywołania 'epoll_ctl') wartość unii '.data' zostanie z powrotem wpisana przez 'epoll_wait' do wynikowej struktury epoll_event W tej unii do wybor są pola fd, u32, u64 i ptr, odpowiednio typów int, uint32_t, uint64_t i void*" style="background-color:#f002">.<span class="me1">data</span>.<span class="me1">u32</span> <span class="sy1">=</span></span> <span class="sy2">-</span><span class="nu0">1</span><span class="sy4">;</span> ├─ (2) <span title="'epoll_ctl' zarządza listą zdarzeń które monitoruje wskazany plik epoll: z flagą EPOLL_CTL_ADD dodaje nowy plik, z flagą EPOLL_CTL_DEL przestaje monitorować wskzany plik, z flagą EPOLL_CTL_MOD zmienia listę zdarzeń i powiązane dane dla już monitorowanego pliku" style="background-color:#f0f2">epoll_ctl</span><span class="br0">(</span>epollDescr, <span title="'epoll_ctl' zarządza listą zdarzeń które monitoruje wskazany plik epoll: z flagą EPOLL_CTL_ADD dodaje nowy plik, z flagą EPOLL_CTL_DEL przestaje monitorować wskzany plik, z flagą EPOLL_CTL_MOD zmienia listę zdarzeń i powiązane dane dla już monitorowanego pliku" style="background-color:#f0f2">EPOLL_CTL_ADD</span>, STDIN_FILENO, <span class="sy3">&</span>ee<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span> │ ee<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span>.<span class="me1">data</span>.<span class="me1">u32</span> <span class="sy1">=</span> <span class="sy2">-</span><span class="nu0">2</span><span class="sy4">;</span> │ epoll_ctl<span class="br0">(</span><span title="Wybiera na którym z plików epoll będzie pracować ta funkcja" style="background-color:#0fa2">epollDescr</span>, EPOLL_CTL_ADD, <span title="Określa na którym pliku należy rozpocząć (lub zmienić/zakończyć) śledzenie zdarzeń" style="background-color:#0082">serverSock</span>, <span title="Wskazuje które zdarzenia mają być raportowane i jakie dane programista chce otrzymać, kiedy wskazane zdarzenie wystąpi" style="background-color:#a0f2"><span class="sy3">&</span>ee<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span><span class="br0"></span></span>)<span class="sy4">;</span> ─┘ <span class="kw1">while</span> <span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span> <span class="br0">{</span> <span title="'epoll_wait' zwraca ile pierwszych struktur z podanej listy wypełnił" style="background-color:#f402"><span class="kw4">int</span> ile_gotowych</span> <span class="sy1">=</span> <span title="'epoll_wait' wpisuje do podanej listy struktur epoll_event listę zdarzeń które wystąpiły na wcześniej zgłoszonych do monitorowania plikach (jeśli trzeba, czekając na pierwsze zdarzenie)" style="background-color:#0ff2">epoll_wait</span><span class="br0">(</span><span title="Wybiera na którym z plików epoll będzie pracować ta funkcja" style="background-color:#0fa2">epollDescr</span>, <span title="wskazuje że wyniki mają trafić do tablicy 'ee' w której są 2 elementy" style="background-color:#8f03">ee, <span class="nu0">2</span></span>, <span title="limit czasu oczekiwania (jak w poll)" style="background-color:#0001"><span class="sy2">-</span><span class="nu0">1</span></span><span class="br0">)</span><span class="sy4">;</span> ═╾─ (3) <span class="kw1">for</span> <span class="br0">(</span><span class="kw4">int</span> i <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span> i <span class="sy1"><</span> ile_gotowych<span class="sy4">;</span> <span class="sy2">++</span>i<span class="br0">)</span> <span class="br0">{</span> ─┐ <span class="kw1">if</span> <span class="br0">(</span>ee<span class="br0">[</span>i<span class="br0">]</span>.<span class="me1">data</span>.<span class="me1">u32</span> <span class="sy1">==</span> <span class="sy2">-</span><span class="nu0">1</span><span class="br0">)</span> │ readingFromStdin<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> │ <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">(</span>ee<span class="br0">[</span>i<span class="br0">]</span>.<span class="me1">data</span>.<span class="me1">u32</span> <span class="sy1">==</span> <span class="sy2">-</span><span class="nu0">2</span><span class="br0">)</span> <span class="br0">{</span> │ <span class="kw4">int</span> cliFd <span class="sy1">=</span> accept<span class="br0">(</span>serverSock, <span class="nu0">0</span>, <span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> │ <span class="kw4">int</span> cliId <span class="sy1">=</span> getFreeClientId<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> │ clients<span class="br0">[</span>cliId<span class="br0">]</span> <span class="sy1">=</span> <span class="br0">{</span>.<span class="me1">fd</span> <span class="sy1">=</span> cliFd<span class="br0">}</span><span class="sy4">;</span> │ ee<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span>.<span class="me1">events</span> <span class="sy1">=</span> EPOLLIN<span class="sy4">;</span> ┐ │ ee<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span>.<span class="me1">data</span>.<span class="me1">u32</span> <span class="sy1">=</span> cliId<span class="sy4">;</span> ├(2)├─ (4) epoll_ctl<span class="br0">(</span>epollDescr, EPOLL_CTL_ADD, cliFd, <span class="sy3">&</span>ee<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span> ┘ │ <span class="br0">}</span> <span title="W tym przykładzie programista używa wartości specjalne -2 i -1 dla standardowego wejścia i gniazda serwerowego, a pozostałe wartości pola .data.u32 to indeksy w tablicy klientów, co ułatwia odnalezienie danych powiązanych z klientem" style="background-color:#0ff4"><span class="kw1">else</span></span> <span class="br0">{</span> │ <span class="kw1">if</span> <span class="br0">(</span>ee<span class="br0">[</span>i<span class="br0">]</span>.<span class="me1">events</span> <span class="sy3">&</span> EPOLLIN<span class="br0">)</span> │ receiveFromCli<span class="br0">(</span><span class="sy3">&</span>clients<span class="br0">[</span>ee<span class="br0">[</span>i<span class="br0">]</span>.<span class="me1">data</span>.<span class="me1">u32</span><span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span> │ <span class="kw1">if</span> <span class="br0">(</span>ee<span class="br0">[</span>i<span class="br0">]</span>.<span class="me1">events</span> <span class="sy3">&</span> EPOLLOUT<span class="br0">)</span> │ sendQueuedData<span class="br0">(</span><span class="sy3">&</span>clients<span class="br0">[</span>ee<span class="br0">[</span>i<span class="br0">]</span>.<span class="me1">data</span>.<span class="me1">u32</span><span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span> │ <span class="br0">}</span> │ <span class="br0">}</span> ─┘ <span class="br0">}</span> </pre> </html> ++++Przykład użycia pola ''.data.ptr''| <html> <pre class="code cpp" style="user-select: none; line-height: 100%"> <span style="background-color:#0001" title="Każdy wskaźnik umieszczany w danych powiązanych ze zdarzeniem w epoll będzie obiektem z klasy która dziedziczy po Handler, więc będzie mieć metodę 'handle'"><span class="kw4">struct</span> Handler <span class="br0">{</span> <span class="kw2">virtual</span> <span class="kw4">void</span> handle<span class="br0">(</span>epoll_event <span class="sy3">&</span>ee<span class="br0">)</span> <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span> <span style="opacity:0.66"><span class="kw2">virtual</span> ~Handler<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span></span> <span class="br0">}</span><span class="sy4">;</span></span> <span style="opacity:0.66"><span class="kw4">struct</span> Client<span class="sy4">;</span> std<span class="sy4">::</span><span class="me2">set</span><span class="sy1"><</span>Client <span class="sy2">*</span><span class="sy1">></span> clients<span class="sy4">;</span></span> <span style="background-color:#8001" title="Klasa Client ma obsługiwać jednego klienta – czytać z jego gniazda i pisać do niego (w tym przechowywać koleję danych do wysłania i początki częściowo dostarczoncyh wiadomości), może też od razu zawierać logikę obsługi klienta"><span class="kw4">struct</span> Client</span> <span class="sy4">:</span> <span class="kw2">public</span> Handler <span class="br0">{</span> <span style="opacity:0.66"><span class="kw4">char</span> <span class="sy2">*</span>partialDataReceived, <span class="sy2">*</span>dataQueuedToSend<span class="sy4">;</span> <span class="kw4">int</span> epollDescr, fd<span class="sy4">;</span></span> <span style="background-color:#0081" title="W konstruktorze klasa Client automatycznie dodaje obsługiwane przez siebie gniazdo do monitorowania"> Client<span class="br0">(</span><span class="kw4">int</span> epollDescr, <span class="kw4">int</span> fd<span class="br0">)</span> <span class="sy4">:</span> epollDescr<span class="br0">(</span>epollDescr<span class="br0">)</span>, fd<span class="br0">(</span>fd<span class="br0">)</span> <span class="br0">{</span> epoll_event ee<span class="br0">{</span>.<span class="me1">events</span> <span class="sy1">=</span> EPOLLIN, .<span class="me1">data</span> <span class="sy1">=</span> <span class="br0">{</span>.<span class="me1">ptr</span> <span class="sy1">=</span> <span class="kw3">this</span><span class="br0">}</span><span class="br0">}</span><span class="sy4">;</span> epoll_ctl<span class="br0">(</span>epollDescr, EPOLL_CTL_ADD, fd, <span class="sy3">&</span>ee<span class="br0">)</span><span class="sy4">;</span> <span style="opacity:0.66">clients.<span class="me1">insert</span><span class="br0">(</span><span class="kw3">this</span><span class="br0">)</span><span class="sy4">;</span></span> <span class="br0">}</span></span> <span style="background-color:#0801" title="W destruktorze klasa Client zamyka gniazdo, co automatycznie usuwa gniazdo ze wszystkich epolli"> ~Client<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> close<span class="br0">(</span>fd<span class="br0">)</span><span class="sy4">;</span> ... <span class="br0">}</span></span> <span style="opacity:0.66"><span class="kw4">void</span> receive<span class="br0">(</span>epoll_event <span class="sy3">&</span>ee<span class="br0">)</span><span class="sy4">;</span> <span class="kw4">void</span> sendQueuedData<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span></span> <span class="kw4">void</span> handle<span class="br0">(</span>epoll_event <span class="sy3">&</span>ee<span class="br0">)</span> <span class="br0">{</span> <span style="background-color:#0001" title="Zwróć uwagę że poza odbieraniem danych funkcja 'receive' musi też obsłużyć błędy na gnieździe"> <span class="kw1">if</span> <span class="br0">(</span>ee.<span class="me1">events</span> <span class="sy3">&</span> EPOLLIN<span class="br0">)</span> receive<span class="br0">(</span>ee<span class="br0">)</span><span class="sy4">;</span></span> <span class="kw1">if</span> <span class="br0">(</span>ee.<span class="me1">events</span> <span class="sy3">&</span> EPOLLOUT<span class="br0">)</span> sendQueuedData<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span> ... <span class="br0">}</span><span class="sy4">;</span> <span class="kw4">struct</span> Server <span class="sy4">:</span> <span class="kw2">public</span> Handler <span class="br0">{</span> <span style="opacity:0.66"><span class="kw4">int</span> epollDescr, serverSock<span class="sy4">;</span></span> <span style="background-color:#0081" title="W konstruktorze klasa Serwer tworzy nowe nasłuchujące gniazdo i dodaje je do monitorowania we wskazanym epollu"> Server<span class="br0">(</span><span class="kw4">int</span> epollDescr, <span class="kw4">uint16_t</span> port<span class="br0">)</span> <span class="sy4">:</span> epollDescr<span class="br0">(</span>epollDescr<span class="br0">)</span> <span class="br0">{</span> serverSock <span class="sy1">=</span> ... ... epoll_event ee<span class="br0">{</span>.<span class="me1">events</span> <span class="sy1">=</span> EPOLLIN, .<span class="me1">data</span> <span class="sy1">=</span> <span class="br0">{</span>.<span class="me1">ptr</span> <span class="sy1">=</span> <span class="kw3">this</span><span class="br0">}</span><span class="br0">}</span><span class="sy4">;</span> epoll_ctl<span class="br0">(</span>epollDescr, EPOLL_CTL_ADD, serverSock, <span class="sy3">&</span>ee<span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span></span> <span style="opacity:0.66">~Server<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span></span> <span style="background-color:#8001" title="W obsłudze zdarzenia – żądanego POLLIN bądź błędu (którego obsługa tu jest pominięta) – przyjmowane jest nowe połączenie i tworzony obiekt z klasy Client, który sam dodaje obsługiwane przez siebie gniazdo do epolla"> <span class="kw4">void</span> handle<span class="br0">(</span>epoll_event <span class="sy3">&</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw4">int</span> cliFd <span class="sy1">=</span> accept<span class="br0">(</span>serverSock, <span class="nu0">0</span>, <span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="kw3">new</span> Client<span class="br0">(</span>epollDescr, cliFd<span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span></span> <span class="br0">}</span><span class="sy4">;</span> <span style="opacity:0.66"><span class="kw4">int</span> main<span class="br0">()</span> <span class="br0">{</span></span> ... <span class="kw4">int</span> epollDescr <span class="sy1">=</span> epoll_create1<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> Server srv<span class="br0">(</span>epollDescr, port<span class="br0">)</span><span class="sy4">;</span> <span class="kw1">while</span> <span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span> <span class="br0">{</span> epoll_event ee<span class="sy4">;</span> epoll_wait<span class="br0">(</span>epollDescr, <span class="sy3">&</span>ee, <span class="nu0">1</span>, <span class="sy2">-</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span style="background-color:#0001" title="Wartość .data.ptr zawsze wskazuje na obiekt z klasy (dziedzicząej po) Handler, z metodą 'handle' do obsługi zdarzenia"><span class="br0">(</span><span class="br0">(</span>Handler <span class="sy2">*</span><span class="br0">)</span>ee.<span class="me1">data</span>.<span class="me1">ptr</span><span class="br0">)</span><span class="sy2">-</span><span class="sy1">></span>handle<span class="br0">(</span>ee<span class="br0">)</span><span class="sy4">;</span></span> <span class="br0">}</span> <span style="opacity:0.66"><span class="br0">}</span></span> </pre> </html> ++++ //Zadanie 4.// Powtórz Zadanie 1, ale tym razem jako jednowątkowy program wykorzystujący mechanizm epoll. //Zadanie 5.// Napisz jednowątkowy serwer czatu używając ''poll'' lub ''epoll_wait''.
sk2/sockets_concurrency.1730754581.txt.gz
· ostatnio zmienione: 2024/11/04 22:09 przez
jkonczak
Narzędzia strony
Pokaż stronę
Poprzednie wersje
Odnośniki
Złóż / rozłóż wszystko
Do góry