Narzędzia użytkownika

Narzędzia witryny


sk2:sockets_concurrency

Różnice

Różnice między wybraną wersją a wersją aktualną.

Odnośnik do tego porównania

Both sides previous revision Poprzednia wersja
Nowa wersja
Poprzednia wersja
sk2:sockets_concurrency [2020/10/27 11:04]
jkonczak [Obsługa wielu strumieni naraz]
sk2:sockets_concurrency [2024/11/12 11:52] (aktualna)
jkonczak
Linia 6: Linia 6:
 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: 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.   * Iteracyjnie – kiedy współbieżność jest zbędna.
-  * Pętla zdarzeń (event loop, [[https://​en.wikipedia.org/​wiki/​Event_loop|[1]]],​ [[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.+  * 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.   * Aplikacja wielowątkowa – każde źródło zdarzeń – np. gniazdo – jest obsługiwane w osobnym wątku.
 +
 ====== Wiele wątków ====== ====== Wiele wątków ======
  
 [[sk2:​cpp11_threads|Wątki w C++]] [[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.#~~ 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 
 +równocześnie ​dane przychodzące ​z sieci będzie wypisywać ​na standardowe wyjście. 
 +\\ 
 +(Możesz skorzystać z {{:​sk2:​tcp_client_template.cpp|przykładowego kodu klienta}})
  
-//Zadanie ​2.// Czat – napisz serwer, który każdą otrzymaną wiadomość przekaże wszystkim połączonym klientom. \\ +~~Zadanie.#~~ Czat – napisz serwer, który każdą otrzymaną wiadomość przekaże 
-(Możesz skorzystać z {{:​sk2:​tcp_server_template.cpp|przykładowego kodu}})+wszystkim połączonym klientom. 
 +\\ 
 +(Możesz skorzystać z {{:​sk2:​tcp_server_template.cpp|przykładowego kodu serwera}})
 ====== Zdarzenia ====== ====== Zdarzenia ======
  
Linia 22: Linia 30:
 System Linux zawiera 3 podstawowe funkcje pozwalające na czekanie na przychodzące zdarzenia na deskryptorach plików: 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)   * ''​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 ilość ​monitorowanych deskryptorów,​ można użyć ponownie struktury opisującej deskryptory etc. (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.+  * ''​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 ​informacje ​które ​zarzenia wystąpiły.
  
 Dłuższe porównanie:​ http://​www.ulduzsoft.com/​2014/​01/​select-poll-epoll-practical-difference-for-system-architects/​ 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 ===== ===== poll =====
-//Zadanie 3.// Powtórz Zadanie 1, ale tym razem jako jednowątkowy program wykorzystujący ''​poll''​. 
  
-Aby stworzyć program korzystający z funkcji ''​poll'',​ należy: +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ć:   - przygotować tablicę struktur ''​pollfd''​ i wypełnić:
     * ''​.fd''​ – deskryptor pliku do monitorowania,​     * ''​.fd''​ – deskryptor pliku do monitorowania,​
     * ''​.events''​ – zbiór monitorowanych zdarzeń:     * ''​.events''​ – zbiór monitorowanych zdarzeń:
       *  ''​POLLIN''​ – funkcja ''​poll''​ ma się przerwać, jeśli można wywołać bez czekania ''​read()/​…''​ lub ''​acccept()''​       *  ''​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()/​…''​ \\ <​html><​small><​/html>​Uwagajeśli spróbujesz zapisać więcej bajtów niż jest miejsca w buforze nadawczym''​write'' ​i tak się zablokuje<html></​small></​div></​li></​ul><​div class="​li"></​html>​ reszta zdarzeń (''​POLLHUP'',​ ''​POLLERR'',​ ''​POLLPRI'',​ …) opisana w ''​man poll''​ <​html></​div><​!--</​html>​+      *  ''​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>​     * <​html>​--><​div class="​li"></​html>​
     * zostawić w spokoju ''​.revents''​ – tam pojawi się informacja o tym co wystąpiło     * zostawić w spokoju ''​.revents''​ – tam pojawi się informacja o tym co wystąpiło
-  - w pętli wywoływać funkcję ''​poll(…)''​. ​Uwaga: ​aby funkcja czekała ​w nieskończoność''​timeout''​ musi być ujemny+  - w pętli wywoływać funkcję ''​poll(…)''​. ​\\ <​small>​ostatni argument funkcji ''​poll''​ to maksymalny czas oczekiwania; ​aby funkcja czekała ​bez limitunależy podać tam dowolną ujemną wartość</​small>​
   - sprawdzać który deskryptor jest gotowy przeglądając pola ''​.revents''​   - sprawdzać który deskryptor jest gotowy przeglądając pola ''​.revents''​
  
-<code cpp> +Funkcja ''​poll'',​ struktura ''​pollfd''​ i stałe ''​POLL…'' ​ są w pliku nagłówkowym ''#​include ​<poll.h>''​
-pollfd nacoczekac[5]{}; ​                 // \. +
-nacoczekac[0].fd=sock1; ​                 //  ​(2) +
-nacoczekac[0].events=POLLIN; ​            // / +
-...+
  
-int gotowe ​= poll(nacoczekac5, -1);    // (3)+<​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">​├─ (1)</​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">​═╾─ (2)</​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">&​amp;</​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">&​amp;</​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">&​lt;</​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">&​amp;</​span>​ POLLIN<​span class="​br0">​)</​span></​span> ​                           <span style="​color:​grey">​├─ ​(3)</​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">&​amp;</​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.#​~~ Powtórz Zadanie 1, ale tym razem jako jednowątkowy program wykorzystujący ''​poll''​. 
 +\\ 
 +(Możesz skorzystać z {{:​sk2:​tcp_client_template.cpp|przykładowego kodu klienta}})
  
-for(pollfd & opis : nacoczekac) {        // \. 
-    if(opis.revents & POLLIN) {          //  | 
-        if(opis.fd == sock1) {           // ​ | 
-            read(opis.fd,​ ...            //   > (4) 
-            ...                          //  | 
-    }}                                   // ​ | 
-}                                        // / 
-</​code>​ 
 ===== epoll ===== ===== epoll =====
  
Linia 65: Linia 88:
   - 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ść). \\   - 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.   - ''​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>''​+Funkcje ''​epoll_…'',​ struktura ''​epoll_event''​ i stałe ''​EPOLL…''​ są w pliku nagłówkowym ​''#​include <​sys/​epoll.h>''​
  
-<code cpp> +<html> 
-int sock socket(... +<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>​
  
-int fd =  epoll_create1(0); ​                             // (1)+...
  
-epoll_event event; ​                                      // \. +  <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)
-  event.events ​EPOLLIN; ​                               / | +
-  event.data.fd ​sock;                                  /  ​> (2) +
-//event.data.u64 ​0xdeadbeef ​                           //  | +
-//​event.data.ptr ​funkcja                             // /+
  
-epoll_ctl(fdEPOLL_CTL_ADD,​ sock, &event);              // \+  <span title="​Struktura epoll_event jest używana: 
-...                                                      //   (2) +1) w epoll_ctl ​jako argument wejściowydo określenia na jakie zdarzenia czekać i powiązania z nimi danych 
-epoll_ctl(fd, EPOLL_CTL_ADD, ​othersock, &event);         ​// /+2w 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ć wskazany 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ć wskazany 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">​&amp;</​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">&​amp;</​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 jest miejsce na co najwyżej 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)
  
-int resultCount ​epoll_wait(fd, &event1, -1);         ​// (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">&​lt;</​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>​epollDescrEPOLL_CTL_ADDcliFd<span class="​sy3">&​amp;</​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">&​amp;</​span>​ EPOLLIN<​span class="​br0">​)</​span> ​                              │ 
 +                  receiveFromCli<​span class="​br0">​(</​span><​span class="​sy3">&​amp;</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">&​amp;</​span>​ EPOLLOUT<​span class="​br0">​)</​span> ​                             │ 
 +                  sendQueuedData<​span class="​br0">​(</​span><​span class="​sy3">&​amp;</​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>​
  
-if( event.events & EPOLLIN && event.data.fd == sock )  ​// \.  +++++Przykład użycia pola ''​.data.ptr''​| 
-    ​read(sock, ...                                       //  > (4+<​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> 
-</code+    ​<span class="​kw2">​virtual<​/span> <span class="​kw4">​void<​/span> handle<​span class="​br0"​>(</​span>​epoll_event <span class="​sy3">&​amp;</​span>​ee<​span class="​br0">​)</​span>​ <span class="​sy1">​=</​span>​ <span class="​nu0">​0</​span><​span class="​sy4">;</​span>​ 
-<html><small></html>Przykład użycia pola ''​.data.ptr''​ +    ​<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>​ 
-<code cpp+<span class="​br0">​}</​span><​span class="​sy4">;</​span></​span>​ 
-std::function<void()> ​readFromSock ​= ... // np'​mySockObject;' czy '​std::​bind(myReadFunc, sock);' + 
-epoll_event ee {}; +<span style="​opacity:​0.66"><​span class="​kw4">​struct</span> Client<​span class="​sy4">;</​span
-ee.events = EPOLLIN; +std<span class="​sy4"​>::</span><span class="​me2">​set</span><span class="​sy1">&​lt;</​span>​Client <span class="​sy2">​*</​span><​span class="​sy1">&​gt;</​span>​ clients<​span class="​sy4">;</​span></​span>​ 
-ee.data.ptr = &​readFromSock+<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>​ 
-epoll_ctl(epollfd, EPOLL_CTL_ADD, ​sock, &ee); +    <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>​ 
-while(epoll_wait(epollfd, &ee, 1, -1)) +<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>​ 
-    (*(std::​function<void()>​*)ee.data.ptr)();​ +        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>​ 
-</code+        ​epoll_ctl<span class="​br0">​(</​span>​epollDescr,​ EPOLL_CTL_ADD,​ fd, <span class="​sy3">&​amp;</​span>​ee<​span class="​br0">​)</​span><​span class="​sy4">;</​span
-<html></small></​html>​+        <​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">&​amp;</​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">&​amp;</​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">&​amp;</​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">&​amp;</​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">​&amp;</​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">&​amp;</​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">​&amp;</​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">&​gt;</​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.#~~ Powtórz Zadanie 1, ale tym razem jako jednowątkowy program wykorzystujący mechanizm epoll. 
 +\\ 
 +(Możesz skorzystać z {{:​sk2:​tcp_client_template.cpp|przykładowego kodu klienta}})
  
-//Zadanie ​5.// Napisz jednowątkowy serwer czatu używając ''​poll''​ lub ''​epoll_wait''​.+~~Zadanie.#~~ Napisz jednowątkowy serwer czatu używając ''​poll''​ lub ''​epoll_wait''​. 
 +\\ 
 +(Możesz skorzystać z {{:​sk2:​tcp_server_template.cpp|przykładowego kodu serwera}})
sk2/sockets_concurrency.1603793079.txt.gz · ostatnio zmienione: 2020/10/27 11:04 przez jkonczak