Protokół WebSockets a HTTP


330

Istnieje wiele blogów i dyskusji na temat websocket i HTTP, a wielu programistów i witryn zdecydowanie opowiada się za websocket, ale nadal nie rozumiem, dlaczego.

na przykład (argumenty miłośników websocket):

HTML5 Web Sockets reprezentuje kolejną ewolucję komunikacji internetowej - dwukierunkowy dwukierunkowy kanał komunikacyjny, który działa poprzez pojedyncze gniazdo w sieci. ( http://www.websocket.org/quantum.html )

HTTP obsługuje przesyłanie strumieniowe: przesyłaj treściowe żądanie (używasz go podczas przesyłania dużych plików) i przesyłaj treściowe odpowiedzi.

Podczas nawiązywania połączenia z WebSocket klient i serwer wymieniają dane na ramkę, która ma po 2 bajty, w porównaniu z 8 kilobajtami nagłówka http podczas ciągłego odpytywania.

Dlaczego te 2 bajty nie zawierają narzutów protokołów tcp i under tcp?

GET /about.html HTTP/1.1
Host: example.org

To jest ~ 48 bajtów nagłówka http.

kodowanie fragmentów http - https://en.wikipedia.org/wiki/Chunked_transfer_encoding :

23
This is the data in the first chunk
1A
and this is the second one
3
con
8
sequence
0
  • Narzut na każdy kawałek nie jest duży.

Oba protokoły działają również przez TCP, więc wszystkie problemy z połączeniami długotrwałymi nadal występują.

Pytania:

  1. Dlaczego protokół WebSockets jest lepszy?
  2. Dlaczego został wdrożony zamiast aktualizacji protokołu HTTP?

2
Jakie jest Twoje pytanie?
Jonas

@Jonas, 1) Dlaczego protokół WebSockets jest lepszy? 2) Dlaczego został wdrożony zamiast aktualizacji protokołu HTTP? 3) Dlaczego websockets są tak promowane?
4esn0k

@JoachimPileborg, możesz to zrobić również za pomocą gniazd TCP lub http dla aplikacji komputerowych; i musisz użyć WebRTC, aby nawiązać komunikację między przeglądarką a witryną
4esn0k

@JachachimPileborg, to webRTC dla przeglądarki od przeglądarki, a nie websockets
4esn0k

@ 4esn0k, WS nie jest lepszy, są one różne i lepsze dla niektórych konkretnych zadań. 3) To nowa funkcja, o której ludzie powinni wiedzieć i otwierać nowe możliwości w Internecie
Jonas

Odpowiedzi:


490

1) Dlaczego protokół WebSockets jest lepszy?

WebSockets jest lepszy w sytuacjach, w których występuje komunikacja o niskim opóźnieniu, szczególnie w przypadku małych opóźnień komunikatów między klientem a serwerem. W przypadku danych serwer-klient można uzyskać dość małe opóźnienie, korzystając z długotrwałych połączeń i transferu porcji. Nie pomaga to jednak w opóźnieniu między klientem a serwerem, co wymaga ustanowienia nowego połączenia dla każdego komunikatu klient-serwer.

Twój 48-bajtowy uścisk dłoni HTTP nie jest realistyczny w rzeczywistych połączeniach przeglądarki HTTP, w których często jest kilka kilobajtów danych wysyłanych jako część żądania (w obu kierunkach), w tym wiele nagłówków i danych cookie. Oto przykład żądania / odpowiedzi na korzystanie z Chrome:

Przykładowe żądanie (2800 bajtów, w tym dane cookie, 490 bajtów bez danych cookie):

GET / HTTP/1.1
Host: www.cnn.com
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.68 Safari/537.17
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: [[[2428 byte of cookie data]]]

Przykładowa odpowiedź (355 bajtów):

HTTP/1.1 200 OK
Server: nginx
Date: Wed, 13 Feb 2013 18:56:27 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: CG=US:TX:Arlington; path=/
Last-Modified: Wed, 13 Feb 2013 18:55:22 GMT
Vary: Accept-Encoding
Cache-Control: max-age=60, private
Expires: Wed, 13 Feb 2013 18:56:54 GMT
Content-Encoding: gzip

Zarówno HTTP, jak i WebSockets mają uzgadniane początkowe połączenia o równoważnych rozmiarach, ale w przypadku połączenia WebSocket uzgadnianie początkowe jest wykonywane raz, a następnie małe wiadomości mają tylko 6 bajtów narzutu (2 dla nagłówka i 4 dla wartości maski). Opóźnienie związane z opóźnieniami nie tyle zależy od wielkości nagłówków, ile od logiki parsowania / obsługi / przechowywania tych nagłówków. Ponadto opóźnienie konfiguracji połączenia TCP jest prawdopodobnie większym czynnikiem niż rozmiar lub czas przetwarzania dla każdego żądania.

2) Dlaczego został wdrożony zamiast aktualizacji protokołu HTTP?

Podejmowane są próby przeprojektowania protokołu HTTP, aby uzyskać lepszą wydajność i mniejsze opóźnienia, takie jak SPDY , HTTP 2.0 i QUIC . Poprawi to sytuację w przypadku zwykłych żądań HTTP, ale jest prawdopodobne, że WebSockets i / lub WebRTC DataChannel nadal będą miały mniejsze opóźnienia dla przesyłania danych między klientem a serwerem niż protokół HTTP (lub będą używane w trybie przypominającym WebSockets w każdym razie).

Aktualizacja :

Oto ramy myślenia o protokołach sieciowych:

  • TCP : niskopoziomowa, dwukierunkowa, pełny dupleks i gwarantowana warstwa transportu zamówień. Brak obsługi przeglądarki (z wyjątkiem wtyczki / Flasha).
  • HTTP 1.0 : protokół transportowy żądanie-odpowiedź warstwowy na TCP. Klient wysyła jedno pełne żądanie, serwer daje jedną pełną odpowiedź, a następnie połączenie zostaje zamknięte. Metody żądania (GET, POST, HEAD) mają określone znaczenie transakcyjne dla zasobów na serwerze.
  • HTTP 1.1 : utrzymuje charakter zapytania-odpowiedzi HTTP 1.0, ale umożliwia połączenie otwarte dla wielu pełnych żądań / pełnych odpowiedzi (jedna odpowiedź na żądanie). Nadal ma pełne nagłówki w żądaniu i odpowiedzi, ale połączenie jest ponownie używane i nie jest zamykane. HTTP 1.1 dodał także kilka dodatkowych metod żądania (OPCJE, PUT, DELETE, TRACE, CONNECT), które również mają określone znaczenie transakcyjne. Jednak, jak zauważono we wstępie do projektu propozycji HTTP 2.0, potokowanie HTTP 1.1 nie jest szeroko wdrażane, więc znacznie ogranicza to użyteczność HTTP 1.1 do rozwiązywania opóźnień między przeglądarkami a serwerami.
  • Długie odpytywanie : rodzaj „włamania” do HTTP (1.0 lub 1.1), w którym serwer nie odpowiada natychmiast (lub tylko częściowo nagłówkami) na żądanie klienta. Po odpowiedzi serwera klient natychmiast wysyła nowe żądanie (używając tego samego połączenia, jeśli przez HTTP 1.1).
  • Strumieniowe przesyłanie HTTP : różne techniki (odpowiedź wieloczęściowa / podzielona na porcje), które pozwalają serwerowi wysłać więcej niż jedną odpowiedź na jedno żądanie klienta. W3C standaryzuje to jako zdarzenia wysyłane przez serwer przy użyciu text/event-streamtypu MIME. Interfejs API przeglądarki (który jest dość podobny do interfejsu WebSocket API) nazywa się interfejsem API EventSource.
  • Push komety / serwera : jest to termin obejmujący zarówno dalekosiężne, jak i strumieniowe przesyłanie HTTP. Biblioteki komet zwykle obsługują wiele technik, aby zmaksymalizować obsługę wielu przeglądarek i serwerów.
  • WebSockets : wbudowana warstwa transportowa TCP, która korzysta z przyjaznego protokołu HTTP Upshake. W przeciwieństwie do TCP, który jest transportem strumieniowym, WebSockets jest transportem opartym na wiadomościach: wiadomości są rozdzielane przewodowo i ponownie składane w całości przed dostarczeniem do aplikacji. Połączenia WebSocket są dwukierunkowe, full-duplex i długotrwałe. Po początkowym żądaniu / odpowiedzi uzgadniania nie ma semantyki transakcyjnej i narzut na wiadomość jest bardzo mały. Klient i serwer mogą wysyłać wiadomości w dowolnym momencie i muszą obsługiwać odbiór wiadomości asynchronicznie.
  • SPDY : inicjowana przez Google propozycja rozszerzenia HTTP przy użyciu bardziej wydajnego protokołu przewodowego, ale z zachowaniem całej semantyki HTTP (żądanie / odpowiedź, pliki cookie, kodowanie). SPDY wprowadza nowy format ramkowania (z ramkami z prefiksem długości) i określa sposób warstwowania par żądania / odpowiedzi HTTP na nowej warstwie ramkowania. Nagłówki można kompresować, a nowe nagłówki można wysyłać po ustanowieniu połączenia. Istnieją rzeczywiste implementacje SPDY w przeglądarkach i serwerach.
  • HTTP 2.0 : ma podobne cele jak SPDY: redukuje opóźnienia HTTP i koszty ogólne przy jednoczesnym zachowaniu semantyki HTTP. Obecna wersja robocza pochodzi z SPDY i definiuje aktualizację uzgadniania i ramkowania danych, która jest bardzo podobna do standardu WebSocket dla uzgadniania i ramkowania. Alternatywna propozycja wersji roboczej HTTP 2.0 (httpbis-speed -obility) faktycznie używa WebSockets dla warstwy transportowej i dodaje multipleksowanie SPDY i mapowanie HTTP jako rozszerzenie WebSocket (rozszerzenia WebSocket są negocjowane podczas uzgadniania).
  • WebRTC / CU-WebRTC : propozycje umożliwienia łączności peer-to-peer między przeglądarkami. Może to umożliwić komunikację o niższych średnich i maksymalnych opóźnieniach, ponieważ ponieważ podstawowym transportem jest SDP / datagram, a nie TCP. Pozwala to na dostarczanie pakietów / wiadomości poza kolejnością, co pozwala uniknąć problemu z opóźnieniami TCP spowodowanymi przez upuszczone pakiety, które opóźniają dostarczenie wszystkich kolejnych pakietów (w celu zagwarantowania dostarczenia w kolejności).
  • QUIC : jest eksperymentalnym protokołem mającym na celu zmniejszenie opóźnień sieci w stosunku do TCP. Na powierzchni, QUIC jest bardzo podobny do TCP + TLS + SPDY zaimplementowanego na UDP. QUIC zapewnia multipleksowanie i kontrolę przepływu równoważną HTTP / 2, bezpieczeństwo równoważne TLS oraz semantykę połączenia, niezawodność i kontrolę przeciążenia równoważną TCP. Ponieważ TCP jest zaimplementowany w jądrach systemu operacyjnego i oprogramowaniu wewnętrznym skrzynki pośredniej, wprowadzenie znacznych zmian w TCP jest prawie niemożliwe. Ponieważ jednak QUIC jest zbudowany na bazie UDP, nie ma takich ograniczeń. QUIC jest zaprojektowany i zoptymalizowany dla semantyki HTTP / 2.

Referencje :


1
>> Jednak to nie pomaga w opóźnieniu między klientem a serwerem, co wymaga ustanowienia nowego połączenia dla każdego komunikatu klient-serwer. - co z transmisją strumieniową treści odpowiedzi? wiem, XMLHttpRequest API nie pozwala na to, ale istnieje. dzięki strumieniowaniu na serwer możesz przesyłać strumieniowo ze strony klienta.
4esn0k

8
@Filipp, zadał pytanie, które i tak chciałem dokładnie zbadać i udokumentować. Kwestia WebSockets kontra inny mechanizm oparty na HTTP pojawia się dość często, więc teraz istnieje dobre odniesienie do linku. Ale tak, wydaje się prawdopodobne, że pytający szukał dowodów na poparcie z góry przyjętego poglądu na temat WebSockets kontra HTTP, zwłaszcza że nigdy nie wybrał odpowiedzi ani nie przyznał nagrody.
kanaka

9
Dziękuję bardzo za ten bardzo ładny i precyzyjny przegląd protokołów.
Martin Meeser,

2
@WardC caniuse.com podaje informacje o zgodności przeglądarki (w tym mobilnej).
kanaka

3
@ www139, nie, na poziomie protokołu WebSocket połączenie pozostaje otwarte, dopóki jedna lub druga strona nie zamknie połączenia. Być może będziesz musiał martwić się przekroczeniem limitu czasu TCP (problem z dowolnym protokołem opartym na TCP), ale każdy ruch co minutę lub dwie utrzyma połączenie otwarte. W rzeczywistości definicja protokołu WebSocket określa typ ramki ping / pong, chociaż nawet bez tego można wysłać pojedynczy bajt (plus nagłówek dwóch bajtów), co utrzyma połączenie otwarte. 2-3 bajty na kilka minut wcale nie mają znaczącego wpływu na przepustowość.
kanaka

130

Wygląda na to, że WebSocket zastępuje HTTP. Nie jest. To rozszerzenie.

Głównym przykładem zastosowania WebSockets są aplikacje Javascript, które działają w przeglądarce internetowej i odbierają dane w czasie rzeczywistym z serwera. Gry są dobrym przykładem.

Przed WebSockets jedyną metodą interakcji aplikacji JavaScript z serwerem była XmlHttpRequest. Ale mają one poważną wadę: serwer nie może wysłać danych, chyba że klient wyraźnie o to poprosi.

Ale nowa funkcja WebSocket pozwala serwerowi wysyłać dane w dowolnym momencie. Pozwala to na implementację gier opartych na przeglądarce o znacznie mniejszych opóźnieniach i bez konieczności używania brzydkich hacków, takich jak długie odpytywanie AJAX lub wtyczki do przeglądarki.

Dlaczego więc nie użyć normalnego HTTP z żądaniami i odpowiedziami przesyłanymi strumieniowo?

W komentarzu do innej odpowiedzi zasugerowałeś, aby asynchronicznie przesyłać strumieniowo żądanie klienta i treść odpowiedzi.

W rzeczywistości WebSockets są w zasadzie takie. Próba otwarcia połączenia WebSocket z klienta na początku wygląda jak żądanie HTTP, ale specjalna dyrektywa w nagłówku (Upgrade: websocket) nakazuje serwerowi rozpoczęcie komunikacji w tym trybie asynchronicznym. Pierwsze wersje protokołu WebSocket nie były niczym więcej, jak i pewnym uzgadnianiem, aby upewnić się, że serwer faktycznie rozumie, że klient chce komunikować się asynchronicznie. Ale potem zdano sobie sprawę, że serwery proxy byłyby przez to zdezorientowane, ponieważ są przyzwyczajeni do zwykłego modelu żądania / odpowiedzi HTTP. Wykryto potencjalny scenariusz ataku na serwery proxy. Aby temu zapobiec, konieczne było, aby ruch WebSocket wyglądał inaczej niż normalny ruch HTTP. Dlatego wprowadzono klucze maskowaniaostateczna wersja protokołu .


>> serwer nie może wysłać danych, chyba że klient wyraźnie tego zażąda; Przeglądarka internetowa powinna zainicjować połączenie WebSockets ... tak samo jak dla XMLHttpRequest
4esn0k

18
@ 4esn0k Przeglądarka nawiązuje połączenie z gniazdem internetowym. Ale po ustaleniu obie strony mogą wysyłać dane w dowolnym momencie. Tak nie jest w przypadku XmlHttpRequest.
Philipp

1
DLACZEGO nie jest to możliwe w przypadku HTTP?
4esn0k

4
@Philipp, gry są dobrym przykładem, w którym świecą WebSockets. Jednak nie są to dane w czasie rzeczywistym z serwera, na którym można uzyskać największą wygraną. Możesz uzyskać prawie tak samo opóźnienie serwer-> klient, korzystając z przesyłania strumieniowego HTTP / długo utrzymywanych połączeń. A przy długo utrzymywanych żądaniach serwery mogą skutecznie wysyłać, gdy tylko mają dane, ponieważ klient już wysłał żądanie, a serwer „wstrzymuje żądanie”, dopóki nie ma danych. Największą wygraną dla WebSockets jest opóźnienie klient-> serwer (a zatem obrót w obie strony). Klient jest w stanie wysyłać, kiedy chce, bez narzutu na żądanie, to prawdziwy klucz.
kanaka

1
@Philipp, kolejna uwaga: istnieją inne metody oprócz XMLHttpRequest i WebSockets dla JavaScript do interakcji z serwerem, w tym ukryte elementy iframe i znaczniki skryptów długiego odpytywania. Zobacz stronę wikipedii Comet, aby uzyskać więcej informacji: en.wikipedia.org/wiki/Comet_(programming)
kanaka

27

Zwykły interfejs API REST używa protokołu HTTP jako podstawowego protokołu komunikacji, który jest zgodny z paradygmatem żądania i odpowiedzi, co oznacza, że ​​komunikacja obejmuje klienta żądającego pewnych danych lub zasobów z serwera, a serwer odpowiada z powrotem temu klientowi. Jednak HTTP jest protokołem bezstanowym, więc każdy cykl żądanie-odpowiedź będzie musiał powtarzać informacje nagłówka i metadanych. Powoduje to dodatkowe opóźnienie w przypadku często powtarzanych cykli żądanie-odpowiedź.

http

W przypadku WebSockets, mimo że komunikacja nadal rozpoczyna się jako początkowy uścisk dłoni HTTP, kolejne aktualizacje są zgodne z protokołem WebSockets (tj. Jeśli zarówno serwer, jak i klient są zgodne z protokołem, ponieważ nie wszystkie podmioty obsługują protokół WebSockets).

Teraz dzięki WebSockets możliwe jest ustanowienie pełnego dupleksu i trwałego połączenia między klientem a serwerem. Oznacza to, że w przeciwieństwie do żądania i odpowiedzi, połączenie pozostaje otwarte tak długo, jak aplikacja jest uruchomiona (tzn. Jest trwała), a ponieważ jest w trybie pełnego dupleksu, możliwa jest jednoczesna komunikacja dwukierunkowa, tj. Teraz serwer może inicjować komunikacja i „wypychanie” niektórych danych do klienta, gdy nowe dane (którymi klient jest zainteresowany) stają się dostępne.

gniazda sieciowe

Protokół WebSockets jest stanowy i pozwala na wdrożenie wzorca komunikatów Publikuj-Subskrybuj (lub Pub / Sub), który jest podstawową koncepcją stosowaną w technologiach czasu rzeczywistego, w których możesz otrzymywać nowe aktualizacje w formie wypychania serwera bez klient musi wielokrotnie żądać (odświeżać stronę). Przykładami takich aplikacji są śledzenie lokalizacji samochodu Uber, powiadomienia push, ceny giełdowe aktualizowane w czasie rzeczywistym, czat, gry wieloosobowe, narzędzia współpracy online na żywo itp.

Możesz zapoznać się z głębokim artykułem nurkowym na temat Websockets, który wyjaśnia historię tego protokołu, jak powstał, do czego służy i jak możesz go wdrożyć samodzielnie.

Oto wideo z prezentacji, którą zrobiłem na temat WebSockets i tego, jak różnią się one od używania zwykłych interfejsów API REST: Standaryzacja i wykorzystanie wykładniczego wzrostu strumieniowania danych


24

Dla TL; DR, oto 2 centy i prostsza wersja na twoje pytania:

  1. WebSockets zapewnia następujące korzyści w porównaniu z HTTP:

    • Trwałe połączenie stanowe przez czas trwania połączenia
    • Niskie opóźnienie: komunikacja niemal w czasie rzeczywistym między serwerem / klientem z powodu braku narzutu ponownego nawiązywania połączeń dla każdego żądania, zgodnie z wymaganiami HTTP.
    • Pełny dupleks: zarówno serwer, jak i klient mogą wysyłać / odbierać jednocześnie
  2. Protokół WebSocket i HTTP zostały zaprojektowane w celu rozwiązania różnych problemów, IE WebSocket został zaprojektowany w celu usprawnienia komunikacji dwukierunkowej, podczas gdy HTTP został zaprojektowany jako bezstanowy, dystrybuowany przy użyciu modelu żądanie / odpowiedź. Poza dzieleniem portów z wcześniejszych powodów (penetracja zapory / proxy), nie ma zbyt wiele wspólnego, aby połączyć je w jeden protokół.


3
Ważne, że w swoim porównaniu podałeś (-aś) termin stanowy i bezpaństwowiec (Y)
Utsav T

15

Dlaczego protokół WebSockets jest lepszy?

Nie sądzę, byśmy mogli porównać je ze sobą, jak kto jest lepszy. To nie będzie sprawiedliwe porównanie tylko dlatego, że rozwiązują dwa różne problemy . Ich wymagania są różne. To będzie jak porównywanie jabłek do pomarańczy. Oni są różni.

HTTP jest protokołem żądanie-odpowiedź. Klient (przeglądarka) czegoś chce, serwer to daje. To jest. Jeśli klient danych chce być duży, serwer może wysłać dane strumieniowe, aby unieważnić niepożądane problemy z buforem. Tutaj głównym wymaganiem lub problemem jest to, jak złożyć żądanie od klientów i jak odpowiedzieć na żądane zasoby (hybertext). Właśnie tam świeci HTTP.

W HTTP tylko żądanie klienta. Serwer odpowiada tylko.

WebSocket nie jest protokołem żądanie-odpowiedź, w którym może żądać tylko klient. Jest to gniazdo (bardzo podobne do gniazda TCP). Oznacza to, że po otwarciu połączenia każda ze stron może wysyłać dane do momentu podkreślenia, że ​​połączenie TCP zostało zamknięte. To jest jak normalne gniazdo. Jedyną różnicą w stosunku do gniazda TCP jest to, że websocket może być używany w sieci. W sieci mamy wiele ograniczeń dla normalnego gniazda. Większość zapór blokuje inne porty niż 80 i 433, których używał HTTP. Problemem będą także proxy i pośrednicy, więc aby ułatwić wdrażanie protokołu w istniejącej infrastrukturze, do uaktualnienia użyj uzgadniania HTTP. Oznacza to, że kiedy pierwsze połączenie ma zostać otwarte, klient wysłał żądanie HTTP do serwera z informacją „To nie jest żądanie HTTP, proszę zaktualizować do protokołu WebSocket”.

Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

Gdy serwer zrozumie żądanie i zaktualizuje do protokołu WebSocket, żaden z protokołów HTTP nie będzie już stosowany.

Więc moja odpowiedź brzmi: Żaden z nich nie jest lepszy od siebie. Oni są zupełnie inni.

Dlaczego został wdrożony zamiast aktualizacji protokołu HTTP?

Cóż, możemy zrobić wszystko pod nazwą HTTP . Ale czy powinniśmy? Jeśli są to dwie różne rzeczy, wolę dwie różne nazwy. Podobnie Hickson i Michael Carter .


6

Inne odpowiedzi nie wydają się dotyczyć tutaj kluczowego aspektu, to znaczy nie wspominasz o wymaganiu obsługi przeglądarki internetowej jako klienta. Większość powyższych ograniczeń zwykłego HTTP zakłada, że ​​będziesz pracować z implementacjami przeglądarki / JS.

Protokół HTTP jest w pełni zdolny do komunikacji w trybie pełnego dupleksu; legalne jest, aby klient wykonał test POST z przesyłaniem fragmentów kodowanych, a serwer zwrócił odpowiedź z ciałem kodującym fragmenty. Spowodowałoby to usunięcie nagłówka nagłówka tylko w momencie inicjacji.

Więc jeśli wszystko, czego szukasz, to pełny dupleks, kontroluj zarówno klienta, jak i serwer, i nie jesteś zainteresowany dodatkowymi ramkami / funkcjami websockets, to argumentowałbym, że HTTP jest prostszym podejściem z mniejszym opóźnieniem / procesorem (chociaż opóźnienie tak naprawdę będzie się różnić tylko w mikrosekundach lub krócej dla obu).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.