20
websocket bez frameworka i bibliotek w node

Zazwyczaj jak potrzeba coś zrobić szybko to używa się gotowych rozwiązań. W przypadku websocket istnieje wiele implementacji tego protokołu.
Każdy popularny mniej lub bardziej język programowania ma bibliotekę obsługującą websocket.
Jednakże, ja chciałem wiedzieć jak upierdliwe jest napisanie zalążka implementacji tego protokołu w C.
Na szczęście okazało się to osiągalne dla mnie. Dlatego teraz przyszła pora na utrwalanie wiedzy którą musiałem odświeżyć sobie tworząc
wersję dla node.
Każdy popularny mniej lub bardziej język programowania ma bibliotekę obsługującą websocket.
Jednakże, ja chciałem wiedzieć jak upierdliwe jest napisanie zalążka implementacji tego protokołu w C.
Na szczęście okazało się to osiągalne dla mnie. Dlatego teraz przyszła pora na utrwalanie wiedzy którą musiałem odświeżyć sobie tworząc
wersję dla node.
Zacząłem od stworzenia w node dwóch serwerów. Pierwszy serwer to serwer http. Jest odpowiedzialny za wyświetlanie strony, dzięki której
będzie można wysłać krótką wiadomość do serwera i wyświetlić odpowiedz.
Drugi serwer tcp jest odpowiedzialny za odebranie i wysłanie tej samej wiadomości.
Oczywiście każdy z serwerów ma swój własny port. Http 8080, tcp 8081.
będzie można wysłać krótką wiadomość do serwera i wyświetlić odpowiedz.
Drugi serwer tcp jest odpowiedzialny za odebranie i wysłanie tej samej wiadomości.
Oczywiście każdy z serwerów ma swój własny port. Http 8080, tcp 8081.
Pierwszą rzeczą jaką należy zrobić po połączeniu klienta z serwerem jest przygotowanie uścisku dłoni.
Żeby przygotować taki uścisk potrzebny jest klucz od klienta. Pierwszy pakiet który wysyła klient jest w formacie tekstowym.
Z tego tekstu trzeba wyciągnąć klucz który kryje się pod Sec-WebSocket-Key.
Gdy już wyciągnie się klucz, należy go potem połączyć z kluczem 258EAFA5-E914-47DA-95CA-C5AB0DC85B11.
Jest to z góry zdefiniowany klucz przez specyfikacje websocket.
Połączone klucze w jeden ciąg należy następnie zaszyfrować algorytmem sha1 a następnie potraktować base64.
Potem tak przygotowany klucz umieścić w odpowiednim nagłówku.
Nagłówkiem jest umieszczony w funkcji createHeader. Należy zwrócić uwagę na formatowanie nagłówka. Ono nie jest przypadkowe. Tak samo
jak pusta linijka na końcu nagłówka. Zadaniem nagłówka jest poinformowanie klienta w tym przypadku przeglądarki że dokonuje się zmiana
protokołu z http na websocket.
Gdy taki nagłówek zostanie wysłany do klienta, a uścisk dłoni zaakceptowany, zostanie wysłąny kolejny pakiet.
Żeby przygotować taki uścisk potrzebny jest klucz od klienta. Pierwszy pakiet który wysyła klient jest w formacie tekstowym.
Z tego tekstu trzeba wyciągnąć klucz który kryje się pod Sec-WebSocket-Key.
Gdy już wyciągnie się klucz, należy go potem połączyć z kluczem 258EAFA5-E914-47DA-95CA-C5AB0DC85B11.
Jest to z góry zdefiniowany klucz przez specyfikacje websocket.
Połączone klucze w jeden ciąg należy następnie zaszyfrować algorytmem sha1 a następnie potraktować base64.
Potem tak przygotowany klucz umieścić w odpowiednim nagłówku.
Nagłówkiem jest umieszczony w funkcji createHeader. Należy zwrócić uwagę na formatowanie nagłówka. Ono nie jest przypadkowe. Tak samo
jak pusta linijka na końcu nagłówka. Zadaniem nagłówka jest poinformowanie klienta w tym przypadku przeglądarki że dokonuje się zmiana
protokołu z http na websocket.
Gdy taki nagłówek zostanie wysłany do klienta, a uścisk dłoni zaakceptowany, zostanie wysłąny kolejny pakiet.
Ten pakiet będzie zawierał ramkę. Skupie się tylko na tej części ramki która pozwala odczytać i wysłać wiadomość tekstową.
W pierwszym bajcie znajduje się informacja o typie ramki, jak i informacje czy spodziewać się więcej ramek.
Drugi bajt zawiera maskę i rozmiar przesyłąnych danych.
Jeżeli maska ma wartość 1 to dane są zaciemnione. Klient zawsze powinien wysyłać dane zaciemnione.
Rozmiar danych może mieć długość od 7 do nawet 71 bitów. Kod który napisałem jest nastawiony na krótkie wiadomości,
nieprzekraczające 127 znaków. Dlatego też cztery kolejne bajty będą zawierały klucz maskujący, dzięki któremu zostały
zaciemnione dane. Zaciemnienie danych nie wpływa na zmianę ich długości czy też kolejność.
W pierwszym bajcie znajduje się informacja o typie ramki, jak i informacje czy spodziewać się więcej ramek.
Drugi bajt zawiera maskę i rozmiar przesyłąnych danych.
Jeżeli maska ma wartość 1 to dane są zaciemnione. Klient zawsze powinien wysyłać dane zaciemnione.
Rozmiar danych może mieć długość od 7 do nawet 71 bitów. Kod który napisałem jest nastawiony na krótkie wiadomości,
nieprzekraczające 127 znaków. Dlatego też cztery kolejne bajty będą zawierały klucz maskujący, dzięki któremu zostały
zaciemnione dane. Zaciemnienie danych nie wpływa na zmianę ich długości czy też kolejność.
Żeby zdemaskować tekst, każdy bajt powinien być przepuszczony przez algorytm który kryje się w funkcji mapUnmask.
Choć algorytm jest prosty, to wzór który jest opisany w dokumentacji jest dla mnie nijak niezrozumiały
Choć algorytm jest prosty, to wzór który jest opisany w dokumentacji jest dla mnie nijak niezrozumiały
Gdy już tekst zostanie przepuszczony przez algorytm. Należy go następnie wysłać. Żeby to zrobić, kopiuje pierwszy bajt.
W drugim bajcie umieszczam rozmiar wysyłanego tekstu. Serwer może wysyłać tylko niezaciemnione dane, dlatego maska jest na 0.
I reszta bajtów to już sam tekst który odbiera przeglądarka.
W drugim bajcie umieszczam rozmiar wysyłanego tekstu. Serwer może wysyłać tylko niezaciemnione dane, dlatego maska jest na 0.
I reszta bajtów to już sam tekst który odbiera przeglądarka.
Omawiany kod.
Źródła z których czerpałem wiedzę na temat websocket.