Narzędzia użytkownika

Narzędzia witryny


Pasek boczny


O mnie


Dydaktyka:

Feedback


sk2:sockets_full

To jest stara wersja strony!


Interface gniazd BSD

Schemat kolejności wywołać funkcji bibliotecznych znajduje się na:
http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/bsdsockets.html#rys-1

Serwer TCP

Aby oczekiwać na przychodzące połączenia TCP, po stworzeniu gniazda (socket(…)) wystarczy wywołać funkcję listen(…), ale: jeśli wcześniej nie ustawi się adresu gniazda, system operacyjny wylosuje efemeryczny port i rozpocznie nasłuch na dowolnym adresie IP na tym porcie.
Dlatego przed funkcją listen(…) należy użyć funkcji bind(…) do ustawienia lokalnego adresu gniazda. Funkcja bind przyjmuje jako argument strukturę sockaddr.

Lokalny adres IP można ustawić na dowolny – INADDR_ANY lub wybrany przez siebie – np. 127.0.0.1 (INADDR_LOOPBACK) jeśli połączenia mają być ograniczone do localhosta.

Po wywołaniu funkcji listen system operacyjny system operacyjny czeka na połączenia. Do odebrania nowego połączenia należy użyć funkcji accept(…). Funkcja accept zwróci nowe gniazdo reprezentujące nawiązane połączenie.

Zadanie 1: Napisz program, który:

  • stworzy gniazdo,
  • ustali adres (bind),
  • rozpocznie oczekiwanie na połączenia (listen),
  • zaakceptuje połączenie (accept); na razie drugi i trzeci argument funkcji accept pozostaw pusty,
  • wyśle tam dane (stały ciąg znaków),
  • zakończy program.

Zadanie 2: Zmodyfikuj program tak, by w pętli obsługiwał nowe połączenia

Funkcja accept (podobnie jak wprowadzana za chwilę recvfrom) może przekazać informację o adresie z którego nawiązano połączenie. W tym celu należy podać jej adres struktury sockaddr (drugi argument) i adres zmiennej, która w momencie wywołania accept na wppisany rozmiar przekazanej struktury, a do której accept zapisze ile bajtów przekazanej struktury wypełnił (trzeci argument).

Zadanie 3: Dodaj do programu wyświetlanie z jakiego adreesu IP i numeru portu nawiązano połączenie

UDP vs TCP - przypomnienie

TCP: Strumieniowy. Połączeniowy. Kontrola przepływu. Niezawodny (reliable).
UDP: Zorientowany na wiadomość. Bezpołączeniowy, bezstanowy. Brak kontroli przepływu. Best-effort.

Tworząc gniazdo UDP należy użyć następujących argumentów:

socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)
// lub: socket(PF_INET, SOCK_DGRAM, 0)

UDP nie nawiązuje połączenia – do odbioru i wysyłania wiadomości należy używać funkcji sendto i recvfrom (lub recv/read, jeśli nie obchodzi nas nadawca). W formie ułatwienia BSD socket API pozwala działać gniazdom UDP w trybie pseudo-połączeniowym - tzn. można wywołać funkcję connect (która ustali adres odbiorcy) i dalej korzystać z send / write.

UDP jest zorientowane na wiadomość – wysyła i odbiera całe datagramy. Jeśli funkcja odbierająca dane zadeklaruje mniejszy bufor niż rozmiar wiadomości, nadmiarowe dane zostaną odrzucone.

Zadanie 4. Odpowiedz na pytanie dlaczego DNS używa UDP zamiast TCP do zapytań.

Klient UDP

Zadanie 5. Napisz program, który wyśle dane (stały ciąg znaków) pod wskazany adres, następnie odbierze dane i się zakończy.

Serwer UDP

Zadanie 6. Napisz program, który odbierze dane i odeśle je pod adres nadawcy zmieniając wielkość liter.
Jeśli nie wiesz jak zmienić wielkość liter, to for(char *it=str; (*it=toupper(*it)); ++it);

Zadanie 7. Napisz program, który będzie w pętli odbierać dane i odsyłać je pod adresy wszystkich wcześniejszych nadawców.

Read i Write - ilość odczytanych / zapisanych danych

Polecenie read, recv,… często zwracają mniej danych niż podana w argumentach wielkość bufora. Powód? Mniej danych przyszło lub skończyło się miejsce w buforze odbiorczym (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, …)).

Polecenia write, send,… może też zwrócić w pewnych okolicznościach mniejszy rozmiar wysłanych danych niż podany rozmiar buforu. Jest to spowodowane zapchaniem systemowego buforu wysyłania (czyli jeśli dane są dostarczane do wysłania szybciej niż można je wysyłać). Normalnie funkcje zapisujące blokują się do momentu aż w buforze będzie dość miejsca. Jeśli jednak ustawi się opcję O_NONBLOCK (typowa w wielu zastosowaniach), to funkcje takie jak write, send, … zapisują tylko część danych.

Dlatego zarówno przy odbieraniu jak i wysyłaniu trzeba zwracać uwagę na zwracaną ilość przetworzonych danych.

Zadanie 8. W serwerze TCP zmień rozmiar buforu nadawczego, przestaw gniazdo w tryb nieblokujący (kod poniżej) i każ wysłać ponad 4096 bajtów. Zaobserwuj co odbierze klient. Uwaga: klient musi być z innego hosta.
Nie wiesz jak wygenerować długi komunikat? ctrl+c, ctrl+v. Albo string s{"a"}; s.resize (22220,'='); s+="z"; Potem: s.c_str() i s.length()

int buffsize = 1024;
setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize));
fcntl(socket, F_SETFL, O_NONBLOCK, 1);

Zadanie 9. W kliencie UDP i TCP zmniejsz rozmiar bufora przekazywanego do funkcji odbioru wiadomości i wykonaj odbiór dwa razy. Zaobserwuj co odbierze klient.

Opcje gniazd

Strona podręcznika man 7 socket opisuje możliwe argumenty funkcji setsockopt i getsockopt. Ważne opcje to:

Funkcja fcntl (man fcntl open) pozwala na ustawienie (F_SETFL) opcji O_NONBLOCK potrzebnej do nieblokującej obsługi gniazd. (O_NONBLOCK można też ustawić sumując ostatni argument funkcji socket z SOCK_NONBLOCK.)

sk2/sockets_full.1476894240.txt.gz · ostatnio zmienione: 2016/10/19 18:24 przez jkonczak