Kategoria

SFML – kurs podstawowy

Przykład: gra multiplayer w SFML

W tym przykładzie zajmiemy się zrobieniem prostej gry sieciowej dla 2 osób. Naszą grą będzie znana wszystkim gra „Kółko i krzyżyk”, także pod nazwą „Tic Tac Toe”. Ten przykład jest robiony częściowo „na żywo” tzn. kod jest pisany równolegle z pisaniem artykułu, jeżeli chcielibyście zobaczyć lekcje w stylu kursu Unity na YT to napiszcie o tym w komentarzach.

Więcej

Przykład transferu FTP: pobieranie dodatkowych poziomów do gry

Na pierwszy ogień postanowiłem, że Wam pokażę w jaki sposób można napisać system pobierania dodatkowych poziomów do gry, która się opiera np. na systemie mapy kafelkowej.


Ten system będzie działał w analogiczny sposób do aktualizacji naszej gry, ale do rzeczy: załóżmy, że wypuściliśmy grę lecz później postanowiliśmy dodać kilka nowych poziomów (DLC 😛 ), a użytkownicy jak wiadomo są leniwi i nie lubią gdy muszą pobierać pliki w sposób manulany i później muszą wklejać kod do gry bo boją się, że coś źle albo zrobią.

Z pomocą im przychodzi system, który przezornie wrzuciliśmy do gry, czyli menu „Dodatkowe poziomy” gdzie poprzez 1 kliknięcie użytkownik pobiera poziom. Takie jest założenie.

Na początku musimy posiadać jakiś serwer ftp, ja do tych testów skorzystam z cba.pl, rejestrujemy domenę i w sekcji konta FTP mamy login do naszego konta FTP, a jeżeli chcemy dostać się do naszych plików od strony web po prostu wchodzimy na adres naszej domeny w przeglądarce i klikamy Menadżer plików.

Na CBA mamy 1GB wolnego miejsca, lecz pojedynczy plik nie może mieć więcej niż 10mb, na nasze możliwości w zupełności nam wystarczy.

Oczywiście poniżej nie wrzucam niczego odkrywczego, jest to raczej prosty kod w konsoli bo transfer FTP jest banalny, jeżeli chcesz możesz pobrać plik exe, który przygotowałem poniżej aby przetestować pobieranie samych plików (są to poziomy z mojej gry SQUARE), a poniżej jest sam kod bez danych do konta poniżej (lub w wersji na GitHub):

To tyle jeżeli chodzi o ten artykuł, przy przykładzie gry dla 2 osób będzie nieco więcej SFML’a samego w sobie.

Plik „.exe

Transfer plików (FTP)

Dla niewtajemniczonych


Jeżeli wiesz czym jest FTP to przejdź do następnego paragrafu.

FTP (file transport protocol) jest to protokół, który pozwala nam manipulować plikami w sposób zdalny. Ten protokół jest sam w sobie prosty, posiada kilka komend typu: utwórz folder, usuń plik, pobierz plik, itp. Aby one działały to komputer do którego wysyłasz te komendy musi mieć uruchomiony serwer FTP, aby mógł „zrozumieć” i wykonać nasze komendy.

Podsumowując: do czego może nam się przydać FTP? Otóż dzięki temu możemy np. zrobić taki feature w naszej grze jak pobieranie map z sieci i dzięki FTP będziemy mogli to osiągnąć. Moglibyśmy także zapisywać zapisywać grę gracza nie lokalnie na dysku lecz w „chmurze”. Dzięki temu mógłbyś też zrobić automatyczne aktualizacje swojej gry.

Jeżeli chcesz dowiedzieć się więcej o FTP polecam inne źródła, chociażby Wikipedie.

 

Klasa klienta FTP


Klasa, która służy do obsługiwania FTP w SFML to jak łatwo można się domyślić sf::Ftp. Jest to klient, czyli możemy łączyć się z serwerem FTP, wysyłać mu komendy, wysyłać/pobierać pliki.

Każda metoda w tej klasie zawiera komendę FTP oraz zwraca standardową odpowiedź FTP. Każda odpowiedź zawiera kod statusu (podobne do HTTP, ale nie takie same) oraz wiadomość informującą nas co się dokładnie stało. Odpowiedzi FTP są zawarte w klasie  sf::Ftp::Response.

Status można użyć do sprawdzenia czy komenda zakończyła się pomyślnie. Jeżeli wartość statusu jest mniejsza niż 400 to zakończyło się powodzeniem, w przeciwnym razie mamy do czynienia z błędem.

Możesz też użyć metody isOk(), która mówi nam tylko czy zakończono błędem czy pomyślnie.

Jeżeli nie dbasz o szczegóły można to zrobić nawet jeszcze krócej:

Dla lepszej czytelności te sprawdzenia poprawności wykonania komend nie będą pisane w dalszej części tego poradnika, ale ty powinieneś je pisać w swoim kodzie.

 

Łączenie z serwerem FTP


Pierwszą rzeczą jaką musimy zrobić to połączenie się z serwerem:

Jako adres podajemy jakikolwiek poprawny sf::IpAdress: ip, URL, nazwę sieci…

Standardowym portem dla FTP jest 21. Jednakże gdyby twój serwer korzystał z innego portu możesz dodać go jako drugi argument.

Jako trzeci opcjonalny argument możesz podać timeout, czyli czas jaki będzie czekał twój program na nawiązanie połączenia z serwerem.

Gdy już udało ci się połączyć to następnym krokiem jest autoryzacja dostępu do serwera:

 

Komendy FTP


Tutaj znajdziesz wszystkie dostępne komendy w klasie sf::Ftp i ich krótki opis. Pamiętaj, że wszystkie komendy działają względnie do obecnej ścieżki, tzn jeżeli uruchomisz komendę będąc wcześniej w home/file, to komenda zadziała w folderze file, a nie home. Działa to dokładnie tak samo jak z komendami w konsoli na twoim OS.

Pobranie ścieżki na której obecnie pracujemy

sf::Ftp::DirectoryResponse to wyspecjalizowana klasa sf::Ftp::Response, która zawiera żądany katalog.

Pobieranie listy katalogów i plików w nich się zawierających

sf::Ftp::ListingResponse to klasa dziedziczona po sf::Ftp::Response posiadająca żądania do katalogów/plików.

Zmiana katalogu

„W górę”

czyli katalog, z którego weszliśmy do katalogu w którym jesteśmy teraz.

Tworzenie nowego katalogu

Usunięcie istniejącego folderu

Zmiana nazwy

Usunięcie istniejącego pliku

Pobranie pliku

Ostatni argument to sposób transferu pliku, istnieje ich kilka: Ascii (do plików tekstowych), Ebcdic (dla plików tekstowych używających zestawu znaków EBCDIC), Binary (dla plików nie-tekstowych). Ascii i Ebcdic mogą przekształcać tekst plik podczas jego transportowania, tak aby dopasować go do środowiska klienta. Binary bezpośrednio kopiuje plik bajt po bajcie.

Wysyłanie plików (na serwer)

Przedłużanie połączenia

Serwery FTP zazwyczaj kończą połączenie gdy nie jesteśmy przez jakiś czas aktywni (nie wysyłamy tam komend). Jeżeli chcesz uniknąć rozłączenia możesz użyć komendy.

 

Rozłączanie z serwerem FTP


Możesz się rozłączyć z serwerem w każdej chwili za pomocą metody:

 

Oryginalny artykuł

SFML i HTTP

Wstęp


Taka uwaga czysto ode mnie: moja znajomość HTTP jest dość mała i ostrzegam, że mogą się zdarzyć błędy w przetłumaczonych nazwach itp. Jeżeli takie się zdarzą dajcie znać w komentarzach.

SFML posiada prostą klasę klienta HTTP, dzięki której możesz się komunikować z serwerami web. Tak jak wspomniałem prostą komunikację, czyli możesz korzystać z: HTTP: POST, GET i żądań typów HEAD, zdobywanie dostępu do nagłówków pól HEAD oraz czytania/pisania stron.

Jeżeli to dla ciebie za mało i potrzebujesz np dostępu do HTTPS to odsyłam cię do bibliotek stworzonych właśnie w tym celu jak libcurl czy cpp-netlib.

Jednak do podstawowej komunikacji pomiędzy programem a serwerem web funkcje zawarte w SFML powinny nam wystarczyć.

 

sf::Http


Do komunikowania z serwerami HTTP używamy klasy sf::Http.

Zauważ, że nie dokonujemy tutaj żadnego połączenia, a jedynie ustawiamy adres z którym będziemy się łączyć. Nasz program łączy się jedynie tymczasowo przy każdej wykonywanej czynności.

Tutaj jest w zasadzie tylko jedna metoda wysyłająca żądania do strony i to jest w zasadzie wszystko co ta klasa robi.

 

Żądania (requests)


Żądania HTTP reprezentowane są przez klasę sf::Http::Request. Klasa ta może zawierać poniższe informacje:

  • metoda: POST (wysyła jakieś dane), GET (pobieranie danych), HEAD (pobranie nagłówka źródłowego bez  sekcji body)
  • URI: adres jakiegoś zasobu (strona, obraz, …) do pobrania/wysłania, adres względem hosta
  • HTTP version (standardowo 1.0, jednak możesz użyć innej aby uzyskać dostęp do innych elementów tej wersji)
  • header: ustawia pole wraz z kluczem i wartością
  • body strony (używane jedynie przy metodzie POST)

SFML automatycznie wypełnia pola obowiązkowe nagłówka, takie jak „host”, „Content-Length”, itp .Możesz wysyłać żądania (requesty) bez obaw, bo SFML dołoży starań aby były poprawne 😉 .

 

Odpowiedzi (responses)


Jeżeli udało nam się pomyślnie połączyć i wysłać żądanie do hosta to odpowiedź ze strony zostanie odebrana przez klasę sf::Http::Response, która posiada następujące elementy:

  • status, który w precyzyjny sposób pokazuje jak serwer zareagował na nasze żądanie (ok, redirected, not found, etc.)
  • wersję http
  • nagłówek: z kluczem i wartością
  • „body of response”

 

Przykład: wysyłanie wyniku do serwera online


Poniżej możesz zobaczyć w jaki sposób można wysłać zdobyty wynik przez gracza do serwera.

Oczywiście pokazany tutaj sposób jest bardzo prosty, nie posiada absolutnie żadnych zabezpieczeń i każdy może wysłać punkty. Zabezpieczeniem mógłby być np dodatkowy parametr w postaci hash kodu, który upewniałby nas że kod wysłano przez program. Jednak to wychodzi poza ten tutorial.

Na koniec jak mogłaby wyglądać strona PHP odbierająca nasze punkty:

Oryginalny artykuł

Pakiety sieciowe

Problemy, które należy rozwiązać


Wymiana danych w sieci potrafi być bardziej skomplikowana niż wydaje się z z początku. Problemem jest zróżnicowanie urządzeń, które wymieniają pomiędzy sobą dane: różne systemy operacyjne, procesory powodują kilka problemów gdy chcemy w rzetelny sposób wymieniać pomiędzy nimi dane.

Pierwszym problemem jest kolejność bajtów (endianness). Jest to porządek wg którego procesor interpretuje prymitywne typy, które używają więcej niż 1 bajt(liczby całkowite i zmiennoprzecinkowe). Istnieją 2 grupy: „big-endian” gdzie bardziej znaczące bajty są przechowywane na początku oraz „little-endian” gdzie bardziej znaczące bajty są przechowywane na końcu.
Problem tutaj jest raczej dość oczywisty: jeżeli wysyłam dane pomiędzy dwoma komputerami, których endiany do siebie nie pasują to dane zostaną błędnie zinterpretowane. Np. 16-bitowa liczba ’42’ w „big-endian” wygląda tak: 00000000 00101010, „little-endian” zinterpretuje to jako liczbę ‚10752’.

Drugim problemem jest wielkość typów prymitywnych (char, short, int, long, float, double) w C++, które nie są ustawione odgórnie i ich rozmiar zależy od procesora. Np. long int może być liczbą 32-bitową na jednych procesorach, a na innych może być 64-bitową.

Trzecim problemem jest sposób działania TCP. Z racji, że dane w TCP mogą być dzielone na części to odbiorca musi odebrane dane jeszcze zrekonstruować przed zinterpretowaniem ich, w przeciwnym razie mogą powstać błędy bo nie odebrano np. całej zmiennej.

Oczywiście pewnie spotkasz się także z innymi problemami, jednak te są najbardziej podstawowe. SFML posiada narzędzia do uniknięcia tych problemów i ich omówieniem właśnie się zajmiemy.

 

Typy o stałych rozmiarach


Ponieważ typy prymitywne mają różne rozmiary na różnych komputerach to rozwiązanie tego problemu jest proste: nie należy ich używać. SFML posiada odpowiedniki tych zmiennych, które są o stałym rozmiarze: sf::Int8, sf::Uint16, sf::Int32, itp. A więc powinieneś ich używać aby w bezpieczny sposób przesyłać dane.

SFML posiada odpowiedniki do liczb całkowitych. W zasadzie w SFML powinny się też znaleźć dla liczb zmiennoprzecinkowych, ale w praktyce nie są potrzebne (przynajmniej na platformach na których działa SFML). Typy float i double posiadają zawsze ten sam rozmiar (odpowiednio 32 i 64 bity).

 

Pakiety


Dwa kolejne problemy dotyczyły endiany i dzielenia danych w TCP. Może je rozwiązać za pomocą klasy sf::Packet. Jako bonus zapewnia ona o wiele przyjemniejszy interfejs niż stare dobre tablice bajtów.

Użycie pakietów jest analogiczne do innych standardowych strumieni. Możesz „włożyć dane za pomocą operatora <<, oraz „wyjąć” za pomocą >>.

W przeciwieństwie do zapisywania danych, odczytywanie ich z pakietów może zakończyć się niepowodzeniem jeżeli próbujemy wydobyć więcej bajtów niż pakiet ich zawiera. Jeżeli nie uda się odczytać danych to status pakietów jest przestawiany na błąd. Aby sprawdzić status możesz użyć if‚a tak jakbyś użył normalny typ bool (tak samo jak w standardowych strumieniach danych).

Wysyłanie i odbieranie danych jest bardzo proste, socket’y posiadają przeładowaną funkcję send oraz receive dla klasy sf::Packet.

Pakiety rozwiązują także problemy z dzieleniem danych na mniejsze części, ponieważ gdy otrzymasz pakiet to otrzymasz go w całości, dokładnie taki sam jaki został wysłany.

 

Wykorzystanie pakietów we własnych klasach


Pakiety posiadają przeładowane operatory dla wszystkich standardowych typów, jednak co jeżeli chcę wysłać własną klasę? W tym wypadku wystarczy przeładować operatory wewnątrz swojej klasy.

Teraz już możesz wysłać/odebrać swoją klasę Character za pomocą pakietów.

 

Niestandardowe pakiety


Pakiety posiadają kilka fajnych cech dla surowych danych, jednak może się zdarzyć że będziesz chciał dodać kilka własnych (np. kompresję/dekompresję danych przy ich wysyłaniu/odbieraniu). Oczywiście możesz to zrobić przez dziedziczenie po klasie sf::Packet oraz po napisaniu metod:

  • onSend: wywoływane przed wysłaniem danych przez socket
  • onReceive: wywoływane po odebraniu danych przez socket

Ten funkcje dają ci bezpośredni dostęp do danych, które możesz przekształcać w dowolny sposób wg własnych potrzeb.

Poniżej masz przykład automatycznej kompresji i dekompresji danych w pakietach (oczywiście przykład jest niedziałający, jest to bardziej pseudo-kod).

Teraz możemy użyć naszej klasy dokładnie w ten sam sposób jak każdy inny sf::Packet z tą różnicą, że automatycznie odbędzie się kompresja i dekompresja danych.

Oryginalny artykuł

Komunikacja z socket’ami

Gniazda (sockets)


Socket’y to bramy internetowe służące do komunikacji z np. innymi aplikacjami przez internet. Socket’y mogą odbierać i wysyłać dane. Istnieje kilka rodzajów socket’ów, najczęściej używane to UDP oraz TCP.

 

TCP vs UDP


Ważną rzeczą jest wybór socketu odpowiadającego twoim wymaganiom.

Podstawową różnicą pomiędzy socketami w przypadku TCP jest to, że gdy jesteś podłączony z jakimś socketem to nie możesz nawiązać połączenia z kolejnym. Połączenie tutaj odbywa się na koncepcji klient-serwer, najpierw klient łączy się z serwerem i dopiero wtedy może wymieniać z nim dane. Jeżeli serwer chce się połączyć z klientem to nasłuchuje połączenia.

UDP może odbierać/wysyłać dane poprzez połączenie z wieloma komputerami za pomocą jednego socketa.

Drugą różnicą jest to że TCP jest bardziej niezawodne niż UDP, tzn. w przypadku TCP masz pewność, że dane zawsze zostaną dostarczone w poprawny sposób (nieuszkodzone i w właściwej kolejności). W przypadku UDP dane są mniejszą ilość razy sprawdzane, więc możesz otrzymać te same informacje np. kilka razy, w innej kolejności lub zostaną gdzieś zagubione, ale za to dużo szybciej. Co ważne te dane też są zawsze poprawne (nieuszkodzone). UDP może brzmieć strasznie i mało praktycznie, ale w większości przypadków wszystkie dane są odbierane prawidłowo i we właściwej kolejności.

Ostatnią różnicą jest to w jaki sposób są dane transportowane. TCP jest strumieniowym protokołem komunikacyjnym, co oznacza że jeżeli prześlesz wiadomość „Hello”, a następnie „SFML” to klient może otrzymać: „HelloSFML”, „Hel” + „loSFML”, lub nawet „He” + „loS” + „FML”. Czyli te dane możesz otrzymać za jednym razem, w 2 częściach lub 3, itp.

UDP to protokół pakietów, a więc dane nie mogą mieć mieszane. Czyli to co wyślesz zostanie otrzymane dokładnie w takiej samej formie jak zostało wysłane.

Ostatnia rzecz o której częściowo wspomniałem, ponieważ UDP nie jest podłączone, to zezwala nam na wysyłanie wiadomości do wielu odbiorców, a nawet do sieci. TCP z racji, że jest połączone tylko z jednym klientem (komunikacja one-to-one) nie może tego robić.

 

Połączenie TCP


Jak można się domyślić ta część jest specyficzna dla socketów TCP. Istnieją tutaj 2 strony połączenia: pierwsza, która czeka na połączenie (serwer) oraz druga która je nawiązuje (klient).

Jeżeli chodzi o klienta to sprawa jest prosta, użytkownik potrzebuje jedynie klasy sf::TcpSocket oraz metody connect.

Aby sprawdzić swoje IP możesz użyć klasy sf::IpAdress, która jest także pierwszym argumentem funkcji connect. Jako IP możemy podać adres URL, albo właśnie normalny adres IP. Drugim argumentem jest port do którego mamy się podłączyć. Połączenie uda się tylko wtedy gdy na tym samym porcie czeka na nas serwer.

Jest także trzeci opcjonalny argument, który określa ile czasu ma czekać klient na nawiązanie połączenia.

Po stronie serwera jest więcej rzeczy do zrobienia. Potrzebne jest do tego wiele socketów: jeden do nasłuchiwania połączenia oraz po jednym dla każdego klienta.

Do nasłuchiwania połączeń używa się klasy sf::TcpListener, jej jedynym zadaniem jest nasłuchiwanie połączenia na określonym porcie. Nie może wysyłać i odbierać danych.

 

Przypisanie portu UDP


W UDP jest nieco prościej bo nie trzeba tworzyć klienta i serwera, nasłuchiwać połączeń itd. Należy stworzyć sobie gniazdo sf::UdpSocket, w przypadku odbierania należy za pomocą funkcji bind określić na jakim porcie chcemy odbierać dane.

Po przypisaniu socketu do portu wszystko jest już gotowe do obierania danych. Jeżeli chcesz aby twój OS przypisał port automatycznie możesz użyć sf::Socket::AnyPort oraz sprawdzić otrzymany port za pomocą socket.getLocalPort().

Gniazda UDP nie potrzebują nic więcej do rozpoczęcia wysyłania danych.

 

Wysyłanie i odbieranie danych


Wysyłanie i odbieranie danych wygląda tak samo dla obu typu gniazd. Jedyną różnicą jest to że UDP posiada 2 dodatkowe argumenty: adres oraz port nadawcy/odbiorcy. Istnieją dwie funkcje dla każdej operacji: nisko poziomowe, które wysyłają/obierają surowe tablice danych oraz wysoko poziomowe, które korzystają z sf::Packet, zobacz tutorial o pakietach aby dowiedzieć się więcej na ten temat. Tutaj zajmiemy się nisko poziomowymi metodami.

Aby wysłać dane musisz wywołać metodę send z wskaźnikiem na wysyłane dane oraz ilością bajtów do wysłania.

Jako argument danych jest użyty wskaźnik void*, co oznacza że jako dane możesz wysłać cokolwiek, jednakże uważa się za zły pomysł wysyłania danych, które nie są tablicami bajtów, ponieważ miejscowe typy większe niż 1 bajt nie muszą zajmować tyle samo na innych urządzeniach. Takie typy jak int czy long mogą mieć inny rozmiar, nie mogą być także wysyłane pomiędzy różnymi systemami. Ten problem został rozwiązany w poradniku o pakietach.

Za pomocą UDP możesz wyemitować specjalną wiadomość do całej podsieci za pomocą specjalnego adresu: sf::IpAdress::Broadcast.

Jest jeszcze jedna rzecz, którą powinieneś wiedzieć o UDP. Ponieważ dane są wysyłane w datagram’ach oraz mają one swój limit, którego nie możesz przekroczyć. Każde wywołanie send musi wysłać mniej bajtów niż sf::UdpSocket::MaxDatagramSize, czyli nieco mniej niż 2^16 (65536) bajtów.

Aby odbierać dane musisz użyć metody receive:

Warto zapamiętać, że jeżeli socket jest w trybie blokowania to receive będzie czekało aż jakieś dane zostaną odebrane.

Pierwsze dwa argumenty to bufor do którego są kopiowane dane oraz maksymalna wielkość przyjmowanych danych. Ostatni argument to zmienna, który sprawdza ile danych zostało przyjętych.

W przypadku UDP 2 ostatnie argumenty to IP oraz port nadawcy. Mogą być wykorzystane później, np aby mu odpowiedzieć.

Te funkcje są nisko poziomowe i powinieneś ich używać jedynie wtedy gdy masz dobry powód do tego. Lepszym rozwiązaniem jest użycie pakietów.

 

Blokowanie grupy socket’ów


Blokowanie pojedynczego gniazda może szybko stać się irytujące, ponieważ zazwyczaj musisz obsłużyć wielu klientów. I nie chcesz używać gniazda A dopóki gniazdo B przyjmuje jakieś dane. To czego chcesz to blokowanie wielu socketów naraz, czyli czekanie aż któryś z nich otrzyma jakieś dane. Może być to osiągnięte za pomocą klasy sf::SocketSelector.

Selektor może obserwować wszystkie typy gniazd: sf::TcpSocket, sf::UdpSocket sf::TcpListener. Aby dodać gniazdo do selektora użyj metody add.

Selektor nie jest kontenerem gniazd. Jeżeli podasz jedynie wskaźniki na sockety to on ich nie przechowam, a więc nie możesz pobierać lub liczyć socketów, które włożyłeś do środka, aby to robić musisz utworzyć własny kontener na sockety (np std::Vector lub std::list).

Gdy już zapełniłeś selektor gniazdami, które chcesz obserwować musisz wezwać metodę wait dopóki czegoś nie odbierzesz (lub dostaniesz błąd). Możesz także ustawić opcjonalny czas oczekiwania, a więc jeżeli zostanie zwrócony błąd jeżeli nie zostaną odebranie żadne dane w określonym czasie. Dzięki temu będziesz mógł uniknąć czekania.

Jeżeli wait zwraca true to oznacza, że przynajmniej w jednym gnieździe odebrano jakieś dane oraz możesz bez obawy uruchomić receive na tym sockecie, który przestanie być blokowany. Jeżeli tym socketem jest sf::TcpListener to oznacza, że socket jest gotowy do połączenia i możesz użyć metody accept.

Ponieważ selektor nie jest kontenerem socketów to nie może podać ci wskaźnika na socket, który jest gotowy, dlatego też musisz sprawdzić wszystkie gniazda i sprawdzić, który z nich jest gotowy do użycia.

Polecam spojrzeć do dokumentacji sf::SocketSelektor aby zobaczyć jak należy odbierać i wysyłać dane od/do wielu klientów.

Jako bonus możliwość czekania Selector::wait, która umożliwia implementację funkcji do odbierania danych z użyciem timeout, która nie jest dostępna bezpośrednio w klasie.

 

Nie-blokowane sockety


Wszystkie sockety są standardowo blokowano jednak możesz użyć setBlocking do zmiany ich zachowania.

Gdy gniazdo ustawione jest na non-blocking to jego funkcje zawsze zwracają się bezzwłocznie. Na przykład receive zwróci sf::Socket::NotReady jeżeli nie ma dostępnych danych. Lub accept zwróci ten sam status jeżeli nie ma żadnego połączenia.

Takie ustawienie socketów jest najprostszym rozwiązaniem w przypadku gdy ich używamy w głównej pętli co określony czas. Dzięki temu nie blokujemy pętli, ponieważ sprawdzamy czy socket jest gotowy do działania przy każdym obiegu pętli.

Oryginalny artykuł