![]() | Pobierz dokument dyplom.instytut.informatyki.doc Rozmiar 1.5 MB |
Instytut Informatyki
Wydziału Techniki Morskiej
Politechniki Szczecińskiej
PRACA DYPLOMOWA
Adam Szablewski
Temat pracy:
Pakiet programów narzędziowych do obsługi protokołów komunikacyjnych w systemie operacyjnym Windows 95
promotor:
mgr inż. Rafał Szmidt
Szczecin 1997
Spis treści
Wstęp 4
1. Cel pracy 5
2. Dostęp do sieci komputerowej w Windows 95 6
2.1. Składniki sieci 6
2.1.1. Klient sieci 6
2.1.2. Karta sieciowe 7
2.1.3. Protokół komunikacyjny 7
2.1.4. Usługa 7
2.2. Programy narzędziowe 9
2.2.1. Program Ping 9
2.2.2. Program Tracert 9
2.2.3. Program Telnet 10
2.3. Programy usługowe - dostęp do Internetu 11
3. Protokoły komunikacyjne 13
3.1. Oprogramowanie sieciowe NetBIOS 13
3.1.1. Informacje podstawowe 13
3.1.2. Rodzaje obsługi 14
3.1.3. Polecenia NetBIOS 14
3.2. Rodzina protokołów TCP/IP 17
3.2.1. Informacje podstawowe 17
3.2.2. Warstwy 17
3.2.3. Adresy Internetu 21
4. Programowanie sieciowe w Windows 95 23
4.1. Sieciowe funkcje API Win32 23
4.1.1. Dostęp do zasobów sieciowych przy użyciu protokołu NetBIOS 23
4.1.2. Wymiana danych przez łącza nazwane i skrzynki „mailslot” 23
4.2. Specyfikacja Windows Sockets 25
4.2.1. Inicjalizacja biblioteki 25
4.2.2. Gniazda 25
4.2.3. Dostęp do sieci 27
4.2.4. Dodatkowe funkcje usługowe 30
4.2.5. Przykładowe programy 32
5. Pakiet programów narzędziowych 36
5.1. Informacje podstawowe 36
5.2. Program Ping 37
5.2.1. Funkcja 37
5.2.2. Algorytm 37
5.2.3. Implementacja 39
5.2.4. Opis działania 41
5.3. Program Tracer 43
5.3.1. Funkcja 43
5.3.2. Algorytm 43
5.3.3. Implementacja 45
5.3.4. Opis działania 47
5.4. Program Namer 50
5.4.1. Funkcja 50
5.4.2. Algorytm 50
5.4.3. Implementacja 51
5.4.4. Opis działania 51
6. Wnioski 54
7. Bibliografia 55
Wstęp
Pojęcia takie jak Internet, poczta elektroniczna czy serwer WWW używane są obecnie przez zwykłych użytkowników komputerów, lecz jeszcze kilka lat temu występowały tylko w słowniku wąskiej grupy informatyków. Do globalnej sieci Internet podłączone są setki tysięcy komputerów, mimo że wojskowa sieć ARPANET, będąca zaczątkiem Internetu, opuściła okolice Departamentu Obrony USA zaledwie 10 lat temu! Nie należy się temu jednak dziwić - komputer podłączony do sieci udostępnia wiele przydatnych usług, takich jak przesyłanie listów zawierających tekst, grafikę, dźwięk i animację, możliwość przeglądania serwisów informacyjnych z najbardziej aktualnymi informacjami, zdalne zamawianie towarów w sklepach, a nawet wideotelefonię i wideokonferencję. Sieć WWW w ciągu krótkiego czasu stała się ogromną encyklopedią multimedialną, zawierającą informacje ze wszystkich dziedzin. Tak dynamiczny przyrost użytkowników wymusza potrzebę równie dynamicznego rozwoju sprzętu i oprogramowania, które służą do przesyłania informacji. Z powodu rosnących wymagań użytkowników, przesyłających coraz większe ilości danych multimedialnych, zwiększana jest przepustowość urządzeń transmisyjnych. Ostatnie osiągnięcia to technologia ATM, pozwalająca na przesyłanie danych z szybkością 2500 Mb/s oraz Gigabit Ethernet o przepustowości 1000 Mb/s. Równolegle z rozwojem sprzętu unowocześniane są protokoły komunikacyjne. Wprowadzana jest właśnie nowa wersja internetowej rodziny protokołów TCP/IP - IPv6. Jedną z najważniejszych zmian zaimplementowaną w nowym protokole jest poszerzenie przestrzeni adresowej z 32 do 128 bitów. Tym samym rozwiązano pojawiający się już problem braku wolnych adresów - teraz można będzie zaadresować w globalnej sieci, niewyobrażalną wprost liczbę, ponad 3*1038 komputerów. Producenci oprogramowania systemowego także dostrzegli zainteresowania sieciowe użytkowników mikrokomputerów. Potentat w dziedzinie oprogramowania, firma Microsoft, zaoferowała w 1995 roku system operacyjny Windows 95, który posiada wbudowane możliwości sieciowe, i to zarówno dla sieci lokalnych, jak i rozległych. Dodatkowa zaleta tego programu, jaką jest łatwość obsługi, spowodowała, że stał się on jednym z systemów operacyjnych najczęściej instalowanych w mikrokomputerach.
Jednakże, pomimo wzrostu jakości sprzętu i oprogramowania (a więc i bezpieczeństwa transmisji), nie sposób wyeliminować możliwości awarii w sieci, w której działa tak dużo elementów pośredniczących. Miejsce awarii w sieci komputerowej, a w szczególności w sieci rozległej, jest często bardzo trudne do wykrycia ze względu na duże odległości między kolejnymi węzłami sieci. Niebagatelne znaczenie mają więc wszelkie sieciowe analizatory sprzętowe i programy narzędziowe pozwalające wykryć awarię, a następnie precyzyjnie określić jej miejsce położenia w sieci.
1. Cel pracy
System operacyjny Windows 95, jako następca sieciowego systemu Windows for Workgroups, umożliwia wykorzystanie podłączenia komputera do sieci lokalnej i zawiera także podstawowe elementy pozwalające na dostęp do sieci rozległych.
Celem tej pracy jest zapoznanie z sieciowymi możliwościami systemu Windows 95, w szczególności z programowaniem sieciowym w środowisku 32-bitowym. W efekcie powstanie pakiet programów narzędziowych, przydatny w rozwiązywaniu problemów dostępu do węzłów sieci Internet. Program Ping umożliwi sprawdzenie, czy zdalna stacja o danym adresie jest dostępna dla użytkownika. Jednocześnie mierzony będzie czas przesyłu pakietu danych między stacją lokalną i zdalną. Do określenia jaka jest trasa pakietu danych w drodze do zdalnego celu posłuży program Tracer. Trzeci program, Namer, będzie pomocny przy określaniu nazw odległych komputerów.
Korzystanie z sieci komputerowych w systemie operacyjnym Windows 95 z poziomu użytkownika omówione zostanie w rozdziale 2. Przedstawione będą tam różne elementy sieci, takie jak: protokoły, usługi, standardowe sterowniki kart sieciowych. W dalszej części tego rozdziału opisane będą systemowe programy narzędziowe Ping, Tracert i Telnet oraz programy usługowe, pozwalające na dostęp do Internetu.
Rozdział 3 to opis dwu specyfikacji umożliwiających połączenie w sieci komputerowej. Przedstawiono w nim oprogramowanie sieciowe NetBIOS oraz rodzinę protokołów komunikacyjnych TCP/IP.
Rozdział 4 poświęcony będzie programowaniu sieciowemu. Omówione zostaną biblioteki funkcji Winsock oraz zestaw funkcji API Win32, służący do programowania w sieciach lokalnych.
Utworzone na podstawie wcześniejszych informacji programy narzędziowe, opisane zostaną w rozdziale 5 tej pracy. Znajdą się tam szczegółowe opisy działania programów, ich algorytmy i sposób wykorzystania.
Rozdział 6 zawierał będzie wnioski dotyczące wykorzystania opisanych bibliotek do sieciowego programowanie w Windows 95 i ewentualne możliwości rozbudowy przedstawionego pakietu programów narzędziowych.
2. Dostęp do sieci komputerowej w Windows 95
2.1. Składniki sieci
Twórcy systemu Windows 95 podzielili elementy udostępniające zasoby sieci na cztery składniki: klient sieci, karta sieciowa, protokół komunikacyjny oraz usługa (rys. 1). Oprogramowanie typu „Klient sieci” umożliwia korzystanie z plików i drukarek udostępnionych w innych komputerach sieciowych. Element „Karta sieciowa” pozwala ustalić sterownik programowy odpowiedni dla zamontowanej w komputerze karty sieciowej. Aby system operacyjny mógł skomunikować się z innym komputerem pracującym w sieci, musi znać język porozumiewania się, czyli protokół. Składnik sieciowy „Usługa” daje możliwość wybrania rodzaju usług jakie system operacyjny będzie świadczył na rzecz pozostałych komputerów sieci i jakie usługi będą dla niego dostępne.
Poszczególne elementy muszą być ze sobą powiązane. Jeżeli zainstalowano klienta sieci NetWare, to musi działać w komputerze karta sieciowa z tym klientem współpracująca, dołączyć należy także odpowiedni protokół (np. „Protokół zgodny z IPX/SPX”), warto też określić czy pliki na dysku lokalnym mają być udostępnione dla innych użytkowników sieci. Na szczęście powiązania te mogą być dokonywane automatycznie przez Windows 95.
rys. 1
2.1.1. Klient sieci
Standardowo zainstalowane są w systemie sterowniki obsługujące następujące typy sieci:
Banyan,
FTP (jest to właściwie usługa protokołu TCP/IP, a nie typ sieci),
Microsoft Networks,
Novell NetWare,
SunSoft PC-NFS.
Najczęściej wykorzystywane są sieci Microsoft Networks i Novell NetWare. Pierwsza ze względu na pełną zgodność z systemem operacyjnym Windows 95 (przynajmniej deklarowaną), a druga z powodu powszechności sieciowego systemu Novell NetWare. Sieć Microsoft Networks tworzą połączone komputery, mające zainstalowane następujące systemy operacyjne firmy Microsoft: Windows 95, Windows NT oraz Windows for Workgroups. Do sieci Novell NetWare można dołączyć się na dwa sposoby - korzystając ze sterowników firmy Microsoft lub z oryginalnych sterowników firmy Novell. Należy pamiętać o dodaniu protokołów odpowiednich dla danego klienta sieci (jeśli nie zostały dołączone automatycznie).
Element FTP nie jest właściwie typem sieci, lecz protokołem wysokiego poziomu TCP/IP. Umożliwia on połączenie komputera z odległym, internetowym serwerem FTP, który udostępnia swoje zasoby plików.
2.1.2. Karta sieciowa
Windows 95 zawiera bardzo bogatą listę sterowników łączących system z kartą sieciową. Można odnaleźć standardowe sterowniki wszystkich znanych producentów kart sieciowych w licznych wersjach (m.in.: 3Com, Artisoft, Cabletron, Compaq, DEC, Hewlett Packard, IBM, Intel, Olicom, SMC). Ponieważ jednak powstają coraz nowsze typy transmisji, a więc i coraz nowsze karty sieciowe, dodano opcję pozwalającą na dodanie odpowiedniego sterownika z dyskietki dostarczonej przez producenta wraz z kartą sieciową.
2.1.3. Protokół komunikacyjny
W systemie zawarto kilkanaście różnych protokołów, a ich ilość może być zwiększona po zainstalowaniu protokołu z dyskietki. Najważniejsze standardowo zaimplementowane protokoły to:
NetBEUI,
zgodny z IPX/SPX,
TCP/IP,
Novell IPX ODI,
PC-NFS (firmy SunSoft).
Protokół NetBEUI, będący rozszerzeniem NetBIOS-u, wykorzystywany jest przez sieć typu Microsoft Networks. Sieć ta wykorzystuje też protokół zgodny z IPX/SPX, który potrzebny jest także dla prawidłowego funkcjonowania klienta sieci NetWare. Alternatywnie można wykorzystać protokół firmy Novell - Novell IPX ODI. Protokoły TCP/IP oraz PC-NFS służą do połączenia komputera z siecią rozległą. Protokołem podstawowym jest TCP/IP. Definiuje on sposób komunikacji między odległymi stacjami. Protokół PC-NFS (rozproszony system plików) pozwala na takie połączenie z siecią rozległą, że użytkownik może korzystać z zasobów tej sieci, jakby to były zasoby zapisane na dysku lokalnym. Aby korzystać z rozproszonego systemu plików na stacji roboczej musi być zainstalowany protokół TCP/IP do komunikacji ze stacjami zdalnymi.
2.1.4. Usługa
Ten składnik sieci systemu operacyjnego Windows 95 odpowiedzialny jest za to, czy zasoby komputera lokalnego udostępniane będą innym użytkownikom sieci. Najważniejsze usługi dotyczą sieci Microsoft Networks i Novell NetWare. Osobno ustawia się możliwość zewnętrznego dostępu do plików oraz lokalnych drukarek. Można określić też dwa rodzaje kontroli dostępu do tych zasobów:
kontrola dostępu na poziomie zasobów, która pozwala na podanie hasła dla każdego udostępnionego zasobu,
kontrola dostępu na poziomie użytkownika, która pozwala na określenie użytkowników i grup, mających dostęp do każdego udostępnionego zasobu.
2.2. Programy narzędziowe
Programy Ping, Tracert i Telnet są prostymi narzędziami do obsługi połączenia realizowanego za pomocą protokołu TCP/IP. Dostarczone są one wraz z systemem operacyjnym Windows 95. Niestety, Ping i Tracert są poleceniami wierszowymi, które należy uruchamiać w tekstowym środowisku MS-DOS 7.0, podając w linii komend listę opcji. Taki sposób obsługi znacząco zmniejsza funkcjonalność tych programów. W dalszej części pracy przedstawione będą nowe wersje programów Ping i Tracert, w pełni wykorzystujące możliwości okienkowego systemu Windows 95.
2.2.1. Program Ping
Program Ping służy do testowania poprawności połączenia z inną stacją roboczą lub bramą (ang. gateway), posiadającą własny adres IP. Działanie polega na wysłaniu komunikatu pod wskazany adres i oczekiwaniu na odpowiedź. Po odebraniu komunikatu zwrotnego porównywany jest czas nadania i odbioru pakietu, dzięki czemu określa się czas jego przesyłu między obiema stacjami. Program wywoływany jest z linii poleceń, a jego sposób wywołania i opcje są następujące:
ping [-t] [-a] [-n ilość] [-l wielkość] [-f] [-i TTL] [-w timeout] adres
opcje:
-t |
pakiety wysyłane będą pod wskazany adres do momentu przerwania działania programu przez użytkownika za pomocą klawiszy Ctrl+C |
-a |
Ping określi nazwę stacji o podanym adresie |
-n ilość |
ilość pakietów do wysłania (domyślnie 4) |
-l wielkość |
wielkość wysyłanego pakietu (domyślnie 32) |
-f |
przed wysłaniem pakietu zostanie ustawiona flaga „Don't Fragment”, zabraniająca fragmentacji pakietów przy przechodzeniu przez kolejne podsieci |
-i TTL |
czas TTL, przy przechodzeniu przez kolejne bramy wartość ta zmniejszana jest o 1; gdy osiągnie 0, pakiet jest likwidowany - należy więc zwrócić uwagę, aby wartość ta nie była zbyt mała (domyślnie 128) |
-w czas |
maksymalny czas oczekiwania na powrót pakietu (podawany w milisekundach); jeśli przez ten czas nie będzie odpowiedzi, ping wyświetli komunikat: „Request timed out” |
adres |
adres (lub nazwa) przeznaczenia - stacji końcowej lub bramy |
2.2.2. Program Tracert
Aby dowiedzieć się jak przebiega trasa pakietu od stacji źródłowej do docelowej należy skorzystać z programu Tracert. Podaje on adres i jeśli to możliwe, nazwę każdej bramy i stacji, przez którą transmitowany jest pakiet w drodze do celu. W przypadku awarii któregoś z węzłów sieci można więc określić jak daleko dostarczany jest pakiet i określić miejsce awarii. Tracert również należy wywołać z linii poleceń interpretera DOS. Składnia wywołania jest następująca:
tracert [-d] [-h ilość_prób] [-w czas] adres
opcje:
-d |
opcja zabraniająca określanie nazw poszczególnych stacji pośrednich; wyświetlane będą jedynie adresy |
-h ilość_prób |
ilość prób wysłania pakietu do jednej stacji pośredniej |
-w czas |
maksymalny czas oczekiwania na odpowiedź od każdej stacji (podawany w milisekundach) |
adres |
adres (lub nazwa) przeznaczenia - stacji końcowej lub bramy |
2.2.3. Program Telnet
Sieci rozległe, takie jak Internet, dają możliwość biernego przeglądania zawartości ogromnych zasobów odległych serwerów, ale również pozwalają na zdalną pracę.
rys. 2
Dzięki usłudze TCP/IP - Telnet i programowi o takiej nazwie, możliwe jest zalogowanie użytkownika w odległym systemie. Program Telnet pracujący w komputerze użytkownika odbiera jego polecenia i przesyła je do odległej stacji. Tam z kolei działa proces Telnet-serwer odbierający żądania, odpowiednio na nie reagując (np. uruchamia programy z przesłanymi danymi użytkownika, po czym odsyła wyniki).
System operacyjny Windows 95 wyposażony został w okienkową wersję Telneta. Po uruchomieniu programu (rys. 2) i pomyślnym połączeniu ze zdalnym komputerem, oferującym usługę Telnet, można zdalnie uruchamiać aplikacje.
2.3. Programy usługowe - dostęp do Inertnetu
Firma Microsoft dołączyła do systemu Windows 95 program Microsoft Exchange. Umożliwia on dostęp do wielu usług jakie oferuje Internet. Najważniejsze z nich to obsługa poczty elektronicznej (e-mail) oraz połączenie z inną sieciową usługą Microsoftu - The Microsoft Network. Dzięki Microsoft Exchange przychodzące do użytkownika przesyłki poczty elektronicznej kierowane są do foldera znajdującego się na pulpicie systemu. Ułatwione jest ich przeglądanie i segregowanie.
Po poprawnym zainstalowaniu modemu i protokołu TCP/IP możliwe jest połączenie z siecią The Microsoft Network. Użytkownik rejestruje się na jednym z wielu rozsianych po całym świecie serwerów (najbliższy znajduje się niestety aż w Berlinie) i może skorzystać z następujących usług:
uczestnictwo w dyskusjach przez Internet „na żywo” z wieloma uczestnikami jednocześnie (listy dyskusyjne),
wymiana informacji w tzw. biuletynach tematycznych; można dzięki temu zadawać i odpowiadać na pytania dotyczące interesujących użytkownika tematów,
korzystanie z ogromnej biblioteki oprogramowania; poszczególne programy można łatwo odszukać, a następnie skopiować na dysk lokalny.
Mimo, że powyższe usługi, oferowane przez programy dołączone do systemu operacyjnego, są bardzo przydatne, to brakuje wśród nich możliwości przeglądania stron WWW. Aby móc skorzystać z tej ogromnej składnicy informacji trzeba zainstalować odpowiedni program. Najlepsze, dostępne obecnie na rynku oprogramowanie tego typu to Netscape Communicator oraz Microsoft Internet Explorer (rys.3).
rys. 3
Oprócz wcześniej opisanych usług programy te oferują znacznie więcej. Są świetnymi przeglądarkami stron WWW, a Netscape zawiera również moduł do samodzielnego tworzenia doskonałych stron. Przeglądarki odczytują bardzo dużą liczbę formatów graficznych i dźwiękowych, dzięki czemu odwiedzane strony mogą być bardzo bogate. Obsługa języka VRML pozwala przeglądać strony trójwymiarowe, a RealAudio umożliwia rozmowę przez Internet tak, jak przez zwykły telefon. Poczta elektroniczna zawiera opcje ułatwiające segregowanie i archiwizację listów, a także ich szyfrowanie.
3. Protokoły komunikacyjne
Przedstawione w tym rozdziale protokoły są najważniejszymi protokołami wykorzystywanymi przez system operacyjny Windows 95. NetBIOS i jego rozszerzona wersja NetBEUI są podstawą sieci The Microsoft Networks, a rodzina protokołów TCP/IP umożliwia dostęp do sieci rozległych.
3.1. Oprogramowanie sieciowe NetBIOS
3.1.1. Informacje podstawowe
Prace nad protokołem Network Basic Input Output System (NetBIOS) rozpoczęła firma IBM w roku 1984, a nieco później dołączyła do niej firma Microsoft [8]. W wyniku współpracy powstał protokół NetBIOS/NetBEUI, którego model (w odniesieniu do modelu OSI) przedstawiono na rys.4. Zadaniem NetBIOS jest zapewnienie komunikacji w środowisku małej lub średniej sieci lokalnej. Wykorzystywany jest w sieciowych systemach operacyjnych firm Microsoft i IBM:
Windows for Workgroups,
Windows NT,
Windows 95,
IBM Lan Server.
rys. 4
Poszczególne elementy środowiska protokołu NetBIOS wyjaśnione są poniżej:
redirector - służy do rozdzielania żądań lokalnych od sieciowych; żądania sieciowe przesyłane są do serwera sieciowego, a komendy lokalne przekazywane są lokalnemu systemowi operacyjnemu,
Server Message Box (SMB) - dostosowuje język i format używany przez dany komputer na format komunikacji sieciowej,
NetBIOS - protokół warstwy sesji; ustanawia sesje między komputerami i zarządza tymi połączeniami,
NetBEUI - zapewnia usługi transportowe żądane przez NetBIOS,
NDIS (Network Driver Interface Specyfication) - interfejs pozwalający obsługiwać także inne protokoły (np. TCP/IP) na jednej karcie sieciowej.
3.1.2. Rodzaje obsługi
System NetBIOS pozwala korzystać z czterech podstawowych rodzajów obsługi:
obsługi nazw;
obsługi sesji,
obsługi datagramów,
poleceń ogólnych.
Rys. 5 przedstawia schemat połączeń między poszczególnymi rodzajami obsługi.
rys. 5
Powyższy schemat nie uwzględnia interfejsu nazywanego blokiem komunikatów serwera (Server Message Block - rys.4). Interfejs ten, umieszczony w warstwie prezentacji, umożliwia programom korzystanie ze wspólnych plików i drukarek. Jego zadania to: tworzenie katalogów, otwieranie i czytanie plików, a także otwieranie, pisanie i zamykanie bufora drukowania.
3.1.3. Polecenia NetBIOS
Wszelkie zasoby systemu NetBIOS posiadają nazwy identyfikowane przez obsługę nazw. Nazwy mają swoje ograniczenia:
maksymalna długość nazwy to 15 znaków alfanumerycznych,
małe i wielkie litery są rozróżniane,
nazwa nie może zaczynać się od znaku gwiazdki ani trójki liter: IBM.
Są dwa rodzaje nazw: nazwy unikatowe i grupowe. W danej sieci lokalnej nie mogą istnieć dwa zasoby posiadające taką samą nazwę unikatową. Jeśli zaś zasoby posiadają taką samą nazwę grupową oznacza to, że przynależą do tej samej grupy.
Obsługa nazw wykorzystuje cztery polecenia wewnętrzne NetBIOS:
ADD_NAME - dodaj nazwę unikatową,
ADD_GROUP_NAME - dodaj nazwę grupową,
DELETE_NAME - skasuj nazwę,
FIND_NAME - sprawdź, czy nazwa jest zarejestrowana.
Gdy proces chce otrzymać nazwę unikatową musi najpierw sprawdzić, czy nazwa unikatowa nie jest już używana w sieci jako unikatowa lub grupowa. W tym celu wysyła polecenie ADD_NAME do wszystkich węzłów sieci i oczekuje odpowiedzi. Jeśli żaden węzeł nie zgłosi zastrzeżenia to dana nazwa jest rejestrowana w sieci. Nazwa grupowa może zostać odrzucona, jeśli jest już używana jako nazwa unikatowa. Każda nazwa otrzymuje identyfikujący ją numer nazwy lokalnej, który przekazywany jest wraz z innymi poleceniami NetBIOS.
Obsługa sesji zapewnia transmisję komunikatów w NetBIOS. Dane przesyłane są także jako komunikaty o długości do 131071 bajtów.
Na potrzeby obsługi sesji zdefiniowano polecenia:
CALL - wywołaj - otwarcie aktywne,
LISTEN - słuchaj - otwarcie bierne,
SEND - wyślij dane,
SEND_NO_ACK - wyślij dane bez potwierdzenia,
RECEIVE - odbierz dane,
RECEIVE_ANY - odbierz dane,
HANG_UP - zakończ sesję,
SESSION_STATUS - podaj stan sesji.
NetBIOS korzysta z połączeniowego systemu klient-serwer. Najpierw serwer wydaje polecenie LISTEN, następnie klient łączy się z serwerem, wysyłając CALL. Wraz z poleceniem LISTEN serwer przekazuje ogólnie znany adres lokalny oraz adres zdalnego klienta. Jeśli jako adres zdalnego klienta serwer poda gwiazdkę, dowolny klient może połączyć się z serwerem. Po nawiązaniu połączenia procesy otrzymują numer sesji lokalnej, którym będą posługiwały się podczas przesyłania danych za pomocą poleceń SEND i RECEIVE. Po otrzymaniu polecenia SEND NetBIOS przesyła dane i czeka na potwierdzenie odbioru tych danych przez polecenie RECEIVE. Dopiero wtedy zwracane jest sterowanie do obu procesów. Można skorzystać z polecenia SEND_NO_ACK, które nie czeka na potwierdzenie odbioru danych. Jeśli proces nie chce określać od kogo ma otrzymać komunikat używa polecenia RECEIVE_ANY.
Datagramy są krótkimi komunikatami, przesyłanymi między procesami bez uprzednio nawiązanego połączenia. Maksymalna wielkość datagramu jest ograniczona i zależy od implementacji NetBIOS.
Istnieją cztery polecenia związane z obsługą datagramów:
SEND_DATAGRAM - wyślij datagram,
SEND_BROADCAST_DATAGRAM - wyślij datagram do wszystkich węzłów,
RECEIVE_DATAGRAM - odbierz datagram,
RECEIVE_BROADCAST_DATAGRAM - odbierz datagram rozprzestrze-niany do wszystkich węzłów.
Wraz z poleceniem SEND_DATAGRAM przekazywana jest nazwa odbiorcy datagramu. Może to być nazwa grupowa, wtedy wszyscy członkowie grupy otrzymają dany komunikat. Jeśli proces wyda polecenie SEND_BROADCAST_DATAGRAM, to wszystkie procesy, które wysłały polecenie RECEIVE_BROADCAST_DATAGRAM otrzymają wysłany datagram.
Dodatkowo zdefiniowano w NetBIOS kilka poleceń ogólnych:
RESET - odtwórz stan początkowy systemu NetBIOS,
CANCEL - skasuj polecenie asynchroniczne,
ADAPTER_STATUS - pobierz stan karty sieciowej,
UNLINK - odłącz od serwera inicjującego działanie stacji roboczej.
Polecenie CANCEL ma sens tylko w tych implementacjach NetBIOS, które umożliwiają wykonywanie asynchroniczne poleceń sieciowych.
3.2. Rodzina protokołów TCP/IP
3.2.1. Informacje podstawowe
Oficjalna nazwa protokołów TCP/IP to „rodzina protokołów DARPA Internet”. Zostały one utworzone wraz z powstaniem sieci ARPANET w latach siedemdziesiątych. Sieć ARPANET rozrosła się do rozmiarów globalnej sieci Internet, ale TCP/IP obsługuje ją prawie w niezmienionej formie. Swój sukces rodzina TCP/IP zawdzięcza następującym właściwościom:
niezależność od producentów,
możliwość implementacji dla różnych rodzajów komputerów,
możliwość używania zarówno w sieciach lokalnych, jak i rozległych,
Powiązania między protokołami rodziny TCP/IP przedstawia rys.6. Aby nie zmniejszać czytelności rysunku nie uwzględniono na nim mniej ważnych protokołów. Protokoły TCP/IP opisane są dokładnie w dokumentach pt. Request for Comments (RFC).
rys. 6
3.2.2. Warstwy
Warstwa kanałowa Internetu, zawierająca warstwy OSI: fizyczną i łącza danych, wykorzystuje wielką liczbę różnych rodzajów połączeń, poczynając od standardu Ethernet, poprzez telefoniczne linie dzierżawione i zwykłe, łącza szeregowe RS-232, transmisję radiową, aż po łącza satelitarne.
Warstwę sieciową stanowi międzysieciowy protokół IP, obsługujący doręczanie pakietów dla protokołów TCP, UDP i ICMP. System ten bazuje na zawodnym i bezpołączeniowym przesyłaniu datagramów. Każdy datagram zawiera adres źródłowy i docelowy. Nie ma żadnej gwarancji, że datagram zostanie doręczony i nie zapewnia się kolejności odbierania równej kolejności nadawania datagramów. Jeśli okaże się, że datagram zawiera błąd (na podstawie obliczanej sumy kontrolnej), to jest on po prostu usuwany z sieci. Warstwa IP zakłada, że wyższa warstwa prześle pakiet ponownie. Każdy datagram traktowany jest w tym protokole niezależnie od pozostałych. Mogą one być składane i rozkładane na inne jednostki danych w wyższych warstwach. Tam też może być zaimplementowana procedura transmisji połączeniowej i poprawności przesyłu.
Warstwa IP zajmuje się wyborem trasy w Internecie oraz rozdrabnianiem zbyt dużych datagramów dla danej sieci. Datagramy takie są scalane na miejscu swojego przeznaczenia.
Nagłówek datagramu IP przedstawia rys.7.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
wersja |
IHL |
typ usługi -TOS |
całkowita wielkość |
|
identyfikacja |
flg |
fragment |
||
TTL |
protokół |
suma kontrolna |
||
adres źródła |
||||
adres przeznaczenia |
||||
opcje |
rys. 7
Poszczególne pola nagłówka IP określają następujące parametry:
wersja - jest to numer wersji protokołu IP, określający jego format; aktualnie stosowana jest wersja 4,
IHL - Internet Header Length, wielkość nagłówka, podawana ilością słów 32-bitowych; najmniejsza możliwa wartość to 5 (20 bajtów),
typ usługi - TOS - pole to wykorzystywane jest tylko w przypadku przechodzenia datagramu przez specyficzne sieci; określa ono jakość usług wymaganą dla datagramu; można np. spróbować zwiększyć czas przesyłu kosztem pewności transmisji,
całkowita wielkość - określa wielkość datagramu IP w bajtach; wielkość ta zawiera nagłówek i dane; pole to ma 16 bitów długości, można więc określić maksymalną wielkość na 65535, co jest jednak niepraktyczne - wiele sieci nie będzie mogło obsłużyć tak dużego datagramu; zakłada się, że wszystkie stacje pośrednie potrafią obsługiwać datagramy o wielkości 576 bajtów,
identyfikacja - wartość pomocna przy składaniu fragmentów datagramu,
flg - pole przeznaczone dla stacji rozdrabniającej i łączącej rozdrobnione datagramy; zawiera 3 bity: bit nr 0 jest zarezerwowany i zawsze powinien być wyzerowany, bit nr 1 określa czy dany datagram może być podzielony (ma wtedy wartość 1), drugi bit określa, czy jest to ostatnia część rozdrobnionego datagramu (=0), czy też nie (=1),
fragment - jeśli datagram był podzielony w czasie transmisji, wartość ta określa, gdzie dane przesyłane w aktualnym datagramie powinny być umieszczone w datagramie oryginalnym,
TTL - wartość określa maksymalny czas, przez który datagram może pozostawać w sieci; po każdym przejściu przez moduł odczytujący nagłówek IP (np. brama), wartość ta zmniejszana jest o jeden nawet wtedy, gdy trwa to krócej niż 1 sekundę; gdy TTL osiągnie wartość 0, datagram usuwany jest z sieci;
protokół - numer protokołu wyższej warstwy, do której kierowany jest datagram,
suma kontrolna - suma kontrolna nagłówka IP, służąca do sprawdzenia poprawności transmisji; ponieważ nagłówek jest modyfikowany w czasie swej podróży, suma musi być za każdym razem przeliczana i sprawdzana,
adres źródła - 32 bitowy adres źródła datagramu,
adres przeznaczenia - 32 bitowy adres przeznaczenia datagramu,
opcje - opcje datagramu; pole o zmiennej wielkości
Protokół IP opisany jest w dokumencie RFC 791 [5].
W warstwie sieciowej znajdują się także trzy inne protokoły: ICMP, ARP i RARP. Protokoły ARP i RARP służą do odwzorowywania adresów sprzętowych - np. w sieci ethernetowej odwzorowują adresy karty sieciowej na adresy internetowe i odwrotnie.
Protokół ICMP to protokół międzysieciowych komunikatów sterujących (Internet Control Message Protocol), obsługujący zawiadomienia o błędach i informacje sterujące przesyłane między bramami a stacjami. Opisuje go dokument RFC 792 [6]. Postać komunikatu ICMP ilustruje rys.8. Ponieważ komunikaty ICMP przesyłane są wewnątrz datagramów IP, w ramce znajduje się nagłówek IP. Postać nagłówka IP przedstawiona jest na rys.7. Pole protokół wypełnione jest wartością oznaczającą protokół ICMP: 1.
nagłówek IP |
nagłówek ICMP |
dane ICMP |
rys. 8
Nagłówek ICMP ma długość 8 bajtów (rys. 9), a jego poszczególne pola oznaczają:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
typ |
kod |
suma kontrolna |
identyfikator |
sekwencja |
|
dane |
rys. 9
typ - typ komunikatu ICMP;
kod - kod, odpowiedni dla typu ICMP,
suma kontrolna - służy do określenia poprawności przesyłania komunikatu ICMP, nagłówka wraz z jego danymi,
identyfikator - identyfikator rodzaju procesu, wysyłającego komunikat ICMP; nie musi być wypełniany;
sekwencja - numer kolejny wysyłanego przez proces komunikatu ICMP; nie musi być wypełniany,
dane - dane przesyłane wraz z nagłówkiem ICMP; ich wielkość może być różna - zależna od typu komunikatu ICMP.
Możliwe są następujące typy komunikatów ICMP:
nazwa komunikatu: |
wartość pola typ nagłówka ICMP: |
Echo Reply |
0 |
Destination Unreachable |
3 |
Source Quench |
4 |
Redirect |
5 |
Echo |
8 |
Time Exceeded |
11 |
Parameter Problem |
12 |
Timestamp |
13 |
Timestamp Reply |
14 |
Information Request |
15 |
Information Reply |
16 |
Znaczenie poszczególnych typów:
Echo i Echo Replay - stacja wysyła komunikat ICMP Echo po to, by sprawdzić, czy istnieje połączenie z inną stacją; jeśli jakaś stacja lub brama otrzyma taki komunikat protokół ICMP zobowiązany jest wysłać do stacji żądającej odpowiedzi komunikat Echo Replay,
Destination Unreachable - stacja przeznaczenia jest nieosiągalna; komunikat taki przesyłany jest do stacji źródłowej datagramu, który nie może osiągnąć swego celu; wraz z nagłówkiem ICMP przesyłany jest nagłówek IP i pierwsze 8 bajtów odebranego datagramu; pole kod nagłówka określa powód:
znaczenie: |
wartość: |
sieć nieosiągalna |
0 |
stacja nieosiągalna |
1 |
protokół nieosiągalny |
2 |
port nieosiągalny |
3 |
niezbędna fragmentacja a bit DF nagłówka IP = 1 |
4 |
Source routing nie powiódł się |
5 |
Source Quench - jeśli datagramy przychodzą do bramy lub stacji końcowej zbyt szybko i nie nadążają one ich przetwarzać, wysyłają do stacji źródłowej datagramów ten komunikat,
Redirect - komunikat wysyłany przez bramę, która określi, że stacja wysyłająca datagram znajduje się w sieci obsługiwanej przez tę właśnie bramę, a brama otrzymuje datagramy tej stacji od bramy innej sieci; pozwala to ominąć transmisję dokonywaną drogą okrężną,
Time Exceed - komunikat wysyłany stacji źródłowej datagramu, którego pole nagłówka TTL osiągnęło wartość 0; możliwe są dwie wartości pola kod: 0, gdy czas życia pakietu osiągnął 0 w czasie transmisji, 1, gdy przekroczony został czas oczekiwania na części rozdrobnionego datagramu,
Parameter Problem - jeśli podczas przetwarzania nagłówka IP okaże się, że wystąpił jakiś błąd, to do stacji źródłowej wysyłany jest komunikat Parameter Problem, dodatkowo nagłówek IP i pierwsze 8 bajtów błędnego pakietu,
Timestamp i Timestamp Replay - para komunikatów służąca do synchronizacji procesów; stacja, która otrzyma komunikat Timestamp powinna odesłać Timestamp Replay, zawierający ilość milisekund jaka upłynęła od północy czasu uniwersalnego na tej stacji,
Information Request i Information Replay - para komunikatów wykorzystywana do określenia adresów innych stacji znajdujących się w sieci lokalnej; stacja wysyła Information Request z adresem przeznaczenia wyzerowanym - inna stacja w tej sieci odsyła komunikat Information Replay ze swoim adresem.
Dokumentacja dotycząca protokołów TCP/IP określa, że nie powinny być generowane komunikaty ICMP z powodu błędu w innym komunikacie ICMP.
Warstwa transportowa TCP/IP to protokoły UDP i TCP. Pośredniczą one w nawiązywaniu połączenia i wymianie danych między procesami użytkownika. Obydwa te protokoły korzystają z protokołu IP (rys. 6).
Jeśli proces wymaga strumieniowej transmisji połączeniowej, równoczesnej i niezawodnej powinien skorzystać z protokołu TCP. Moduł TCP zajmuje się ustanawianiem i kończeniem połączeń między procesami, ustalaniem właściwej kolejności pakietów, które mogą nadchodzić w przypadkowej kolejności, niezawodnością obsługi oraz bezpośrednim sterowaniem przepływem. Protokół UDP określa natomiast bezpołączeniową i zawodną transmisję datagramów.
Zarówno protokół TCP, jak i UDP używają do łączenia procesów tzw. numerów portów. Dzięki nim na jednej stacji może korzystać z sieci (i z jednego adresu IP) wiele procesów, co więcej jeden proces może nawiązać wiele połączeń na różnych portach. Numer portu umieszczany jest w nagłówku TCP lub UDP, a wraz z adresem IP i rodzajem protokołu stanowi swoistą, unikatową nazwę końcówki połączenia.
Protokół TCP opisany jest w RFC 793, a UDP w RFC 768.
3.2.3. Adresy Internetu
Adres w Internecie zajmuje 32 bity i składa się z części identyfikującej sieć i stację. Każda stacja posiada adres unikatowy, którego przydziałem zajmuje się Sieciowe Centrum Informacyjne (Network Information Center - NIC) [3]. Jednak pojedyncza stacja może mieć więcej niż jeden adres internetowy. Istnieją cztery klasy adresów IP: A, B, C, D (rys.10).
klasa A |
|||||||
|
7 bitów |
24 bity |
|||||
0 |
id. sieci |
id. stacji |
|||||
|
|||||||
klasa B |
|||||||
|
14 bitów |
16 bitów |
|||||
1 |
0 |
id. sieci |
id. stacji |
||||
|
|||||||
klasa C |
|||||||
|
21 bitów |
8 bitów |
|||||
1 |
1 |
0 |
id. sieci |
id. stacji |
|||
|
|||||||
klasa D |
|||||||
|
28 bitów |
||||||
1 |
1 |
1 |
0 |
adres grupowy |
rys. 10
Adresy klasy A przydzielone są sieciom złożonym, w których występuje bardzo dużo komputerów na pojedyncze sieci. W przypadku sieci złożonych, składających się z wielu małych sieci bardziej odpowiednia będzie klasa C. Klasa D pozwala przesyłać dane jednocześnie dla wielu użytkowników, mających ten sam adres. Centrum NIC przydziela tylko klasę i identyfikator sieci adresu IP, identyfikatory stacji przydzielane są indywidualnie. Możliwe jest takie zorganizowanie sieci, że część przeznaczona na identyfikatory stacji zostanie podzielona na identyfikator podsieci i identyfikator stacji. Ułatwia to identyfikowanie stacji w poszczególnych sieciach lokalnych.
Adres IP przedstawiany jest najczęściej w postaci 4 liczb oddzielonych kropkami (np. 198.105.232.1). Dzięki internetowej usłudze DNS (Domain Name Service) liczbowe adresy mogą mieć swoje odpowiedniki tekstowe, dużo łatwiejsze do zapamiętania (np. ftp.microsoft.com).
4. Programowanie sieciowe w Windows 95
Dostępność wielu różnych bibliotek funkcji dla systemu Windows 95 daje możliwość tworzenia programów, które będą pracowały w wielu typach sieci komputerowych. Dla programowania w sieciach lokalnych przeznaczone są biblioteki: API Win32, biblioteka LAN Manager, system Network Dynamic Data Exchange. Specyfikacja Winsock wykorzystywana jest przy tworzeniu aplikacji sieciowych korzystających z protokołu TCP/IP.
4.1. Sieciowe funkcje API Win32
4.1.1. Dostęp do zasobów sieciowych przy użyciu protokołu NetBIOS
Biblioteka API Win32 [2] zawiera zestaw funkcji, które pozwalają dołączyć do lokalnego systemu zasoby znajdujące się w sieci za pomocą protokołu NetBIOS. Aby uzyskać połączenie z innym systemem plików lub drukarką należy użyć funkcji WNetAddConnection2(), której argumentami są struktura NETRESOURCE oraz hasło dostępu do danego zasobu, nazwa użytkownika starającego się o dostęp i opcje określające sposób dostępu. Struktura NETRESOURCE zawiera dane określające typ zasobu (pliki, drukarki, wszystko), nazwę własną zasobu i nazwę, którą zasób przyjmie w systemie lokalnym (np. F:). Po poprawnym połączeniu można już korzystać z sieciowych plików lub drukarek. Aby zakończyć połączenie używa się funkcji WNetCancelConnection2(). Zestaw funkcji wzbogacony jest o procedury zwracające kompletne informacje o rodzajach zasobów znajdujących się i dostępnych aktualnie w sieci. Są to funkcje: WNetOpenEnum() oraz WNetEnumResource(). Istnieje także dialogowa wersja funkcji do otwierania połączenia: WNetConnectionDialog(). Powoduje ona otwarcie okna dialogowego, dzięki któremu użytkownik programu może sam określić jakie zasoby i w jaki sposób dołączyć do systemu lokalnego. Powyższe funkcje nie pozwalają na bezpośrednią wymianę danych między programami pracującymi w różnych miejscach sieci, ale umożliwiają korzystanie ze zdalnych plików i drukarek.
4.1.2. Wymiana danych przez łącza nazwane i skrzynki „mailslot”
Przesłanie informacji między komputerami za pomocą plików często niepotrzebnie wydłużać będzie czas transmisji. Aby bezpośrednio do pamięci przekazywać dane można skorzystać z łącz nazwanych lub mailslotów.
Łącza nazwane to usługa systemowa polegająca na dwukierunkowym połączeniu jednego procesu nadrzędnego i jednego lub wielu procesów podrzędnych. Proces nadrzędny (serwer) tworzy łącze wywołując funkcję CreateNamedPipe(), podając jednocześnie nazwę łącza oraz ile łącze może mieć kanałów. Gdy proces podrzędny (klient) uruchomi funkcję CreateFile() lub CallNamedPipe() z nazwą łącza jako argumentem, ustanowione zostanie połączenie między serwerem i klientem, wykorzystujące jeden z kanałów łącza. Definiując większą liczbę kanałów można wykorzystać jedno łącze do komunikacji między jednym serwerem i wieloma klientami. Wymiana danych wykonywana jest za pomocą tych samych funkcji, które służą do operacji na plikach: do czytania z łącza ReadFile(), a do pisania WriteFile(). Oczywiście w przypadku łączy dane przekazywane są z pamięci do pamięci, z pominięciem systemu plików. Aby odczytać z łącza dane bez czyszczenia bufora łącza należy użyć funkcji PeekNamedPipes(). Po zakończeniu współdziałania łącze jest zamykane po wykonaniu funkcji: CloseHandle() po stronie klienta i DisconnectNamedPipe() po stronie serwera. Każdy proces może otrzymać informacje o łączu jeśli zna jego nazwę. Po uruchomieniu funkcji GetNamedPipeInfo() zwracana jest informacja o typie łącza, wielkościach buforów do nadawania i odbierania danych oraz liczba kanałów tego łącza.
Mailslot to bardzo podobna do łączy nazwanych usługa systemowa. Różnica polega na tym, że w tym przypadku transmisja danych jest jednokierunkowa. Aplikacja-serwer tworzy swój mailslot o pewnej nazwie (funkcja CreateMailslot()) i tylko ona może z niego czytać informacje (ReadFile()). Inne aplikacje (także te znajdujące się na innych komputerach sieci) mogą pisać do skrzynki serwera, pod warunkiem, że znają nazwę mailslota. Dostęp do niego uzyskują poprzez funkcję CreateFile(), a informacje zapisuje funkcja WriteFile. Kolejno przesyłane komunikaty są w skrzynce składowane. Właściciel skrzynki sprawdza, czy coś w niej się znajduje wywołując funkcję GetMailslotInfo(). Jeśli wiele procesów w obrębie jednej domeny sieci utworzyło mailsloty o takich samych nazwach, to przesyłany komunikat trafi do wszystkich tych skrzynek jednocześnie. W odróżnieniu od łącz nazwanych, komunikacja typu mailslot jest datagramowa - proces wysyłający informację nie otrzymuje więc potwierdzenia o poprawnym jej odebraniu.
4.2. Specyfikacja Windows Sockets
Dokumentacja Windows Sockets [1] definiuje interfejs programowania sieciowego, opierając się na pomyśle gniazd (ang. sockets) zastosowanym po raz pierwszy w UNIX-ie - BSD 4.3. Schemat korzystania z gniazd pozostał w systemach okienkowych taki sam, dodano natomiast funkcje wykorzystujące wielowątkowość Windows oraz sterowanie komunikatami. Ponieważ zestaw funkcji Windows Sockets jest interfejsem, to możliwe jest programowanie za jego pomocą w różnych rodzajach sieci (jeśli tylko dostępna jest odpowiednia biblioteka łącząca protokół sieci z gniazdami). Zarówno wersja UNIX-owa, jak i pierwsza specyfikacja Windows Sockets 1.1 [2] pozwalały wykorzystywać jedynie protokół TCP/IP. Co gorsza, wersja dla Windows umożliwiała użycie tylko dwóch typów gniazd (SOCK_STREAM i SOCK_DGRAM), co znacznie zmniejszało funkcjonalność biblioteki. Najnowsza dokumentacja Windows Sockets (wersja 2) nie ustanawia żadnych ograniczeń co do protokołu, dodano także do biblioteki obsługę kilku znanych z UNIX-a typów gniazd i protokołów. Autorzy specyfikacji (m.in.: Intel, Microsoft, FTP Software, Novell) zwracają uwagę, że jest ona opisem, który powinien posłużyć producentom programowania do tworzenia własnych, aktualniejszych wersji bibliotek.
4.2.1. Inicjalizacja biblioteki
Biblioteka funkcji Windows Sockets znajduje się w module dołączanym dynamicznie - winsock.dll (Dynamic Link Library). W czasie kompilacji programu niezbędna jest biblioteka umożliwiająca łatwe odwołania do funkcji DLL - ws2_32.lib oraz plik winsock2.h (starsza wersja: winsock.h), zawierający definicje. Aby uzyskać dostęp do biblioteki DLL należy ją zainicjalizować funkcją WSAStartup(). Jako argument przyjmuje ona numer wersji specyfikacji z jaką aplikacja chciałaby współpracować. WSAStartup() zwraca maksymalny numer wersji, który biblioteka potrafi obsługiwać. Zwracane są także takie informacje jak: maksymalna ilość wykorzystywanych jednocześnie gniazd i maksymalny rozmiar datagramowego komunikatu. Funkcja WSAStartup() powinna być pierwszą funkcją Windows Sockets wywoływaną w programie. Po zakończeniu współpracy z biblioteką należy ją zamknąć za pomocą funkcji WSACleanup().
4.2.2. Gniazda
Podstawą działania Windows Sockets, zapożyczoną z UNIX-a [9], są gniazda. Gniazdo jest końcówką połączenia między komunikującymi się stacjami. Każda transmisja danych użytkownika musi zostać poprzedzona utworzeniem gniazda, zarówno po stronie nadawczej, jak i odbiorczej (oczywiście, jeżeli obie strony korzystają z mechanizmów Windows Sockets). Gniazdo powstaje po wywołaniu funkcji socket(). Przyjmuje ona jako argumenty dane określające typ połączenia: format adresu, rodzaj gniazda oraz protokół, który będzie przez gniazdo używany. Najczęściej wykorzystywanym formatem adresu jest format ARPA Internet - AF_INET. Wersja Windows Sockets 1.1 umożliwiała przesyłanie danych tylko dwoma sposobami: gniazdem połączeniowym (SOCK_STREAM) i datagramowym (SOCK_DGRAM). Wersja 2.2 posiada kilka dodatkowych typów gniazd, między innymi SOCK_RAW, który umożliwia dostęp do całego odbieranego pakietu (z nagłówkami) i pozwala na większą swobodę w dostępie do nagłówków pakietu wysyłanego. Dzięki temu typowi gniazda możliwa jest na przykład obsługa kontrolnego protokołu ICMP. Do transmisji zwykłych danych wykorzystywane są jednak gniazda SOCK_STREAM i SOCK_DGRAM. SOCK_STREAM używa protokołu TCP [7], więc przesyłanie danych jest bezpieczniejsze, gdyż poprawne odebranie pakietu po stronie odbiorczej jest potwierdzane, pakiety przesyłane są sekwencyjnie i niemożliwe jest ich zduplikowanie. Gdy aplikacja wykorzystuje gniazdo typu SOCK_DGRAM i protokół UDP [4] transmisja jest szybsza, lecz z powodu braku potwierdzania - nie wiadomo, czy komunikat dotarł do adresata, a gdy dotarł - nie wiadomo, czy nie został podwojony lub wyprzedził w transmisji inne komunikaty. Program wywołujący socket() może sam określić rodzaj protokołu lub pozwolić funkcji wybrać domyślny dla danego typu gniazda protokół. W przypadku gniazd SOCK_STREAM i SOCK_DGRAM wybrane zostaną odpowiednie protokoły dla danego typu połączenia (IPPROTO_TCP i IPPROTO_UDP). W przypadku protokołu surowego (SOCK_RAW) należy samemu określić protokół. Można to zrobić podając funkcji odpowiednią stałą (np. IPPROTO_ICMP) lub wywołując funkcję getprotobyname(), która zwraca numer protokołu akceptowany przez socket(). Jako argument getprotobyname() przyjmuje nazwę protokołu w postaci ciągu znaków (może to być więc np. „icmp”).
Jeśli funkcja socket() zakończy z powodzeniem działanie zwraca aplikacji wywołującej deskryptor gniazda jako wartość typu SOCKET. Wartością tą należy posługiwać się podczas wszelkich odwołań do funkcji wykorzystujących zainicjalizowaną transmisję. Jeśli podane parametry są błędne, brakuje obsługi określonych protokołów lub przekroczono dopuszczalną liczbę gniazd, to socket() zwróci wartość INVALID_SOCKET oznaczającą błąd. Gniazda znajdujące się po obydwu stronach połączenia powinny używać tego samego typu gniazda.
Specyficzne dla Windows Sockets są gniazda blokowane i nieblokowane. Dzięki wykorzystaniu systemowego mechanizmu sterowania komunikatami można zlecić bibliotece Windows Sockets jakąś operację (np. odbieranie danych) i w czasie oczekiwania na napływanie tych danych z zewnątrz, wykonywać inne zadania. Gdy na wejściu pojawią się oczekiwane dane Windows Sockets prześle do systemu zdefiniowany wcześniej przez użytkownika komunikat. Jednakże wszystkie gniazda biorące udział w takiej operacji powinny być nieblokowane. Gdy gniazdo jest utworzone, działa w trybie blokowania, co można zmienić za pomocą funkcji ioctlsocket() lub WSAAsyncSelect(). Funkcja ioctlsocket() przyjmuje komendę FIONBIO ze wskaźnikiem na zmienną typu u_long różną od zera. Funkcja WSAAsyncSelect() odblokowuje gniazdo i pozwala określić jaki komunikat zostanie przesłany do systemu, gdy wystąpi jakaś akcja związana z gniazdem (np.: zewnętrzne gniazdo spróbuje się skontaktować, pojawią się dane do odczytu, bufor do wysyłania został opróżniony - można wysyłać dane dalej). Gniazdo w czasie swojego istnienia może wielokrotnie zmieniać stan z blokowanego na nieblokowane i odwrotnie. Aby ustawić stan gniazda na nieblokowane trzeba znów użyć funkcji ioctlsocket() z komendą FIONBIO i zmienną u_long równą zero. Gdy gniazdo pracuje w trybie blokowanym, aplikacja może sprawdzić jaki jest jego stan i dopiero wtedy wykonać odpowiednią operację (np. czytanie z gniazda). Do określania stanu blokowanego gniazda służy funkcja select(). Pozwala ona sprawdzić czy w zestawie gniazd znajdują się takie, z których można czytać, do których można pisać i takie, które nie działają prawidłowo.
Opcje dotyczące gniazda mogą być zmieniane przy użyciu funkcji setsockopt(). Dzięki temu można rozsyłać komunikaty w sieci (SO_BROADCAST), zmienić wielkość buforów: nadającego i odbierającego (SO_SNDBUF i SO_RCVBUF) i, co bardzo ważne, ustawić przynajmniej niektóre składniki nagłówka IP wysyłanego pakietu.
4.2.3. Dostęp do sieci
W czasie, gdy gniazdo jest tworzone przez funkcję socket(), należy określić jego pełną nazwę. Nazwa, w przypadku gniazd typu SOCK_STREAM i SOCK_DGRAM, składa się z trzech elementów: adresu IP stacji, protokołu oraz numeru portu. Dowiązanie adresu i portu do gniazda wykonuje funkcja bind(), przyjmująca jako argumenty deskryptor gniazda oraz strukturę typu sockaddr, która ma następującą postać:
struct sockaddr
{
u_short sa_family;
char sa_data[14];
};
Zmienna sa_family przyjmuje wartość formatu adresu (najczęściej AF_INET), natomiast sa_data to 14 bajtów zawierających adres i numer portu dla gniazda. Wypełnienie tej tablicy ułatwia struktura sockaddr_in, którą należy następnie zrzutować (przekonwersować) na sockaddr. Struktura sockaddr_in wygląda następująco:
struct sockaddr_in
{
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
Zmienne sin_port i sin_addr określają numer portu i adres IP. Tablica sin_zero dodana jest, aby zgadzały się długości struktury sockaddr_in i sockaddr. Struktura sin_addr zdefiniowana jest jako unia:
struct in_addr
{
union
{
struct
{
unsigned char s_b1,s_b2,s_b3,s_b4;
} S_un_b;
struct
{
unsigned short s_w1,s_w2;
} S_un_w;
unsigned long s_addr;
} S_un;
};
Dzięki temu można uzyskać dostęp do adresu IP na trzy sposoby.
Aby zamienić łańcuch znaków określający adres IP, dany w postaci „kropkowej”, na wartość typu unsigned long, należy użyć funkcji inet_addr(), np.:
unsigned long adres=inet_addr(”192.100.100.1”);
Zmienną adres z powyższego przykładu można podstawić pod zmienną s_addr struktury sockaddr_in. Do własnych celów aplikacje powinny łączyć się, wykorzystując porty o numerach większych od 1024. Numery niższe mogą być zarezerwowane dla tzw. dobrze znanych usług (np. FTP zawsze wykorzystuje port numer 21). Istnieje pewien problem związany z tym, że inna może być kolejność bajtów w danych komputera lokalnego i w sieci. Aby uniknąć błędów należy stosować funkcję htons(), zamieniającą, w razie potrzeby (np. na komputerach typu PC), liczbę u_short na odwrotną kolejność używaną w sieci:
u_short port=htons(10000);
Po poprawnym wypełnieniu struktury sockaddr_in można już użyć funkcji bind():
bind(sock,(struct sockaddr*)&sockaddr_in_DATA,sizeof(sockaddr_in));
Jeśli po stronie nadawczej i odbiorczej istnieją już gniazda z przydzielonymi adresami można przystąpić do połączenia. W przypadku gniazd typu SOCK_STREAM ustanawiane jest połączenie typu klient-serwer. Gniazdo typu serwer wykonuje funkcję listen(), klient natomiast connect(). Gdy listen() zwróci poprawną wartość serwer może zaakceptować połączenie funkcją accept(). Po takiej konwersacji dane mogą być przesyłane za pomocą funkcji send() i odbierane przez recv() w obydwu kierunkach.
Schemat połączenia jest następujący:
serwer
|
klient |
gniazdo1S=socket(AF_INET,SOCK_STREAM,0)
|
gniazdo1K=socket(AF_INET,SOCK_STREAM,0) |
bind(gniazdo1S,&addrS,...) |
bind(gniazdo1K,&addrK,...)
|
listen(gniazdo1S,1)
|
|
|
connect(gniazdo1K,&addrS,...)
|
gniazdo2S=accept(gniazdo1S,&addrK,...)
|
|
Argumentami funkcji listen() są: deskryptor niepołączonego gniazda oraz wartość backlog, określająca ile żądań połączenia musi nadejść do funkcji, aby zwróciła ona sterowanie aplikacji. Funkcja listen() zwraca zero, gdy pojawiły się żądania dostępu do gniazda lub SOCKET_ERROR w przypadku wystąpienia błędu.
Jeśli jakakolwiek aplikacja w sieci zna nazwę gniazda SOCK_STREAM (protokół, adres IP i port) utworzonego na innym komputerze, może spróbować się z nim skontaktować za pomocą funkcji connect(). Jako argumenty funkcja ta przyjmuje deskryptor lokalnego gniazda oraz strukturę sockaddr z adresem odległej stacji. Jeśli pod wskazanym adresem istnieje gniazdo oczekujące połączenia (funkcja listen()), to connect() zwróci 0, jeśli wystąpi błąd, to wartość powrotna będzie miała wartość SOCKET_ERROR.
Aby zatwierdzić połączenie stacja-serwer wywołuje funkcję accept(), zaraz po powrocie z funkcji listen(). Procedura accept() wymaga podania gniazda, które oczekiwało na połączenie i ewentualnie wskaźnika na strukturę sockaddr, w której funkcja zwróci parametry gniazda żądającego połączenia. Jeśli zamiast wskaźnika na sockaddr podany zostanie NULL, gniazdo akceptujące nie otrzyma informacji o gnieździe kontaktującym się. accept() zwraca wartość, będącą deskryptorem nowego gniazda - gotowego do wymiany danych z gniazdem odległym. Jeśli działanie funkcji zakończy się niepowodzeniem, zwraca ona INVALID_SOCKET.
Dzięki funkcji WSAAsyncSelect() i gniazdom nieblokującym można zdefiniować zdarzenia, które zostaną uruchomione w momencie, gdy aplikacja może uzyskać połączenie z innym komputerem. Jest to dość ważne, ponieważ w czasie oczekiwania na połączenie aplikacja może wykonywać inne zadania. Po zgłoszeniu się odpowiednich zdarzeń, program wykonuje funkcję accept() lub connect().
Do przesyłania danych służą funkcje send() oraz recv(). Funkcja send() wysyłająca dane potrzebuje następujących argumentów: deskryptora połączonego gniazda, wskaźnika typu char* na obszar pamięci, zawierający dane do przesłania oraz ilość tych danych. Aplikacja odbierająca dane wykonuje recv() i podaje podobne argumenty: swoje gniazdo, wskaźnik na miejsce w pamięci, gdzie zostaną umieszczone dane i wielkość tego bufora. Jeśli ilość danych przekracza wielkość bufora, recv() wypełni obszar i w dalszym ciągu można będzie czytać dane z tego gniazda. Funkcja zwraca ilość odczytanych bajtów. Także w przypadku funkcji send() i recv() możliwe jest zdefiniowanie zdarzeń reagujących na nadchodzące z zewnątrz dane i możliwość wysłania danych (w przypadku zastosowania gniazda nieblokującego).
Powyższy schemat połączenia i przesyłania danych obowiązuje dla gniazd typu SOCK_STREAM. Gniazda typu SOCK_DGRAM nie wymagają używania funkcji: listen(), connect() i accept(). Po utworzeniu gniazd i przydzieleniu im konkretnych adresów (za pomocą bind()) można transmitować komunikaty przy użyciu funkcji sendto() oraz recvfrom(). Funkcje te mają takie same argumenty wywołania jak funkcje send() i recv(), dodatkowo wymagają podania struktury sockaddr. Trzeba umieścić w niej adres gniazda, do którego przesyłane są dane (w przypadku sendto()) lub można w tej strukturze uzyskać adres gniazda nadającego (w przypadku recv()). Komunikaty datagramowe mają ograniczoną wielkość, którą można uzyskać ze struktury WSAData, zwróconej przez WSAStartup(). Składnik tej struktury: iMaxUdpDg, zawiera maksymalną wielkość pakietu IP. Jeśli wielkość ta zostanie przekroczona, sendto() zwróci błąd WSAEMSGSIZE. Należy pamiętać, że datagramowy mechanizm przesyłania komunikatów nie gwarantuje poprawnego odebrania danych, mimo że sendto() nie zasygnalizuje błędu. Funkcje sendto() oraz recvfrom() mogą być również używane przez gniazda typu połączeniowego. Argumenty wywołania dotyczące adresów są wtedy ignorowane. Jeżeli natomiast gniazdo datagramowe otrzyma przypisany adres gniazda zdalnego (przy pomocy funkcji connect()), to będzie mogło korzystać z funkcji send() oraz recv(), gdyż znany jest już adres docelowy.
Po zakończeniu sesji transmisyjnej gniazda powinny zostać zamknięte. Zwolnienia deskryptorów gniazd dokonuje funkcja closesocket(), przyjmująca jako parametr deskryptor zamykanego gniazda.
4.2.4. Dodatkowe funkcje usługowe
Biblioteka Windows Sockets zawiera funkcje umożliwiające łatwe zdobycie wiadomości o odległych stacjach, dostępnych protokołach i usługach.
Aby uzyskać lokalną nazwę komputera należy wywołać funkcję gethostname(). Jako argumenty przekazuje się do funkcji wskaźnik typu char* do bufora, w którym znajdzie się nazwa oraz wielkość bufora. Zwrócona nazwa jest łańcuchem zakończonym znakiem NULL i może (lecz nie musi) zawierać pełną nazwę z częścią oznaczającą domenę. Nazwa ta może być użyta jako argument wywołania funkcji gethostbyname(), w celu określenia innych parametrów stacji.
Funkcja gethostbyname() przyjmuje jako argument nazwę stacji (nawet odległej) a zwraca strukturę hostent, opisaną dalej:
struct hostent
{
char FAR * h_name;
char FAR * FAR * h_aliases;
short h_addrtype;
short h_length;
char FAR * FAR * h_addr_list;
};
Poszczególne elementy struktury zawierają:
h_name - nazwa stacji,
h_aliases - tablica nazw alternatywnych (ostatni element: NULL),
h_addrtype - format adresu,
h_length - ilość bajtów zajmowaną przez każdy adres,
h_addr_list - tablica adresów hosta; adresy dane są w sieciowej kolejności bajtów, a ostatni element to NULL.
Aplikacja nie powinna nigdy modyfikować tej struktury, gdyż jest ona alokowana tylko raz dla jednego wątku procesu. Najbezpieczniej jest przekopiować jej zawartość przed uruchomieniem innych funkcji Windows Sockets. Jeśli nie można odnaleźć w sieci stacji o podanej nazwie, gethostbyname() zwróci NULL.
Jeżeli aplikacja zna adres stacji, a nie jej nazwę może wywołać funkcję gethostbyaddr() w celu określenia dodatkowych informacji. Ta funkcja także zwraca opisaną wcześniej strukturę hostent. Adres przekazywany jest do niej w postaci ciągu bajtów. Aby stworzyć taki ciąg można skorzystać z funkcji inet_addr(), przyjmującej „kropkowy” adres internetowy, a zwracającej - adres w postaci wartości typu unsigned long. Podobnie jak poprzednio, w przypadku podania błędnego adresu, gethostbyaddr() zwraca NULL.
Jeśli aplikacja chce skorzystać z jakiegoś protokołu powinna znać jego numer. Aby uzyskać numer protokołu, znając jego nazwę wywołuje się funkcję getprotobyname(). Przyjmuje ona ciąg znaków, np.: „udp”, „tcp”,”icmp”, a zwraca strukturę protoent:
struct protoent
{
char FAR * p_name;
char FAR * FAR * p_aliases;
short p_proto;
};
Elementy struktury oznaczają:
p_name - nazwa protokołu,
p_aliases - tablica nazw alternatywnych (ostatni element: NULL),
p_proto - numer protokołu,
Istnieje także funkcja getprotobynumber() pozwalająca uzyskać powyższe informacje, jeśli aplikacja zna numer protokołu (a nie zna jego nazwy).
Obie funkcje zwrócą NULL jeśli argumenty wywołania są niepoprawne lub Windows Sockets nie obsługuje danego protokołu.
Możliwe jest także uzyskanie informacji o nazwach i portach usług dostępnych w sieci. Jeśli znana jest nazwa usługi (np.: „ftp”, „telnet”) należy skorzystać z funkcji getservbyname(), jeśli zaś znany jest jej port - getservbyport(). Funkcje te zwracają strukturę servent:
struct servent
{
char FAR * s_name;
char FAR * FAR * s_aliases;
short s_port;
char FAR * s_proto;
};
Struktura zawiera następujące informacje:
s_name - nazwa usługi,
s_aliases - tablica nazw alternatywnych (ostatni element: NULL),
s_port - numer portu, do którego trzeba się podłączyć, aby skorzystać z usługi,
s_proto - nazwa protokołu, którego należy używać, aby uzyskać dostęp do usługi.
Bardzo ważną funkcją jest WSAGetLastError(). W przypadku jeśli jakakolwiek inna funkcja (oprócz WSAStartup()) zwróci informację o błędzie, dzięki WSAGetLastError() można uzyskać numer tego błędu. Ponieważ WSAStartup() służy do zainicjalizowania biblioteki Windows Sockets, której częścią jest WSAGetLastError(), błędy powstałe przy WSAStartup() zwracane są bezpośrednio przez nią.
4.2.4. Przykładowe programy
Aby zilustrować działanie Windows Sockets przedstawione zostaną kompletne aplikacje służące do przesyłania danych. Pierwsza z nich - serwer.cpp - służy do wysłania krótkiego łańcucha znaków, a druga - klient.cpp - do jego odebrania. Chociaż w takim przypadku łatwiej byłoby wykorzystać połączenie datagramowe, programy przekażą dane za pomocą gniazd typu SOCK_STREAM. Takie podejście pozwoli zaprezentować działanie funkcji połączeniowych.
Program SERWER przedstawiony jest na wydruku 1. Zdefiniowano w nim numer lokalnego portu, na którym będą odbierane dane (PORT_LOCAL). Adres IP komputera aplikacji zapamiętany jest w definicji ADDRESS_LOCAL_TEXT.
W przypadku wystąpienia jakichkolwiek błędów wykonana zostanie procedura printError(), która przyjmuje łańcuch znaków i wyświetla go na ekranie w postaci okienka MessageBox. Błędy pochodzące od serwera mają w nagłówku tego okna napis: „SERWER - Błąd!”.
Z uwagi na niewielki rozmiar programu prawie cała jego zawartość znajduje się w głównej funkcji WinMain().
Wykorzystywane są następujące zmienne lokalne. Tablica znakowa text o wielkości 100 bajtów posłuży do zapamiętania odebranej wiadomości; zmienna typu int: fromLen - wielkość struktury sockaddr_in podawana funkcji accept(); struktura WSAData, wypełniana przez funkcję WSAStartup(); dwa gniazda sock i sock2: jedno służące do odbierania połączenia (funkcja listen()), a drugie do odbierania danych (accept() i recv()); dwie struktury sockaddr_in potrzebne przy adresowaniu: jedna do dowiązania adresu lokalnego dla gniazda sock, a druga wypełniana przez accept() adresem gniazda nadającego.
Program zaczyna się od inicjalizacji Windows Sockets funkcją WSAStartup(). Jako argument przekazywany jest żądany numer wersji 2.0 (jako wyrażenie: (0<<8)+2 ). następnie tworzone jest gniazdo sock, z którym wiązany jest lokalny adres (funkcja bind() i struktura to). Po wywołaniu funkcji listen() funkcja oczekuje na żądanie połączenia. Jeśli jakaś aplikacja, gdziekolwiek w sieci, zażąda połączenia z gniazdem sock funkcja listen() zwróci do aplikacji wartość zero. Serwer akceptuje to połączenie uruchamiając funkcję accept(), która zwraca deskryptor nowego gniazda. Zapamiętany jest on w zmiennej sock2. Przy jej pomocy odbierana jest wiadomość od KLIENTA po wywołaniu funkcji recv(). Wiadomość ta wyświetlana jest w okienku przez funkcję MessageBox(). Przed zakończeniem aplikacji zwalniane są oba deskryptory gniazd (closesocket()) oraz zamykana jest sama biblioteka Windows Sockets (WSACleanup()).
//SERWER
#include<winsock2.h>
#include<windows.h>
#include<string.h>
/***************************************************************************************/
#define PORT_LOCAL 10001
#define TEXT_OUT "Tekst klienta"
#define ADDRESS_LOCAL_TEXT "192.100.100.1"
/***************************************************************************************/
void printError(char *text) { MessageBox(NULL,text,"SERWER - Błąd!",MB_ICONSTOP); }
/***************************************************************************************/
int PASCAL WinMain(HANDLE inst, HANDLE prev, LPSTR argv, int state)
{
char text[100];
int fromLen;
WSADATA wsaData;
SOCKET sock,sock2;
struct sockaddr_in FAR from,to;
if (WSAStartup((0<<8)+2,&wsaData) != 0)
{
printError("Brak w systemie zainstalowanej biblioteki WINSOCK 2.0"); return 0;
}
if ((sock=socket(AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET)
{
printError("Nie można otworzyć gniazda"); return 0;
}
to.sin_family=AF_INET;
to.sin_port=htons(PORT_LOCAL);
to.sin_addr.s_addr=inet_addr(ADDRESS_LOCAL_TEXT);
if (bind(sock,(struct sockaddr *)&to,sizeof(to)) == SOCKET_ERROR)
{
printError("Nie można powiązać adresu z gniazdem"); return 0;
}
if (listen(sock,1) == SOCKET_ERROR)
{
printError("Nie można nawiązać połączenia"); return 0;
}
fromLen=sizeof(from);
if ((sock2=accept(sock,(struct sockaddr *)&from,&fromLen)) == INVALID_SOCKET)
{
printError("Nie można nawiązać połączenia"); return 0;
}
if (recv(sock2,text,sizeof(text),0) == SOCKET_ERROR)
{
printError("Odebranie danych nie powiodło się"); return 0;
}
MessageBox(NULL,text,"SERWER - Wiadomość od klienta:",MB_ICONSTOP);
closesocket(sock2);
closesocket(sock);
WSACleanup();
return 1;
}
/***************************************************************************************/
wydruk 1
Na wydruku 2 znajduje się program KLIENT wysyłający dane do SERWERA.
Oprócz lokalnego portu i adresu zdefiniowano w nim także port i adres gniazda zdalnego SERWERA (PORT_REMOTE i ADDRESS_REMOTE_TEXT) oraz napis „Tekst klienta” (TEXT_OUT). Tu także występuje funkcja printError() do informowania użytkownika o sytuacjach nieprawidłowych.
Zmienne mają podobne znaczenie do tych występujących w programie SERWER. Nowa zmienna typu char * text, wskazuje na łańcuch znaków do wysłania.
Początek działania aplikacji także przypomina poprzedni program: inicjalizowana jest biblioteka Windows Sockets i tworzone gniazdo sock, do którego dowiązywany jest lokalny adres. Następna funkcja, connect(), próbuje połączyć się z SERWEREM. Jeśli na komputerze o adresie danym w strukturze to uruchomiono aplikację SERWER i zdążyła już ona wywołać funkcję listen(), to connect() zwróci wartość różną od SOCKET_ERROR, oznaczającą błąd. Połączenie gotowe jest do transmisji, którą wykonuje funkcja send(). Pobiera ona z pamięci określonej przez wskaźnik text tyle bajtów ile liczy łańcuch TEXT_OUT (wartość zwrócona przez strlen() - ilość znaków + bajt o wartości 0, oznaczający koniec łańcucha). Na koniec zwalniany jest deskryptor gniazda sock i zamykana jest biblioteka.
Jeśli którakolwiek z funkcji zakończy się niepowodzeniem, to zwróci wartość SOCKET_ERROR (w przypadku funkcji zwracających typ int) lub INVALID_SOCKET (gdy funkcja powinna zwrócić deskryptor gniazda). Aby określić jaki dokładnie rodzaj błędu wystąpił, należałoby wtedy wywołać funkcję WSAGetLastError().
Chociaż w podanym przykładzie dane transmitowane były w kierunku od KLIENTA do SERWERA, to nic nie stoi na przeszkodzie, aby także SERWER używał funkcji send(), a KLIENT - recv().
Numery portów stosowane w tego rodzaju aplikacjach powinny być większe od 1024. Pozostałe porty mogą być zarezerwowane dla różnych usług.
Programy wykonywalne SERWER i KLIENT, wraz z ich kodami źródłowymi, znajdują się na dyskietce dołączonej do pracy.
//KLIENT
#include<winsock2.h>
#include<windows.h>
#include<string.h>
/***************************************************************************************/
#define PORT_LOCAL 10000
#define PORT_REMOTE 10001
#define TEXT_OUT "Tekst klienta"
#define ADDRESS_LOCAL_TEXT "192.100.100.1"
#define ADDRESS_REMOTE_TEXT "192.100.100.1"
/***************************************************************************************/
void printError(char *text) { MessageBox(NULL,text,"KLIENT - Błąd!",MB_ICONSTOP); }
/***************************************************************************************/
int PASCAL WinMain(HANDLE inst, HANDLE prev, LPSTR argv, int state)
{
char *text=TEXT_OUT;
int err;
WSADATA wsaData;
SOCKET sock;
struct sockaddr_in FAR from, to;
if ((err=WSAStartup((0<<8)+2,&wsaData)) != 0)
{
printError("Brak w systemie zainstalowanej biblioteki WINSOCK 2.0"); return 0;
}
if ((sock=socket(AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET)
{
printError("Nie można otworzyć gniazda"); return 0;
}
from.sin_family=AF_INET;
from.sin_port=htons(PORT_LOCAL);
from.sin_addr.s_addr=inet_addr(ADDRESS_LOCAL_TEXT);
if (bind(sock,(struct sockaddr *)&from,sizeof(from)) == SOCKET_ERROR)
{
printError("Nie można powiązać adresu z gniazdem"); return 0;
}
to.sin_family=AF_INET;
to.sin_port=htons(PORT_REMOTE);
to.sin_addr.s_addr=inet_addr(ADDRESS_REMOTE_TEXT);
if (connect(sock,(struct sockaddr *)&to,sizeof(to)) == SOCKET_ERROR)
{
printError("Nie można nawiązać połączenia"); return 0;
}
if (send(sock,text,strlen(text)+1,0) == SOCKET_ERROR)
{
printError("Wysłanie danych nie powiodło się"); return 0;
}
closesocket(sock);
WSACleanup();
return 1;
}
/***************************************************************************************/
wydruk 2
5. Pakiet programów narzędziowych
5.1. Informacje podstawowe
Przedstawione w tym rozdziale programy narzędziowe pomagają określić stan połączenia między stacjami w sieci. Mogą być uruchamiane w sieci wykorzystującej protokół TCP/IP, a więc także w Internecie. Aplikacje te odwołują się do biblioteki Windows Sockets 2.0, aby więc mogły działać biblioteka taka musi być zainstalowana w systemie.
Programy zostały napisane w języku C, a skompilowane za pomocą kompilatora Microsoft Visual C++ wersja 4.0. Z uwagi na wielkość kodów źródłowych nie są one dołączone do pracy w postaci listingów, lecz znajdują się na dyskietce. Wraz z plikami cpp znaleźć tam można także pliki wykonywalne.
Programy Ping i Tracer wykorzystują w swoim działaniu protokół ICMP (Internet Control Message Protocol - internetowy protokół komunikatów sterujących) i wielokrotnie zmieniają poszczególne składniki nagłówków IP i ICMP [9].
5.2. Program Ping
5.2.1. Funkcja
Nazwa Ping odnosi się do programów realizujących podobne cele w wielu różnych rodzajach sieci. Jest skrótem od określenia: Packet InterNet Groper (internetowy lokalizator sieciowy).
Ping jest najprostszym sposobem ustalenia, czy jest możliwe połączenie z daną stacją. Pozwala określić stan przeładowania połączenia sieciowego. Program ten określa czas przebiegu pakietu od stacji źródłowej do docelowej i z powrotem. Dzięki możliwości określenia wielkości przesyłanego pakietu pozwala ustalić jak wielkość ramki wpływa na transmisję danych.
5.2.2. Algorytm
Istnieje wiele różnych mutacji programu Ping, lecz najczęściej wykorzystywany jest w jego algorytmie protokół ICMP. Pozwala on wysłać pakiet danych pod określony adres IP, tak by stacja docelowa odebrała go i odesłała odpowiednią wiadomość. Algorytm zastosowany w opisanym dalej programie Ping przedstawiono na rys 11. Program Ping wysyła komunikat ICMP: Echo, żądający przekazanie echa, a następnie oczekuje na nadejście odpowiedzi. Stacja, która otrzyma Echo powinna niezwłocznie wysłać do stacji żądającej echa komunikat Echo Replay. Po otrzymaniu echa stacja może już stwierdzić, że istnieje połączenie z danym adresem. Aby zmierzyć czas przesyłu komunikatu można zapamiętać czas startu pakietu i porównać go z czasem powrotu. Jednak w przypadku dużej liczby wysyłanych komunikatów tego typu staje się to kłopotliwe. Ponieważ wraz z nagłówkiem ICMP przesyłana jest także pewna porcja danych, można tam umieścić czas nadania komunikatu, a po jego powrocie sprawdzić czas nadejścia i oba czasy porównać. Możliwe to jest dlatego, że komunikat Echo Replay zawiera dane z komunikatu Echo. Dokładniej: większość pól nagłówka ICMP komunikatu Echo oraz dane pozostają takie same w komunikacie Echo Replay. Warunkiem możliwości zastosowania takiego mechanizmu jest wielkość danych równa 8 bajtów, gdyż tyle potrzeba na zapamiętanie struktury timeval zawierającej czas. Zaletą obecności w powrotnym komunikacie danych pierwotnie wysłanych jest możliwość sprawdzenia ich identyczności. Jest to pewną miarą poprawności transmisji.
rys. 11
Ponieważ wygodnie jest ustalić liczbę komunikatów Echo i wysyłać je raz za razem, to ważne jest, by kolejne komunikaty były numerowane. Jednak takie sesje mogą być powtarzane, co mogłoby prowadzić do pomylenia spóźnionych komunikatów powrotnych z poprzedniej sesji z komunikatami dopiero co wysłanymi. Warto więc zawrzeć w komunikacie numer kolejny uruchomienia sesji Ping. Dodatkowo na jednej stacji może być uruchomionych kilka instancji tej samej aplikacji Ping - tak więc dobrze jest identyfikować komunikaty wartością jednoznacznie określającą instancję.
5.2.3. Implementacja
Implementacja powyższego algorytmu wykorzystuje możliwości systemu Windows 95. Program posiada wygodne okna dialogowe (rys.12) oraz okno pomocy, dzięki wykorzystaniu zdarzeń sterujących program można w każdej chwili zatrzymać.
rys. 12
Podczas inicjalizacji głównego okna uruchamiana jest funkcja initPing(), która rozpoczyna współpracę z Windows Sockets, odczytuje informacje o stacji lokalnej (getHostInfo()), inicjalizuje część zmiennych globalnych i tworzy gniazdo w protokole ICMP za pomocą funkcji socket(). Funkcją sterującą jest funkcja głównego okna dialogowego: mainProc(). Odbiera ona wszystkie komunikaty przesyłane do tego okna. Gdy użytkownik naciśnie klawisz START wywoływana jest funkcja startPing(). Odczytuje ona najpierw wartości okienek dialogowych zawierających dane dla aktualnej sesji Ping:
adres stacji zdalnej,
ilość pakietów ICMP do wysłania,
wielkość pojedynczego pakietu,
odstęp czasu między wysłaniem dwóch pakietów.
Dzięki wykorzystaniu funkcji gethostbyname() adres stacji zdalnej podany może być w postaci nazwy DNS. Funkcja startPing() sprawdza poprawność wszystkich danych, a w razie błędu informuje o tym użytkownika za pomocą funkcji printError(). W przypadku pozytywnego odczytania wszystkich danych inicjalizowane są zmienne potrzebne w danej sesji Ping, uruchamiany jest zegar nr 1 z czasem odstępu określonym przez użytkownika (zmienna timeDelay w funkcji SetTimer()), ustawiana jest pułapka na nadchodzące do gniazda komunikaty (funkcja WSAAsyncSelect() z komendą FD_READ i zdarzeniem WM_SOCK_READ).
Zegar nr 1 co odstęp czasu równy timeDelay wysyła do systemu komunikat WM_TIMER. Uruchamiana jest wtedy funkcja sendPing() wysyłająca kolejny komunikat ICMP Echo. Równolegle, w razie nadejścia komunikatu ICMP do gniazda sock, system otrzymuje zdefiniowane wcześniej zdarzenie WM_SOCK_READ i wywołuje funkcję recvPing(), która odczytuje pakiet.
Funkcja sendPing() wypełnia poszczególne pola nagłówka ICMP:
typ - Echo (ICMP_ECHO),
kod - 0,
suma kontrolna - 0 (później zostanie wywołana funkcja in_cksum() obliczająca sumę kontrolną komunikatu ICMP),
sekwencja - upakowane w niej są dwie ważne informacje: numer kolejnej sesji Ping (4 bity) i numer kolejnego komunikatu Echo w tej sesji (12 bitów); takie upakowanie wynika z potrzeby umieszczenia tych danych w nagłówku ICMP - wielkość danych dodatkowych może przecież zostać określona przez użytkownika jako 0,
identyfikator - unikatowy numer procesu (dzięki temu mogą pracować w systemie jednocześnie dwa Pingi).
czas wysłania (GetCurrentTime()) umieszczany jest w obszarze danych nieobowiązkowych, jeśli ich wielkość na to pozwala (8 bajtów),
Wypełniony pakiet jest wysyłany za pomocą funkcji sendto(). Po poprawnym wysłaniu pakietu inkrementowana jest wartość transmPacketNum oznaczająca ilość wysłanych pakietów. Jeśli wysłano już wszystkie pakiety ustawiany jest zegar nr 2. Przy prawidłowym, wystarczająco krótkim czasie powrotu komunikatów zegar ten powinien być wyłączony przed upływem określonego dla niego czasu. Wyłączyć go powinna funkcja recvPing(), gdy odbierze ostatni pakiet. Jeśli jednak pakiet ten nie nadejdzie zegar nr 2 umożliwi dalsze działanie programu (aplikacja nie będzie w nieskończoność czekała na ostatni, zaginiony pakiet).
Funkcja recvPing() wywoływana jest zawsze, gdy gniazdo sock może odebrać jakiś komunikat ICMP. Ponieważ gniazdo sock jest typu SOCK_RAW (gniazdo surowe), funkcja recvfrom() odbiera cały datagram IP. Po tym sprawdzane jest, czy wielkość datagramu jest odpowiednia oraz czy odebrany komunikat ICMP jest typu Echo Replay. Jeśli tak, należy porównać wartości oznaczające identyfikator procesu (processID) i numer sesji Ping tego procesu (pingNum) z identycznymi danymi odczytanymi z komunikatu ICMP. Jeśli się nie zgadzają, komunikat nie został wysłany przez ten proces lub został wysłany w poprzedniej sesji. W obu przypadkach nie ma sensu przetwarzać tego komunikatu. Dzięki temu, że wszystkie procesy oczekujące komunikatów ICMP pod danym adresem otrzymują swoją kopię tego samego komunikatu, to nie ma obawy o odczytanie komunikatu tylko przez jeden proces. W dalszej części funkcji recvping() sprawdzane jest, czy pakiet już wcześniej nie przybył (dublowanie pakietów) oraz czy nie zawiera błędów. Po zmierzeniu czasu powrotu formowany i drukowany jest odpowiedni tekst, informujący o powrocie komunikatu ICMP.
Jeśli odczytano już wszystkie komunikaty, na które Ping czekał w tej sesji wyłączany jest zegar nr 2 i zdarzenie WM_SOCK_READ. Wykonywana jest teraz funkcja drukująca ostateczne informacje statystyczne recvPingFinish(). Drukuje ona informacje o ilości pakietów wysłanych, odebranych, zgubionych i zdublowanych oraz czasy przesyłu pakietów: minimalny, średni i maksymalny.
5.2.4. Opis działania
Wygląd głównego okna aplikacji przedstawia rys.12, a ikona programu ukazana jest na rys.13.
rys. 13
Okno to zawiera następujące pola:
IP stacji lokalnej - jest to pole informacyjne, w którym wyświetlony jest adres IP stacji, na której uruchomiony jest Ping; jeśli stacja ta posiada swoją własną nazwę, to zostanie ona również wyświetlona; pola tego nie można edytować,
IP stacji zdalnej - tu należy wpisać adres IP lub nazwę DNS (np.
) stacji, z którą połączenie ma być sprawdzone,
Ilość pakietów - ilość komunikatów ICMP jaka zostanie wysłana w jednej sesji Ping,
Wielkość pakietu - wielkość danych w pojedynczym pakiecie ICMP (bez nagłówka IP i ICMP); jeśli wielkość ta będzie mniejsza od 8 nie będzie mierzony czas przesyłu komunikatu przez sieć,
Odstęp (msec) - czas odstępu, mierzony w milisekundach, między kolejnymi momentami wysłania komunikatu ICMP Echo,
Bez wydruków - opcja pozwalająca zabronić drukowania informacji o powracających komunikatach; wydrukowana zostanie tylko informacja o rozpoczęciu i zakończeniu sesji,
klawisz START - rozpoczyna sesję Ping, wykorzystując bieżące ustawienia,
klawisz STOP - pozwala przerwać trwającą sesję Ping, aktywny tylko w czasie działania sesji Ping,
klawisz WYCZYŚĆ - czyści okno wyjściowe, w którym gromadzone są wyniki działania kolejnych sesji Ping,
klawisz POMOC - wyświetla okno pomocy, z podręcznymi informacjami o sposobie korzystania z programu,
klawisz INFO - wyświetla informację o programie,
klawisz KONIEC - powoduje natychmiastowy koniec pracy programu Ping,
Na rys.12 przedstawiono sytuację, gdy Ping wysłał pięć komunikatów ICMP o wielkości danych 100 bajtów, pod adres 198.105.232.1, z odstępem 100 msek. Wszystkie pakiety dotarły pod wskazany adres i powróciły komunikaty Echo Replay (0% zgubionych). Rys.14 przedstawia natomiast sytuację, kiedy użytkownik nie chciał, aby były wyświetlane informacje o poszczególnych komunikatach (
). Podczas tej sesji jeden komunikat zaginął - powodem tego mogło być zmniejszenie odstępu do 10 msek.
rys. 14
5.3. Program Tracer
5.3.1. Funkcja
Funkcją programu Tracer jest określenie trasy przebiegu pakietów wysyłanych do określonej stacji docelowej. Dzięki temu można określić, w którym miejscu nastąpiła awaria, jeśli pakiety nie docierają do adresata.
Internet zbudowany jest z wielu sieci, które połączone są ze sobą za pomocą urządzeń nazywanych bramami (ang. gateway). Komputer wysyłający wiadomość, na podstawie adresu (identyfikator sieci), określa, czy stacja docelowa dołączona jest do tej samej sieci. Jeśli tak, to połączenie dokonywane jest w ramach sieci lokalnej. W przeciwnym razie pakiet IP kierowany jest do lokalnej bramy, która zawiera tablicę marszrutowania z adresami innych sieci Internetu. Zwykle tablica ta zawiera wiele kierunków połączeń, lecz jeśli nie ma w niej szukanej sieci, to wykorzystywana jest brama domyślna. W ten sposób następuje próba określenia położenia celu pakietu, który może znajdować się „gdzieś na świecie”. Aby tablice marszrutowania nie były zbyt wielkie, sieci (np. jednego państwa) zorganizowane są w domeny, które z kolei mają możliwości połączenia z innymi domenami.
5.3.2. Algorytm
Program Tracer, podobnie jak Ping, wykorzystuje w swoim działaniu kontrolny protokół Internetu ICMP. Wykorzystano tutaj właściwość przesyłania datagramów IP, polegającą na usuwaniu z sieci tych pakietów, których czas życia się skończył. Czas życia TTL określany jest dla datagramu w momencie startu w stacji źródłowej i zwykle ma on wartość 128. Po przejściu przez kolejną bramę, oddzielającą sieć od sieci, wartość ta zmniejszana jest o 1. Jeśli któraś brama stwierdzi, że TTL przetwarzanego pakietu ma wartość zero, zobowiązana jest go usunąć z obiegu. Jednocześnie brama ta powinna wysłać komunikat ICMP Time Exceed do stacji źródłowej pakietu. W komunikacie tym zawarty jest adres danej bramy. Dzięki temu mechanizmowi, ustawiając TTL startującego pakietu na kolejne wartości (począwszy od 1), można określić adresy wszystkich pośrednich bram na drodze pakietu.
Wysyłany datagram ma nagłówek UDP z pewnym, określonym numerem portu. Algorytm zakłada, że port ten nie jest obsługiwany przez protokół UDP na stacji docelowej. W przeciwnym razie pakiet, który dotrze do tej stacji, zostanie przez nią odebrany bez powiadomienia o osiągnięciu celu stacji źródłowej. Jeśli port nie będzie obsługiwany, to warstwa IP powinna wysłać komunikat ICMP Destination Unreachable z kodem Port Unreachable. Numer portu przeznaczenia może być swobodnie zmieniany w granicach od 1024 do 65535, małe jest więc prawdopodobieństwo obsługiwania danego portu przez stację docelową. Zazwyczaj wykorzystywane są numery portów poniżej 1024, są to tzw. ogólnie znane porty.
Algorytm programu Tracer przedstawia rys.15.
rys. 15
5.3.3. Implementacja
Program Tracer wykorzystuje dwa protokoły TCP/IP: datagramowy UDP oraz kontrolny ICMP. Protokół datagramowy służy do wysyłania pakietów pod wskazany przez użytkownika adres ze zmiennym czasem życia pakietu TTL. Obsługa komunikatów ICMP polega na odbieraniu wiadomości kontrolnych od bram pośrednich i stacji końcowej. Funkcja initTracer(), wywoływana na początku działania programu wykonuje następujące czynności:
inicjalizuje Windows Sockets, za pomocą funkcji WSAStartup(),
odczytuje informacje o stacji lokalnej (getHostInfo()),
tworzy dwa gniazda: socketOut, do wysyłania datagramów UDP, oraz socketIn, do odbierania komunikatów ICMP,
inicjalizuje zmienne globalne.
Główną funkcją okna, wywoływaną przy okazji każdego zdarzenia z nim związanego, jest mainWinProc(). Gdy użytkownik naciśnie klawisz START, wykonywana jest funkcja startTracer(), a później sendProbe(). Funkcja startTracer() pobiera dane z pól edycyjnych i inicjalizuje zmienne potrzebne w bieżącej sesji Tracera: ttl=0, probe=0. Wykonuje też dwie ważne operacje na gniazdach
wysyła jeden pakiet Echo pod adres docelowy; komunikat powrotny Echo Replay zostanie przez program pominięty; taka operacja inicjalizuje połączenie gniazda sockIn z protokołem ICMP - jest to własność Windows Sockets; bez takiej inicjalizacji gniazdo nie otrzymałoby wiadomości od protokołu; ta własność oraz kilka dodatkowych świadczą o tym, że biblioteka dostarczana wraz z Windows Sockets 2.0 nie jest ostatecznie przystosowana do obsługi gniazd surowych (SOCK_RAW),
wykonuje funkcję flushSocket() na gnieździe socketIn, powodującą odczytanie wszystkich komunikatów, które przyszły już do tego gniazda (np. spowodowanych wcześniejszą sesją Tracera), mogących zakłócić działanie bieżącej sesji Tracera.
Funkcja sendProbe() sprawdza, czy wysłano już wszystkie komunikaty przeznaczone dla jednej bramy pośredniej (probeNum) i jeśli tak zeruje zmienną probe, a zmienna ttl jest inkrementowana. Czas TTL pakietu UDP ustalany jest na ttl za pomocą funkcji setsockopt(). Aktualna biblioteka Windows Sockets nie umożliwia niestety swobodnego dostępu do wszystkich pól nagłówka IP wysyłanego pakietu. Wykorzystanie funkcji setsockopt() wydaje się być jedyną możliwością dokonania zmiany czasu życia pakietu. Pakiet wysyłany jest za pomocą funkcji sendto(). Następnie ustawiany jest zegar na czas określony przez użytkownika: „waitTime” sekund. Po tym czasie przyjmuje się, że nie ma odpowiedzi od danej bramy lub stacji. Aby móc odczytać komunikat ICMP ustawiana jest pułapka na zdarzenie FD_READ gniazda socketIn: WM_SOCK_READ.
Po zakończeniu funkcji sendProbe() następuje oczekiwanie na przybycie komunikatu ICMP lub zadziałanie zegara. Gdy przybędzie komunikat ICMP, aplikacja otrzymuje zdarzenie WM_SOCK_READ i wywołuje funkcję recvProbe(). Funkcja ta wywołuje funkcję recvReplay(), określającą, czy komunikat ICMP przyszedł od bramy pośredniej, dla której skończył się czas życia TTL pakietu (typ=Time Exceed, kod=In Transmission), czy też od stacji końcowej (typ=Destination Unreachable). Wszystkie inne komunikaty ICMP są ignorowane. Gdy odebrano oczekiwany komunikat zatrzymywany jest zegar i drukowana jest odpowiednia informacja dla użytkownika: jeśli jest to pierwsza pozytywna próba, to odczytywany jest adres IP z nagłówka komunikatu ICMP bramy pośredniej; przedstawiany jest także czas transmisji. Adres IP przekształcany jest na nazwę DNS i, jeśli taka istnieje, jest wyświetlana.
Po odebraniu spodziewanego komunikatu ICMP lub upływie czasu oczekiwania na niego, wysyłany jest kolejny datagram UDP. Wysyłanie datagramów kończy się w czterech przypadkach:
stacja otrzymała komunikat ICMP Destination Unreachable - Port Unreachable, co oznacza dotarcie do adresu docelowego,
stacja otrzymała komunikat ICMP Destination Unreachable - lecz kod jest inny niż Port Unreachable, co oznacza niedostępność adresu docelowego (sieci, stacji, protokołu itp.),
osiągnięto maksymalną dopuszczalną (maxttl) wartość czasu TTL dla wysyłanych pakietów,
wystąpił błąd podczas wysyłania lub odbierania pakietów.
Po odebraniu ostatniego komunikatu ICMP, wyświetlana jest informacja końcowa (rys.16) i Tracer gotowy jest do następnej sesji.
rys. 16
5.3.4. Opis działania
Okno dialogowe programu Tracer ilustruje rys.16. Na rys.17 przedstawiono ikonę programu.
rys. 17
Poszczególne pola okna dialogowego oznaczają:
IP stacji lokalnej - jest to pole informacyjne, w którym wyświetlony jest adres IP stacji, na której uruchomiony jest Tracer; jeśli stacja ta posiada swoją własną nazwę, to zostanie ona również wyświetlona; pola tego nie można edytować,
IP stacji zdalnej - tu należy wpisać adres IP lub nazwę DNS (np.
) stacji, do której trasa jest określana,
numer portu, choć ustalany eksperymentalnie, prawdopodobnie nie powinien być obsługiwany przez stację zdalną, jeśli jest inaczej, trzeba wybrać inny port,
Ilość prób - tyle razy wysyłany będzie datagram UDP z jedną wartością czasu TTL; inaczej: tyle razy Tracer będzie próbował skontaktować się z jedną bramą pośrednią,
Max TTL - maksymalna wartość jaką może uzyskać pole Time To Live w nagłówku IP datagramu UDP; wartość 30 wystarcza zwykle do osiągnięcia wszystkich adresów Internetu,
Odstęp (sec) - tyle czasu Tracer będzie czekał na odpowiedź od bramy lub stacji w postaci komunikatu ICMP; jeśli komunikat taki nie nadejdzie, pakiet UDP lub komunikat ICMP zostanie uznany za zaginiony,
klawisz START - rozpoczyna sesję Tracera, wykorzystując bieżące ustawienia,
klawisz STOP - pozwala przerwać trwającą sesję Tracera, aktywny tylko w czasie działania sesji Tracera,
klawisz WYCZYŚĆ - czyści okno wyjściowe, w którym gromadzone są wyniki działania kolejnych sesji Tracera,
klawisz POMOC - wyświetla okno pomocy, z podręcznymi informacjami o sposobie korzystania z programu,
klawisz INFO - wyświetla informację o programie,
klawisz KONIEC - powoduje natychmiastowy koniec pracy programu Tracer,
rys. 18
Okno wyjściowe programu Tracer posiada wygodny suwak, którym należy się posługiwać, gdy informacje nie będą się w nim już mieściły (rys.18 i 19).
rys. 19
Rys.18 i rys.19 przedstawiają wyniki działania programu Tracer, który określał trasę do stacji: ftp.microsoft.com. Tracer określił, że po drodze do tej stacji jest 18 bram, część z nich ma swoje nazwy tekstowe, które zostały wyświetlone. Dla każdej bramy i stacji zażądano trzech prób i zazwyczaj tyle czasów transmisji widnieje przy każdym z adresów. Czasami jednak zdarzało się, że komunikat ICMP nie dotarł do Tracera - oznaczone jest to gwiazdką (bramy: 8, 10, 12, 14, 15,17, 18). Może to wynikać, ze znacznego obciążenia sieci.
Na rys.17 przedstawiono sytuację, kiedy Tracer z powodzeniem osiągnął stację info.nask.pl. Maksymalna dopuszczalna wartość TTL ustawiona była na 30, ale wystarczyło tylko 9 - 8 bram pośrednich i stacja docelowa. Zachowanie Tracera w przypadku zbyt małego maksymalnego czasu TTL ilustruje rys.20. ponieważ Max TTL = 5, po osiągnięciu piątej bramy Tracer zgłosił komunikat: „TRACER nie mógł osiągnąć stacji docelowej”.
rys. 20
5.4. Program Namer
5.4.1. Funkcja
Namer jest dodatkowym programem narzędziowym pozwalającym ustalić adres IP, jeśli jest znana nazwa DNS stacji lub odwrotnie: nazwę, gdy znany jest adres.
5.4.2. Algorytm
rys. 21
Algorytm działania programu Namer przedstawia rys.21. W zależności od operacji jakiej zażąda użytkownik, wykonywana jest funkcja gethostbyaddr() lub gethostbyname().
rys. 22
5.4.3. Implementacja
Funkcją sterującą jest mainWinProc(). Gdy użytkownik naciśnie klawisz
lub
(rys.22) funkcja ta otrzyma zdarzenie ID_IP_NAME lub ID_NAME_IP. Pierwsze zdarzenie oznacza chęć zamiany adresu na nazwę, a drugie operację odwrotną. W tym momencie wywoływana jest funkcja startNamer() z argumentem określającym rodzaj operacji. Jeśli zamieniany jest adres na nazwę, to wywoływana jest funkcja gethostbyaddr(). W przeciwnym razie: gethostbyname(). Obie funkcje zwracają wskaźnik do struktury hostent, zawierającej zarówno adres IP, jak i nazwę stacji. Jeśli odnalezienie adresu lub nazwy nie jest możliwe zwracana jest wartość NULL, a Namer wyświetla odpowiedni komunikat (rys.24 i rys.25).
5.4.4. Opis działania
Wygląd ekranu z uruchomionym programem Namer widnieje na rys.22, a jego ikona na rys.23.
rys. 23
Okno dialogowe programu zawiera następujące składniki:
IP stacji lokalnej - jest to pole informacyjne, w którym wyświetlony jest adres IP stacji, na której uruchomiony jest Namer; jeśli stacja ta posiada swoją własną nazwę, to zostanie ona również wyświetlona; pola tego nie można edytować,
Adres IP stacji zdalnej - tu należy wpisać adres IP stacji, której nazwa jest nieznana,
Nazwa stacji zdalnej - tu należy wpisać nazwę DNS stacji, której adres jest nieznany,
klawisz zamiany adresu IP na nazwę stacji; wcześniej musi być wpisany adres zdalnej stacji (w przeciwnym razie zostanie wzięty adres stacji lokalnej),
klawisz zamiany nazwy stacji na adres IP; wcześniej musi być wpisana nazwa zdalnej stacji (w przeciwnym razie zostanie wzięta nazwa stacji lokalnej),
klawisz POMOC - wyświetla okno pomocy, z podręcznymi informacjami o sposobie korzystania z programu,
klawisz INFO - wyświetla informację o programie,
klawisz KONIEC - powoduje natychmiastowy koniec pracy programu Namer,
rys. 24
W przypadku wpisania błędnego adresu IP lub adresu, który nie posiada nazwy DNS, wypisany zostanie komunikat jak na rys.24. Gdy natomiast użytkownik poda nazwę, z którą nie jest związany żaden adres IP, to program zareaguje tak, jak na rys.25.
rys. 25
Ponieważ oczekiwanie na określenie nazwy lub adresu (funkcje gethostbyaddr() i gethostbyname()) może trwać nawet kilka sekund, w tym czasie wyświetlany jest komunikat:
, a klawisze służące do zamiany są nieaktywne.
6. Wnioski
System operacyjny Windows 95 dość dobrze spełnia rolę systemu dla niezaawansowanej stacji roboczej. Zaimplementowano w nim obsługę wielu protokołów i zaoferowano użytkownikowi liczne usługi. Jednak ze względu na zawodność tego systemu nie powinien być on raczej stosowany w profesjonalnych zastosowaniach. Wydaje się, że w takich przypadkach dużo bezpieczniej jest zastosować system Windows NT.
Istniejące biblioteki oprogramowania dla systemu Windows 95, zarówno dla sieci lokalnych jak i rozległych bardzo dobrze spełniają swoją rolę podczas przesyłania wiadomości. Przedstawione w rozdziale 4 przykładowe programy, bazujące na funkcjach biblioteki Windows Sockets, są podstawą do budowy dużych programów wymagających komunikacji TCP/IP. Tego rodzaju programowanie nie powinno nastręczać żadnych trudności.
Jednakże biblioteki te bardzo dobrze oddzielają programistę od dostępu do warstwy niskiego poziomu. Może to być przydatne, aby zapobiec przypadkowej zmianie niektórych parametrów, co mogłoby spowodować nawet załamanie pracy systemu. Jednakże czasami dostęp niskiego poziomu jest niezbędny. Biblioteka Windows Sockets nie umożliwia ustawienia wszystkich pól w nagłówkach poszczególnych pakietów. Niedostępnych jest też wiele opcji znanych z wersji UNIX-owej gniazd. Niedoskonałości te nie są jednak zbyt znaczące w zwykłym programowaniu aplikacji transmisyjnych. Należy też mieć nadzieję, że zgodnie ze specyfikacją Windows Sockets, kolejne wersje bibliotek będą miały zaimplementowaną większą liczbę opcji TCP/IP.
Programy Ping, Tracer i Namer spełniają swoje założenia i są przydatnym dodatkiem do systemu sieciowego. Programy te mogą zostać rozwinięte w jedną dużą aplikację narzędziową. Można by nawet pokusić się o napisanie swoistego edytora IP - programu, który pozwalałby edytować pola datagramu IP, wysyłałby pakiet, a następnie oczekiwał odpowiedzi, zgodnie z zamierzeniami operatora. Pozwalałoby to na obsługę dowolnych protokołów i umożliwiało obserwację wielu różnych zjawisk w sieci.
7. Bibliografia
1. Hall M.:Windows Sockets 2 Application Programming Interface, 1996,
2. Help for Microsoft Developer Studio, SDK - Network Services, 1994-95,
3. Hunt C.: TCP/IP Administracja sieci, Oficyna Wydawnicza READ ME, Warszawa 1996,
4. Postel J.: User Datagram Protocol, RFC 768, 1980
5. Postel J.: Internet Protocol, RFC 791, 1981
6. Postel J.: Internet Control Message Protocol, RFC 792, 1981
7. Postel J.: Transmission Control Protocol, RFC 793, 1981
8. Sheldon T.: Wielka Encyklopedia Sieci Komputerowych, Wydawnictwo ROBOMATIC, Wrocław
9. Stevens W. R.: Programowanie zastosowań sieciowych w systemie UNIX, Wydawnictwa Naukowo-Techniczne, Warszawa 1995,
____________________________________________________________________________________
strona 55
![]() | Pobierz dokument dyplom.instytut.informatyki.doc Rozmiar 1.5 MB |