Programovanie soketov v C
Programovanie zásuviek je spôsob prepojenia dvoch uzlov v sieti na vzájomnú komunikáciu. Jeden soket (uzol) počúva na konkrétnom porte na IP, zatiaľ čo druhý soket siaha k druhému, aby vytvoril spojenie. Server vytvára soket načúvania, zatiaľ čo klient oslovuje server.
Socketové programovanie sa široko používa v aplikáciách na odosielanie okamžitých správ, binárne streamovanie a spolupráca na dokumentoch, online streamingové platformy atď.
Príklad
V tomto programe C si vymieňame jednu pozdravnú správu medzi serverom a klientom, aby sme demonštrovali model klient/server.
server.c
C #include #include #include #include #include #include #define PORT 8080 int main ( int argc char const * argv []) { int server_fd new_socket ; ssize_t valread ; struct sockaddr_in address ; int opt = 1 ; socklen_t addrlen = sizeof ( address ); char buffer [ 1024 ] = { 0 }; char * hello = 'Hello from server' ; // Creating socket file descriptor if (( server_fd = socket ( AF_INET SOCK_STREAM 0 )) < 0 ) { perror ( 'socket failed' ); exit ( EXIT_FAILURE ); } // Forcefully attaching socket to the port 8080 if ( setsockopt ( server_fd SOL_SOCKET SO_REUSEADDR | SO_REUSEPORT & opt sizeof ( opt ))) { perror ( 'setsockopt' ); exit ( EXIT_FAILURE ); } address . sin_family = AF_INET ; address . sin_addr . s_addr = INADDR_ANY ; address . sin_port = htons ( PORT ); // Forcefully attaching socket to the port 8080 if ( bind ( server_fd ( struct sockaddr * ) & address sizeof ( address )) < 0 ) { perror ( 'bind failed' ); exit ( EXIT_FAILURE ); } if ( listen ( server_fd 3 ) < 0 ) { perror ( 'listen' ); exit ( EXIT_FAILURE ); } if (( new_socket = accept ( server_fd ( struct sockaddr * ) & address & addrlen )) < 0 ) { perror ( 'accept' ); exit ( EXIT_FAILURE ); } // subtract 1 for the null // terminator at the end valread = read ( new_socket buffer 1024 - 1 ); printf ( '%s n ' buffer ); send ( new_socket hello strlen ( hello ) 0 ); printf ( 'Hello message sent n ' ); // closing the connected socket close ( new_socket ); // closing the listening socket close ( server_fd ); return 0 ; }
klient.c
C #include #include #include #include #include #define PORT 8080 int main ( int argc char const * argv []) { int status valread client_fd ; struct sockaddr_in serv_addr ; char * hello = 'Hello from client' ; char buffer [ 1024 ] = { 0 }; if (( client_fd = socket ( AF_INET SOCK_STREAM 0 )) < 0 ) { printf ( ' n Socket creation error n ' ); return -1 ; } serv_addr . sin_family = AF_INET ; serv_addr . sin_port = htons ( PORT ); // Convert IPv4 and IPv6 addresses from text to binary // form if ( inet_pton ( AF_INET '127.0.0.1' & serv_addr . sin_addr ) <= 0 ) { printf ( ' n Invalid address/ Address not supported n ' ); return -1 ; } if (( status = connect ( client_fd ( struct sockaddr * ) & serv_addr sizeof ( serv_addr ))) < 0 ) { printf ( ' n Connection Failed n ' ); return -1 ; } // subtract 1 for the null // terminator at the end send ( client_fd hello strlen ( hello ) 0 ); printf ( 'Hello message sent n ' ); valread = read ( client_fd buffer 1024 - 1 ); printf ( '%s n ' buffer ); // closing the connected socket close ( client_fd ); return 0 ; }
Zostavovanie
gcc client.c -o clientgcc server.c -o server
VýstupClient:Hello message sentHello from serverServer:Hello from clientHello message sentKomponenty programovania soketov
1. Zásuvky
Zásuvky sú jedným zo základných komponentov, ktoré program používa na prístup k sieti na komunikáciu s inými procesmi/uzlami v sieti. Je to jednoducho kombinácia IP adresy a čísla portu, ktorý funguje ako koncový bod pre komunikáciu.
Príklad: 192.168.1.1:8080 kde dve časti oddelené dvojbodkou predstavujú IP adresa (192.168.1.1) a číslo portu (8080).Typy zásuviek:
- TCP Socket (Stream Socket): Poskytuje spoľahlivú komunikáciu založenú na pripojení (t.j. TCP protokol ).
- Zásuvka UDP (zásuvka datagramu): Poskytuje rýchlejšiu, ale nespoľahlivú komunikáciu bez pripojenia (t.j. protokol UDP ).
2. Model klient-server
The model klient-server sa vzťahuje na architektúru používanú pri programovaní soketov, kde klient a server navzájom spolupracujú za účelom výmeny informácií alebo služieb. Táto architektúra umožňuje klientovi odosielať servisné požiadavky a serveru spracovávať a odosielať odpovede na tieto servisné požiadavky.
Stavový diagram pre model servera a klienta
Stavový diagram pre serverový a klientsky model Socketu Programovanie soketov v C je účinný spôsob, ako zvládnuť sieťovú komunikáciu.
Vytvorenie procesu na strane servera
Server sa vytvorí pomocou nasledujúcich krokov:
1. Vytvorenie zásuvky
Tento krok zahŕňa vytvorenie zásuvky pomocou funkcie socket().
Parametre:
- sockfd: deskriptor zásuvky celé číslo (ako popisovač súboru)
- doména: celé číslo určuje komunikačnú doménu. Na komunikáciu medzi procesmi na rovnakom hostiteľovi používame AF_ LOCAL, ako je definované v štandarde POSIX. Na komunikáciu medzi procesmi na rôznych hostiteľoch pripojených cez IPV4 používame AF_INET a AF_I NET 6 pre procesy pripojené cez IPV6.
- typ: typ komunikácie
SOCK_STREAM: TCP (orientované na spoľahlivé pripojenie)
SOCK_DGRAM: UDP (nespoľahlivé bez pripojenia) - protokol: Hodnota protokolu pre internetový protokol (IP), ktorá je 0. Je to rovnaké číslo, aké sa zobrazuje v poli protokolu v hlavičke IP paketu. (ďalšie podrobnosti nájdete v protokoloch man)
sockfd = socket ( domain type protocol )
2. Nastavte opt
Pomáha to pri manipulácii s voľbami pre soket, na ktorý odkazuje deskriptor súboru sockfd. Toto je úplne voliteľné, ale pomáha pri opätovnom použití adresy a portu. Zabraňuje chybám, ako napríklad: adresa sa už používa.
C setsockopt ( sockfd level optname optval socklen_t optlen );
3. Zviazať
Po vytvorení soketu funkcia bind() naviaže soket na adresu a číslo portu špecifikované v addr (vlastná dátová štruktúra). Vo vzorovom kóde naviažeme server na localhost, preto na špecifikáciu IP adresy používame INADDR_ANY.
C++ bind ( sockfd sockaddr * addr socklen_t addrlen );
Parametre:
- sockfd : deskriptor súboru soketu vytvorený pomocou funkcie socket().
- adr : ukazovateľ na štruktúru sockaddr, ktorá obsahuje IP adresu a číslo portu na spojenie soketu.
- addrlen : dĺžka štruktúry addr.
4. Počúvajte
V tomto kroku server používa funkciu listen(), ktorá uvedie soket servera do pasívneho režimu, v ktorom čaká, kým sa klient priblíži k serveru a vytvorí spojenie. Backlog definuje maximálnu dĺžku, do ktorej môže narásť front čakajúcich pripojení pre sockfd. Ak žiadosť o pripojenie príde, keď je front plný, klient môže dostať chybu s označením ECONNREFUSED.
C listen ( sockfd backlog );
Parametre :
- sockfd : deskriptor súboru soketu vytvorený pomocou funkcie socket().
- nevybavených vecí : číslo predstavujúce veľkosť frontu, v ktorom sa nachádzajú čakajúce pripojenia, kým server čaká na prijatie pripojenia.
5. Prijmite
V tomto kroku server extrahuje prvú požiadavku na pripojenie z frontu čakajúcich pripojení pre počúvajúci soket sockfd vytvorí nový pripojený soket pomocou prijať() a vráti nový deskriptor súboru odkazujúci na daný soket. V tomto bode je vytvorené spojenie medzi klientom a serverom a sú pripravení na prenos dát.
C new_socket = accept ( sockfd sockaddr * addr socklen_t * addrlen );
Parametre:
- sockfd : deskriptor súboru soketu vrátený funkciami socket() a bind().
- adr : ukazovateľ na štruktúru sockaddr, ktorá bude obsahovať IP adresu klienta a číslo portu.
- addrlen : ukazovateľ na premennú, ktorá určuje dĺžku štruktúry adresy.
6. Odoslať/prijať
V tomto kroku môže server odosielať alebo prijímať dáta od klienta.
Odoslať(): na odoslanie údajov klientovi
C send ( sockfd * buf len flags );
Parametre:
- sockfd : deskriptor súboru soketu vrátený funkciou socket().
- buf : ukazovateľ na vyrovnávaciu pamäť obsahujúcu údaje, ktoré sa majú odoslať.
- len : počet bajtov dát, ktoré sa majú odoslať.
- vlajky : celé číslo určujúce rôzne možnosti, ako sa údaje odosielajú, zvyčajne sa pre predvolené správanie používa 0.
Prijať() : na získanie údajov od klienta.
C recv ( sockfd * buf len flags );
Parametre:
- sockfd : deskriptor súboru soketu vrátený funkciou socket().
- buf : ukazovateľ na vyrovnávaciu pamäť obsahujúcu údaje, ktoré sa majú uložiť.
- len : počet bajtov dát, ktoré sa majú odoslať.
- vlajky : celé číslo určujúce rôzne možnosti, ako sa údaje odosielajú, zvyčajne sa pre predvolené správanie používa 0.
6. Zatvorte
Po dokončení výmeny informácií server zatvorí soket pomocou funkcie close() a uvoľní systémové prostriedky.
C close ( fd );
Parametre:
- fd: deskriptor súboru soketu.
Vytvorenie procesu na strane klienta
Pri vytváraní procesu na strane klienta postupujte podľa nasledujúcich krokov:
1. Zásuvkové pripojenie
Tento krok zahŕňa vytvorenie soketu, ktorý sa vykonáva rovnakým spôsobom ako vytvorenie soketu servera
2. Pripojte sa
Systémové volanie connect() spája soket, na ktorý odkazuje deskriptor súboru sockfd, na adresu zadanú addr. Adresa a port servera sú uvedené v adr.
C++ connect ( sockfd sockaddr * addr socklen_t addrlen );
Parametre
- sockfd : deskriptor súboru soketu vrátený funkciou socket().
- adr : ukazovateľ na štruktúru sockaddr obsahujúcu IP adresu servera a číslo portu.
- addrlen : veľkosť adr.
3. Odoslať/prijať
V tomto kroku môže klient odosielať alebo prijímať dáta zo servera, čo sa robí pomocou funkcií send() a recieve() podobne ako server odosiela/prijíma dáta od klienta.
4. Zatvorte
Po dokončení výmeny informácií musí klient tiež zatvoriť vytvorený soket a uvoľniť systémové prostriedky pomocou funkcie close() rovnakým spôsobom ako server.
Bežné problémy a ich opravy v programovaní soketov
- Zlyhania pripojenia: Aby sme sa vyhli zlyhaniam pripojenia, mali by sme zabezpečiť, aby sa klient pokúšal pripojiť k správnemu IP adresa a port .
- Chyby viazania portov: Tieto chyby sa vyskytujú, keď je port už používaný inou aplikáciou. V tomto scenári väzba na tento port zlyhá. Skúste použiť iný port alebo zatvorte predchádzajúcu aplikáciu pomocou portu.
- Blokovacie zásuvky: V predvolenom nastavení sú zásuvky blokované. To znamená, že volania ako accept() alebo recv() budú čakať donekonečna, ak neexistuje žiadne pripojenie klienta alebo údaje. V prípade potreby môžete zásuvku nastaviť do neblokovacieho režimu.