Gniazda Berkeleya
Gniazda Berkeley to interfejs programowania aplikacji (API) dla gniazd internetowych i gniazd domeny Unix , używany do komunikacji między procesami (IPC). Jest powszechnie implementowany jako biblioteka modułów, które można łączyć. Pochodzi z Unix 4.2BSD , który został wydany w 1983 roku.
Gniazdo jest abstrakcyjną reprezentacją ( uchwytem ) lokalnego punktu końcowego sieciowej ścieżki komunikacyjnej . Interfejs API gniazd Berkeley reprezentuje go jako deskryptor pliku ( uchwyt pliku ) w filozofii systemu Unix , który zapewnia wspólny interfejs do wprowadzania i wyprowadzania strumieni danych.
Gniazda Berkeley ewoluowały z niewielkimi modyfikacjami z de facto standardu do komponentu specyfikacji POSIX . Termin gniazda POSIX jest zasadniczo synonimem gniazd Berkeley , ale są one również znane jako gniazda BSD , co potwierdza pierwszą implementację w Berkeley Software Distribution .
Historia i wdrożenia
systemu operacyjnego 4.2BSD Unix , wydanego w 1983 roku, jako interfejs programistyczny. Jednak dopiero w 1989 roku Uniwersytet Kalifornijski w Berkeley mógł wydać wersje systemu operacyjnego i biblioteki sieciowej wolne od ograniczeń licencyjnych zastrzeżonego systemu Unix firmy AT&T Corporation .
Wszystkie nowoczesne systemy operacyjne implementują wersję interfejsu gniazda Berkeley. Stał się standardowym interfejsem dla aplikacji działających w Internecie . Nawet Winsock dla MS Windows, stworzona przez niepowiązanych programistów, jest ściśle zgodna ze standardem.
API gniazd BSD jest napisane w języku programowania C. Większość innych języków programowania zapewnia podobne interfejsy, zwykle napisane jako biblioteka opakowująca oparta na C API.
Gniazda BSD i POSIX
Wraz z ewolucją interfejsu API gniazda Berkeley i ostatecznie powstaniem interfejsu API gniazda POSIX, niektóre funkcje zostały uznane za przestarzałe lub usunięte i zastąpione innymi. Interfejs API POSIX jest również przeznaczony do ponownego wprowadzania i obsługuje protokół IPv6.
Działanie | BSD | POSIX |
---|---|---|
Konwersja z adresu tekstowego na adres spakowany | inet_aton | inet_pton |
Konwersja z adresu spakowanego na adres tekstowy | inet_ntoa | inet_ntop |
Wyszukiwanie do przodu dla nazwy hosta/usługi | gethostbyname, gethostbyaddr, getservbyname, getservbyport | pobierz informacje o dodaniu |
Wyszukiwanie wsteczne dla nazwy hosta/usługi | gethostbyaddr, getservbyport | pobierz informacje o nazwie |
Alternatywy
Interfejs API warstwy transportowej (TLI) oparty na STREAMS stanowi alternatywę dla interfejsu API gniazda. Wiele systemów udostępniających interfejs API TLI udostępnia również interfejs API gniazda Berkeley.
Systemy inne niż Unix często udostępniają interfejs API gniazda Berkeley z warstwą translacji dla natywnego sieciowego interfejsu API. Plan 9 i Genode używają interfejsów API systemu plików z plikami kontrolnymi zamiast deskryptorów plików.
Pliki nagłówkowe
Interfejs gniazda Berkeley jest zdefiniowany w kilku plikach nagłówkowych. Nazwy i zawartość tych plików różnią się nieznacznie w zależności od implementacji. Na ogół obejmują one:
Plik | Opis |
---|---|
sys/socket.h | Podstawowe funkcje gniazd i struktury danych. |
netinet/w.h | Rodziny adresów AF_INET i AF_INET6 oraz odpowiadające im rodziny protokołów, PF_INET i PF_INET6. Należą do nich standardowe adresy IP oraz numery portów TCP i UDP. |
sys/un.h | Rodzina adresów PF_UNIX i PF_LOCAL. Używany do lokalnej komunikacji między programami działającymi na tym samym komputerze. |
arpa/inet.h | Funkcje do manipulowania numerycznymi adresami IP. |
netdb.h | Funkcje do tłumaczenia nazw protokołów i nazw hostów na adresy numeryczne. Przeszukuje dane lokalne oraz usługi nazw. |
Funkcje interfejsu API gniazda
Interfejs API gniazda Berkeley zazwyczaj udostępnia następujące funkcje:
- socket() tworzy nowe gniazdo określonego typu, identyfikowane przez liczbę całkowitą, i przydziela do niego zasoby systemowe.
- Metoda bind() jest zwykle używana po stronie serwera i wiąże gniazdo ze strukturą adresu gniazda, tj. określonym lokalnym adresem IP i numerem portu.
- Listen() jest używany po stronie serwera i powoduje, że powiązane gniazdo TCP przechodzi w stan nasłuchiwania.
- connect() jest używany po stronie klienta i przypisuje wolny numer portu lokalnego do gniazda. W przypadku gniazda TCP powoduje próbę nawiązania nowego połączenia TCP.
- accept() jest używany po stronie serwera. Akceptuje odebrane przychodzące próby utworzenia nowego połączenia TCP od zdalnego klienta i tworzy nowe gniazdo powiązane z parą adresów gniazda tego połączenia.
- send() , recv() , sendto() i recvfrom() służą do wysyłania i odbierania danych. Można również użyć standardowych funkcji write() i read() .
- close() powoduje, że system zwalnia zasoby przydzielone do gniazda. W przypadku protokołu TCP połączenie zostaje przerwane.
- gethostbyname() i gethostbyaddr() są używane do rozpoznawania nazw i adresów hostów. Tylko IPv4.
- getaddrinfo() i freeaddrinfo() są używane do rozpoznawania nazw i adresów hostów. IPv4, IPv6.
- select() służy do zawieszania, oczekiwania na jedno lub więcej z podanej listy gniazd, które będą gotowe do odczytu, gotowe do zapisu lub będą zawierać błędy.
- poll() służy do sprawdzania stanu gniazda w zestawie gniazd. Zestaw można przetestować, aby sprawdzić, czy dowolne gniazdo może być zapisywane, odczytywane lub czy wystąpił błąd.
- getsockopt() służy do pobierania bieżącej wartości określonej opcji gniazda dla określonego gniazda.
- setsockopt() służy do ustawiania określonej opcji gniazda dla określonego gniazda.
gniazdo elektryczne
Funkcja socket() tworzy punkt końcowy komunikacji i zwraca deskryptor pliku dla gniazda. Używa trzech argumentów:
-
domain , która określa rodzinę protokołów tworzonego gniazda. Na przykład:
- AF_INET dla protokołu sieciowego IPv4 (tylko IPv4)
- AF_INET6 dla IPv6 (aw niektórych przypadkach kompatybilność wsteczna z IPv4)
- AF_UNIX dla gniazda lokalnego (przy użyciu specjalnego węzła systemu plików)
-
typ , jeden z:
- SOCK_STREAM (niezawodna usługa zorientowana na strumień lub gniazda strumieniowe )
- SOCK_DGRAM (usługa datagramowa lub gniazda datagramowe )
- SOCK_SEQPACKET (niezawodna usługa sekwencjonowania pakietów)
- SOCK_RAW (surowe protokoły na wierzchu warstwy sieciowej)
- 0 protokół określający rzeczywisty protokół transportowy do użycia. Najpopularniejsze to IPPROTO_TCP , IPPROTO_SCTP , IPPROTO_UDP , IPPROTO_DCCP . Te protokoły są określone w pliku netinet/in.h . Wartość może służyć do wyboru protokołu domyślnego z wybranej domeny i typu.
Funkcja zwraca -1, jeśli wystąpił błąd. W przeciwnym razie zwraca liczbę całkowitą reprezentującą nowo przypisany deskryptor.
wiązać
bind() kojarzy gniazdo z adresem. Gdy gniazdo jest tworzone za pomocą funkcji socket() , otrzymuje ono tylko rodzinę protokołów, ale nie ma przypisanego adresu. To powiązanie musi zostać wykonane, zanim gniazdo będzie mogło akceptować połączenia z innych hostów. Funkcja ma trzy argumenty:
- sockfd , deskryptor reprezentujący gniazdo
- my_addr , wskaźnik do struktury sockaddr reprezentującej adres do powiązania.
- addrlen , pole typu socklen_t określające rozmiar struktury sockaddr .
bind() zwraca 0 w przypadku powodzenia i -1 w przypadku wystąpienia błędu.
Słuchać
Po powiązaniu gniazda z adresem, listen() przygotowuje je na połączenia przychodzące. Jest to jednak konieczne tylko dla trybów danych zorientowanych na strumień (zorientowanych na połączenie), tj. dla typów gniazd ( SOCK_STREAM , SOCK_SEQPACKET ). Listen() wymaga dwóch argumentów:
- sockfd , prawidłowy deskryptor gniazda.
- backlog , liczba całkowita reprezentująca liczbę oczekujących połączeń, które mogą być umieszczone w kolejce w dowolnym momencie. System operacyjny zwykle ogranicza tę wartość.
Po zaakceptowaniu połączenia jest ono usuwane z kolejki. W przypadku sukcesu zwracane jest 0. Jeśli wystąpi błąd, zwracane jest -1.
zaakceptować
Gdy aplikacja nasłuchuje połączeń strumieniowych z innych hostów, jest powiadamiana o takich zdarzeniach (por. funkcja select() ) i musi zainicjować połączenie za pomocą funkcji accept() . Tworzy nowe gniazdo dla każdego połączenia i usuwa połączenie z kolejki nasłuchiwania. Funkcja ma następujące argumenty:
- sockfd , deskryptor gniazda nasłuchującego, które ma połączenie w kolejce.
- cliaddr , wskaźnik do struktury sockaddr, aby otrzymać informacje o adresie klienta.
- addrlen , wskaźnik do lokalizacji socklen_t , która określa rozmiar struktury adresu klienta przekazanej do accept(). Gdy funkcja accept() zwraca, ta lokalizacja zawiera rozmiar (w bajtach) struktury.
accept() zwraca nowy deskryptor gniazda dla zaakceptowanego połączenia lub wartość -1 , jeśli wystąpi błąd. Cała dalsza komunikacja ze zdalnym hostem odbywa się teraz za pośrednictwem tego nowego gniazda.
Gniazda datagramowe nie wymagają przetwarzania przez accept(), ponieważ odbiorca może natychmiast odpowiedzieć na żądanie za pomocą gniazda nasłuchującego.
łączyć
connect() ustanawia bezpośrednie łącze komunikacyjne z określonym zdalnym hostem identyfikowanym przez jego adres za pośrednictwem gniazda identyfikowanego przez jego deskryptor pliku.
W przypadku korzystania z protokołu zorientowanego na połączenie ustanawia to połączenie. Niektóre typy protokołów są bezpołączeniowe, w szczególności User Datagram Protocol . W połączeniu z protokołami bezpołączeniowymi connect definiuje zdalny adres do wysyłania i odbierania danych, umożliwiając korzystanie z funkcji takich jak send i recv . W takich przypadkach funkcja connect uniemożliwia odbiór datagramów z innych źródeł.
0 connect() zwraca liczbę całkowitą reprezentującą kod błędu: reprezentuje sukces, a –1 oznacza błąd. Historycznie rzecz biorąc, w systemach wywodzących się z BSD stan deskryptora gniazda jest niezdefiniowany, jeśli wywołanie połączenia nie powiedzie się (jak określono w specyfikacji Single Unix), dlatego aplikacje przenośne powinny natychmiast zamykać deskryptor gniazda i uzyskiwać nowy deskryptor z socket() w przypadku niepowodzenia wywołania connect().
gethostbyname i gethostbyaddr
Funkcje gethostbyname() i gethostbyaddr() są używane do rozwiązywania nazw hostów i adresów w systemie nazw domen lub innych mechanizmów tłumaczących lokalnego hosta (np. wyszukiwanie /etc/hosts). Zwracają wskaźnik do obiektu typu struct hostent , który opisuje hosta protokołu internetowego . Funkcje używają następujących argumentów:
- nazwa określa nazwę DNS hosta.
- addr określa wskaźnik do struktury in_addr zawierającej adres hosta.
- len określa długość w bajtach adresu .
- type określa typ rodziny adresów (np. AF_INET) adresu hosta.
można sprawdzić zewnętrzną liczbę całkowitą h_errno , aby zobaczyć, czy jest to tymczasowa awaria, czy nieprawidłowy lub nieznany host. W przeciwnym razie zwracana jest poprawna struktura struct hostent * .
Te funkcje nie są ściśle komponentami API gniazda BSD, ale są często używane w połączeniu z funkcjami API. Ponadto funkcje te są obecnie uważane za starsze interfejsy do wysyłania zapytań do systemu nazw domen. Zdefiniowano nowe funkcje, które są całkowicie niezależne od protokołów (obsługują IPv6). Te nowe funkcje to getaddrinfo() i getnameinfo() i są oparte na nowej strukturze danych addrinfo .
Rodziny protokołów i adresów
Interfejs API gniazd Berkeley jest ogólnym interfejsem do komunikacji sieciowej i międzyprocesowej oraz obsługuje różne protokoły sieciowe i architektury adresów.
Poniżej wymieniono przykładowe rodziny protokołów (poprzedzone standardowym identyfikatorem symbolicznym) zdefiniowane we współczesnej implementacji systemu Linux lub BSD :
Identyfikator | Funkcja lub zastosowanie |
---|---|
PF_LOCAL, PF_UNIX, PF_FILE | Lokalny do hosta (potoki i domena plików) |
PF_INET | Wersja protokołu internetowego 4 |
PF_AX25 | Krótkofalarstwo AX.25 |
PF_IPX | Wymiana pakietów międzysieciowych firmy Novell |
PF_APPLETALK | AppleTalk |
PF_NETROM | Radioamatorskie NetROM (związane z AX.25) |
PF_BRIDGE | Most wieloprotokołowy |
PF_ATMPVC | Stałe obwody wirtualne w trybie transferu asynchronicznego |
PF_ATMSVC | Asynchroniczne obwody wirtualne przełączane w trybie transferu |
PF_INET6 | Wersja protokołu internetowego 6 |
PF_DECnet | Zarezerwowane dla projektu DECnet |
PF_NETBEUI | Zarezerwowane dla projektu 802.2LLC |
PF_BEZPIECZEŃSTWO | PseudoAF wywołania zwrotnego zabezpieczeń |
PF_KEY | Interfejs API zarządzania kluczami PF_KEY |
PF_NETLINK, PF_ROUTE | interfejs API routingu |
PF_PACKET | Gniazda przechwytywania pakietów |
PF_ECONET | Żołądź Econet |
PF_SNA | architektury sieci systemów Linux (SNA). |
PF_IRDA | gniazda IrDA |
PF_PPPOX | PPP przez gniazda X |
PF_WANPIPE | Gniazda API Sangoma Wanpipe |
PF_BLUETOOTH | Gniazda Bluetooth |
Gniazdo do komunikacji jest tworzone za pomocą funkcji socket()
, podając żądaną rodzinę protokołów ( identyfikator PF_ ) jako argument.
Oryginalna koncepcja projektowa interfejsu gniazda rozróżniała typy protokołów (rodziny) i określone typy adresów, z których każdy może korzystać. Przewidywano, że rodzina protokołów może mieć kilka typów adresów. Typy adresów zostały zdefiniowane przez dodatkowe stałe symboliczne, używając przedrostka AF zamiast PF . Identyfikatory AF są przeznaczone dla wszystkich struktur danych, które dotyczą konkretnie typu adresu, a nie rodziny protokołów. Jednak ta koncepcja oddzielenia typu protokołu i adresu nie znalazła wsparcia w implementacji, a AF zostały zdefiniowane przez odpowiedni identyfikator protokołu, pozostawiając rozróżnienie między stałymi AF i PF jako argument techniczny bez praktycznych konsekwencji. Rzeczywiście, istnieje wiele nieporozumień we właściwym użyciu obu form.
Specyfikacja POSIX.1—2008 nie określa żadnych stałych PF , a jedynie stałe AF
Surowe gniazda
Surowe gniazda zapewniają prosty interfejs, który omija przetwarzanie przez stos TCP/IP hosta. Umożliwiają implementację protokołów sieciowych w przestrzeni użytkownika i pomagają w debugowaniu stosu protokołów. Gniazda surowe są używane przez niektóre usługi, takie jak ICMP , działające w warstwie internetowej modelu TCP/IP.
Tryb blokujący i nieblokujący
Gniazda Berkeley mogą pracować w jednym z dwóch trybów: blokującym lub nieblokującym.
Gniazdo blokujące nie zwraca sterowania, dopóki nie wyśle (lub nie otrzyma) niektórych lub wszystkich danych określonych dla operacji. To normalne, że gniazdo blokujące nie wysyła wszystkich danych. Aplikacja musi sprawdzić zwracaną wartość, aby określić, ile bajtów zostało wysłanych lub odebranych, i musi ponownie wysłać wszelkie dane, które nie zostały jeszcze przetworzone. Podczas korzystania z gniazd blokujących należy zwrócić szczególną uwagę na accept(), ponieważ może ona nadal blokować po wskazaniu czytelności, jeśli klient rozłączy się w fazie połączenia.
Gniazdo nieblokujące zwraca wszystko, co znajduje się w buforze odbiorczym i natychmiast kontynuuje. Programy korzystające z gniazd nieblokujących, jeśli nie są napisane poprawnie, są szczególnie podatne na warunki wyścigu ze względu na różnice w szybkości łącza sieciowego. [ potrzebne źródło ]
Gniazdo jest zwykle ustawiane w tryb blokowania lub nieblokowania za pomocą funkcji fcntl i ioctl .
Gniazda końcowe
System operacyjny nie zwalnia zasobów przydzielonych do gniazda, dopóki gniazdo nie zostanie zamknięte. Jest to szczególnie ważne, jeśli połączenie nie powiedzie się i zostanie ponowione.
Gdy aplikacja zamyka gniazdo, niszczony jest tylko interfejs gniazda. Jądro jest odpowiedzialne za wewnętrzne zniszczenie gniazda. Czasami gniazdo może wejść w CZAS_OCZEKIWANIA po stronie serwera na maksymalnie 4 minuty.
W systemach SVR4 użycie metody close()
może spowodować odrzucenie danych. W tych systemach może być wymagane użycie funkcji shutdown()
lub SO_LINGER w celu zagwarantowania dostarczenia wszystkich danych.
Przykład klient-serwer przy użyciu protokołu TCP
Protokół kontroli transmisji (TCP) to protokół zorientowany na połączenie , który zapewnia różnorodne funkcje korekcji błędów i wydajności transmisji strumieni bajtów. Proces tworzy gniazdo TCP, wywołując socket()
z parametrami dla rodziny protokołów ( PF INET , PF_INET6 ), trybem gniazda dla gniazd strumieniowych ( SOCK_STREAM ) i identyfikatorem protokołu IP dla protokołu TCP ( IPPROTO_TCP ).
serwer
Ustanowienie serwera TCP obejmuje następujące podstawowe kroki:
- Tworzenie gniazda TCP za pomocą wywołania funkcji socket().
- Powiązanie gniazda z portem nasłuchującym ( bind() ) po ustawieniu numeru portu.
- Przygotowanie gniazda do nasłuchiwania połączeń (uczynienie go gniazdem nasłuchującym) za pomocą wywołania metody listen() .
- Akceptowanie połączeń przychodzących ( accept() ). Blokuje to proces do momentu odebrania połączenia przychodzącego i zwraca deskryptor gniazda dla zaakceptowanego połączenia. Początkowy deskryptor pozostaje deskryptorem nasłuchującym, a accept() może zostać wywołana ponownie w dowolnym momencie z tym gniazdem, dopóki nie zostanie ono zamknięte.
- Komunikacja ze zdalnym hostem za pomocą funkcji API send() i recv() oraz funkcji ogólnego przeznaczenia write() i read() .
- Zamykanie każdego gniazda, które zostało otwarte po użyciu, za pomocą funkcji close()
Poniższy program tworzy serwer TCP nasłuchujący na porcie numer 1100:
0
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main ( void ) { struct sockaddr_in sa ; int SocketFD = gniazdo ( PF_INET , SOCK_STREAM , IPPROTO_TCP ); if ( SocketFD == -1 ) { perror ( "nie można utworzyć gniazda" ); wyjście ( EXIT_FAILURE ); } memset ( & sa , , sizeof sa ); sa . rodzina_grzechów = AF_INET ; sa . sin_port = htons ( 1100 ); sa . adres_grzechu . s_addr = htonl ( INADDR_ANY ); if ( bind ( SocketFD ,( struct sockaddr * ) & sa , sizeof sa ) == -1 ) { perror ( "Błąd wiązania" ); zamknij ( SocketFD ); wyjście ( EXIT_FAILURE ); } if ( list ( SocketFD , 10 ) == -1 ) { perror ( "słuchanie nie powiodło się" ); zamknij ( SocketFD ); wyjście ( EXIT_FAILURE ); } for (;;) { int ConnectFD = zaakceptuj ( SocketFD , NULL , NULL ); if ( ConnectFD == -1 ) { perror ( "akceptacja nie powiodła się" ); zamknij ( SocketFD ); wyjście ( EXIT_FAILURE ); } /* wykonaj operacje odczytu i zapisu ... read(ConnectFD, buff, size) */ if ( shutdown ( ConnectFD , SHUT_RDWR ) == -1 ) { perror ( "zamknięcie nie powiodło się" ); zamknij ( ConnectFD ); zamknij ( SocketFD ); wyjście ( EXIT_FAILURE ); } zamknij ( ConnectFD ); } zamknij ( SocketFD ); powrót EXIT_SUCCESS ; }
Klient
Programowanie aplikacji klienckiej TCP obejmuje następujące kroki:
- Tworzenie gniazda TCP.
-
Łączenie się z serwerem ( connect() ), poprzez przekazanie struktury
sockaddr_in
zsin_family
ustawionym na AF_INET ,sin_port
ustawionym na port, na którym nasłuchuje punkt końcowy (w sieciowej kolejności bajtów), asin_addr
ustawionym na adres IP serwera nasłuchującego ( również w sieciowej kolejności bajtów). - Komunikacja ze zdalnym hostem za pomocą funkcji API send() i recv() , a także funkcji ogólnego przeznaczenia write() i read() .
- Zamykanie każdego gniazda, które zostało otwarte po użyciu, za pomocą funkcji close().
0
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main ( void ) { struct sockaddr_in sa ; int res ; int SocketFD ; SocketFD = gniazdo ( PF_INET , SOCK_STREAM , IPPROTO_TCP ); if ( SocketFD == -1 ) { perror ( "nie można utworzyć gniazda" ); wyjście ( EXIT_FAILURE ); } memset ( & sa , , sizeof sa ); sa . rodzina_grzechów = AF_INET ; sa . sin_port = htons ( 1100 ); res = inet_pton ( AF_INET , "192.168.1.3" & sa . sin_addr ) ; if ( connect ( SocketFD , ( struct sockaddr * ) & sa , sizeof sa ) == -1 ) { perror ( "połączenie nie powiodło się" ); zamknij ( SocketFD ); wyjście ( EXIT_FAILURE ); } /* wykonaj operacje odczytu i zapisu ... */ zamknij ( SocketFD ); powrót EXIT_SUCCESS ; }
Przykład klient-serwer przy użyciu protokołu UDP
User Datagram Protocol (UDP) jest protokołem bezpołączeniowym bez gwarancji dostarczenia. Pakiety UDP mogą docierać w innej kolejności, wielokrotnie lub wcale. Ze względu na tę minimalną konstrukcję, UDP ma znacznie mniejszy narzut niż TCP. Bycie bezpołączeniowym oznacza, że nie istnieje pojęcie strumienia ani stałego połączenia między dwoma hostami. Takie dane są określane jako datagramy ( gniazda datagramowe ).
Przestrzeń adresowa UDP, przestrzeń numerów portów UDP (w terminologii ISO, TSAP ) , jest całkowicie rozłączna z przestrzenią portów TCP.
serwer
Aplikacja może skonfigurować serwer UDP na porcie numer 7654 w następujący sposób. Program zawiera nieskończoną pętlę, która odbiera datagramy UDP za pomocą funkcji recvfrom() .
0
0
#include <stdio.h> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <unistd.h> /* for close() for socket */ #include <stdlib.h> int main ( void ) { int sock ; struct sockaddr_in sa ; bufor znaków [ 1024 ]; ssize_t popraw rozmiar ; socklen_t fromlen ; memset ( & sa , rozmiar sa ) ; sa . rodzina_grzechów = AF_INET ; sa . adres_grzechu . s_addr = htonl ( INADDR_ANY ); sa . sin_port = hton ( 7654 ); fromlen = rozmiar sa ; skarpeta = gniazdo ( PF_INET , SOCK_DGRAM , IPPROTO_UDP );
if ( bind ( sock , ( struct sockaddr * ) & sa , sizeof sa ) == -1 ) {
perror ( "Błąd wiązania nie powiodło się" ); blisko ( skarpeta ); wyjście ( EXIT_FAILURE ); } for (;;) 0 { = recvfrom ( skarpeta , ( pusta * ) bufor , rozmiar bufora , ( struct sockaddr * ) & sa , & fromlen ) ;
recsize if ( recsize < ) { fprintf ( stderr , "%s \n " , strerror ( errno )); wyjście ( EXIT_FAILURE ); } printf ( "recsize: %d \n " , ( int ) recsize ); sen ( 1 ); printf ( "datagram: %.*s \n " , ( int ) recsize , buffer ); } }
Klient
Poniżej znajduje się program kliencki do wysyłania pakietu UDP zawierającego napis „Hello World!” na adres 127.0.0.1 na porcie numer 7654.
0
0
0
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet /in.h> #include <unistd.h> #include <arpa/inet.h> int main ( void ) { int skarpetka ; struct sockaddr_in sa ; int bytes_sent ; bufor znaków [ 200 ]; strcpy ( bufor , "Witaj świecie!" ); /* utwórz Internet, datagram, gniazdo używając UDP */ sock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ); if ( sock == -1 ) { /* jeśli nie udało się zainicjować gniazda, zakończ */ printf ( "Błąd podczas tworzenia gniazda" ); wyjście ( EXIT_FAILURE ); } /* Wyzeruj adres gniazda wyjściowego */ memset ( & sa , , sizeof sa ); /* Adres to IPv4 */ sa . rodzina_grzechów = AF_INET ; /* Adresy IPv4 to uint32_t, przekonwertuj ciąg reprezentujący oktety na odpowiednią wartość */ sa . adres_grzechu . s_addr = inet_addr ( "127.0.0.1" ); /* gniazda to unsigned shorts, htons(x) zapewnia, że x jest w sieciowej kolejności bajtów, ustaw port na 7654 */ sa . sin_port = hton ( 7654 ); 0 bytes_sent = sendto ( skarpeta , bufor , strlen ( bufor ), ( struct sockaddr * ) & sa , sizeof sa );
if ( bytes_sent < ) { printf ( "Błąd wysyłania pakietu: %s \n " , strerror ( errno )); wyjście ( EXIT_FAILURE ); } zamknij ( skarpetka ); /* zamknij gniazdo */ return ; }
W tym kodzie bufor jest wskaźnikiem do danych, które mają zostać wysłane, a długość_bufora określa rozmiar danych.
Standardowa definicja de iure interfejsu Sockets jest zawarta w standardzie POSIX, znanym jako:
- standard IEEE 1003.1-2001 Standard technologii informacyjnej — interfejs przenośnego systemu operacyjnego (POSIX).
- Open Group Technical Standard: Base Specifications, wydanie 6, grudzień 2001.
- ISO/IEC 9945:2002
Informacje na temat tego standardu i bieżących prac nad nim są dostępne na stronie internetowej firmy Austin .
Rozszerzenia IPv6 do interfejsu API gniazda podstawowego są udokumentowane w dokumentach RFC 3493 i RFC 3542.
- Stevens, W. Richard; Rago, Stephen A. (24 maja 2013). Zaawansowane programowanie w środowisku UNIX (wydanie trzecie). Addison-Wesley Professional . ISBN 978-0321637734 . Źródło 27 lutego 2015 r .
Linki zewnętrzne
- Dokumenty uzupełniające programisty systemu UNIX (PSD: 20-1)
- Przewodnik Beeja po programowaniu sieciowym - 2007
- Portowanie programów Berkeley Socket do Winsock - dokumentacja Microsoftu.
- Programowanie gniazd UNIX w C - najczęściej zadawane pytania - 1996
- Programowanie sieci w systemie Linux — Linux Journal , 1998