Ciele
- Pripraviť si prostredie pre prácu na cvičení
- Porozumieť soketom
- Porozumieť službe bind
- Porozumieť službe listen
- Porozumieť službe accept
- Porozumieť službe connect
- Naučiť sa zasielať, odosielať správy a zrušiť deskriptor súboru
- Pochopiť aplikáciu poznatkov v praxi
Postup
Krok 1: Úvod
Príprava
Pre prácu na cvičení si stiahnite súbor k cvičeniu.
Obsah súboru:
.
└── 10
├──u1_1.c
├──u1_2.c
└──Makefile
Poznámka
Programy je možné kompilovať pomocou make nazov_suboru.c
Napríklad:
make u1_1.c
Krok 2: Sokety
Model Klient - Server
Komunikačný mechanizmu socket je obojsmernou komunikačnou technológiou. Umožňuje komunikáciu medzi procesmi. Možno sním pracovať ako so súborom, má pridelený vlastný jedinečný deskriptor.
Jedným zo základných modelov pre komunikáciu medzi procesmi prostredníctvom socketov je model klient–server.Tento model je založený na existencii dvoch typov procesov:procesu-servera a procesu-klienta. Proces-servervykonáva pasívnu úlohu - čaká na požiadavky od klientských procesov, ktorýmposkytuje nejakú „službu“. Klienti, ktorí spolupracujú s jedným typom servera, môžu byť rôzneho typu a môžu sa navzájom líšiť používateľským prostredím.
Komunikačný systém | |
---|---|
IP adresa | sieťová adresa stroja, pomocou nej vedia komunikovať sieťové zariadenia |
Port | celočíselný identifikátor komunikujúceho procesu, na ktorom sú vybavované požiadavky procesov |
Protokol TCP/IP pozostáva zo skupiny protokolov, z ktorých pre prácu so socketmi na štvrtej vrstve TCP/IP modelu sa využívajú protokoly uvedené v nasledujúcej tabuľke.
TCP a UDP

Na strane procesu server (protokol TCP) musíme na rozdiel od procesu klient priradiť socketu adresu (službou bind()). Potom musíme vytvoriť front do ktorého sa budú ukladať požiadavky na spojenie (službou listen()). Požiadavky na spojenie musíme z frontu vyberať postupne (služba accept()). Ak vo fronte nie je žiadna požiadavka na spojenie, proces server počká (bude uspatý), kým nejaká požiadavka nedôjde. Služba accept() nám vráti nový socket, pomocou ktorého budeme komunikovať s procesom-klientom, ktorý sa pripája na proces server systémovým volaním connect().
Úloha 2.1
Pre podrobnejšie vysvetlenie príznakov modelu Klient - Server navštívte: wiki klient-server
Krok 3: Vytvorenie záverečného bodu komunikácie
Služba jadra socket()
Služba socket() vytvára záverečné body komunikácie.
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
Parametre služby socket:
- Parameter domain určuje spôsob adresácie komunikačných uzlov. Môžme to prirovnať k výberu sôsobu komunikácie medzi ľudmi (telefón, list, e-mail, skype).
Domény | Popis |
---|---|
PF_UNIX | Lokálna komunikácia |
PF_INET | IPv4 internetové protokoly |
PF_INET6 | IPv6 internetové protokoly |
PF_IPX | IPX-Nowell protokoly |
PF_APPLETALK | Appletalk DDP |
- Parameter type určuje typ socketu. Vieme ho prirovnať k službám mobilného operátora (SMS, hovory, internet, MMS).
Možné hodnoty | |
---|---|
SOCK_STREAM | spojená transportná slúžba, využíva sa vtedy ake chceme najprv vytvoriť spojenie medzi socketmi |
SOCK_DGRAM | nespojovaná slúžba, nie je žiadna záruka že správa bude doručená |
- Parameter protocol. Pre naše potreby bude tento parameter nastavený na nulu, čo znamená použitie defaultného protokolu. Ak budeme chcieť vytvoriť socket pre spojovo orientovanú komunikáciu, použijeme službu socket() s týmito parametrami:
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Návratová hodnota | |
---|---|
v prípade úspešného vykonania vráti socket | |
v prípade neúspešného vykonania vráti -1 |
Aplikácia služby v programe:
#include <sys/socket.h>
#include <stdio.h>
int main(){
int socket = socket(PF_INET, SOCK_STREAM,0);
//vytvorenie socketu
if(socket == -1){
perror("socket");
}
//kontrola sluzby socket()
else{
printf("Socket s id %d bol vytvoreny.\n",socket);
}
return 0;
}
Opis kódu:
Predchadzajúci fragment kódu vytvorí socket pre spojovanú komunikáciu s defaultne nastaveným protokolom.
Výpis z programu má vyzerať následovne:
Socket s id .. bol vytvorený.
Úloha 3.1
Čo ste mali namiesto ..
?
Úloha 3.2
Pre podrobnejšie vysvetlenie služby socket navštívte: man socket
Krok 4: Priradenie IP adresy
Služba jadra bind()
Služba bind() zviaže resp. priradí socketu IP adresu a port. Po zviazaní môžeme socket využívať na komunikáciu medzi procesmi vrámci počítačovej siete.
#include <sys/socket.h>
int bind (int socket, struct sockaddr *address, int address_len);
Parametre služby | |
---|---|
socket | špecifikuje socket(pomocou deskriptora), ktorý má byť zviazaný s adresou |
address | ukazuje na sockaddr štruktúru, formát ktorý je požadovaným správaním socketu |
address_len | určuje dľžku štruktúry sockaddr, určenú parametrom address |
Poznámka k parametrom: deskriptor sme získali pomocou služby socket() štruktúra sockaddr sa skladá zo štruktúr sockaddr a sockaddr_um
Návratová hodnota | |
---|---|
v prípade úspešného vykonania vráti 0 | |
v prípade neúspešného vykonania vráti -1 |
Opis štruktúry sockaddr:
Proces potrebuje na nadviazanie spojenia socket, IP adresu stroja a portu ktorý je na nej určený pre pripájanie. Adresa je súčasťou štruktúry:
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
Premenné štruktúry | Vysvetlenie |
---|---|
unsigned short sa_family | typ komunikačného socketu AF nie PF |
char sa_data[14] | 14 bytov protokolovej adresy |
Položka štruktúry sa_data[14] obsahuje cieľovú adresu a číslo portu pre daný socket. Pre IPv4 budeme používať odvodenú štruktúru sockaddr_in, ktorá je definovaná v hlavičkovom súbore
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
Premenné štruktúry | Vysvetlenie |
---|---|
short int sin_family | typ komunikačného socketu AF nie PF |
unsigned short int sin_port | 2 byty cislo portu |
struct in_addr sin_addr | 4 byty actual IP address |
unsigned char sin_zero[8] | zvysnych 8 bytov nulujeme |
Príklad použitia
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define MYPORT 3490 //port, na ktory sa budu uzivatelia pripajat
int main() {
int s;
struct sockaddr_in my_addr; /* struktura obasahujuca informacie o mojej adrese*/
int sin_size;
if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket"); //vytvorenie socketu
exit(1);
}
my_addr.sin_family = AF_INET; //naplnenie struktury sockaddr_in
my_addr.sin_port = htons(MYPORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero), 8);
//zviazanie socketu sluzbou bind()
if(bind(s,(struct sockaddr *)&my_addr, sizeof(struct sockaddr))==-1){
perror("bind");
exit(1);
}
}
Poznámka
Pomocou služby bind() je možné zviazať socket s IP adresou a portom
Opis kódu
Najprv bol vytvorený socket pre spojovanú komunikáciu s defaultne nastaveným protokolom pomocou služby socket()
Aby sa mohla zviazať IP adresa a port s službou bind()
, musí sa najprv naplniť štruktúra sockaddr_in
Všetky príkazy začínajúce my_addr slúžia na napľňanie štruktúry sockaddr_in.
Funkcia htons()
zabezpečí transformáciu endianity počítača na endianitu siete. Funkcia bzero()
nám doplní reťazec my_addr.sin_zero o 8 núl.
Rozlíšenie adresy
V prípade, že máme meno uzla (host), ktorý sa má podieľať na komunikácii, jeho IP adresu zistíme pomocou funkcie gethostbyname(char hostname), ktorá vracia smerník na štruktúru hostent, ktorej definícia je:
struct hostent {
char* h_name; //oficialne meno hostu
char** h_aliases; //smernik na zoznam aliasov (iných mien)
int h_addrtype; //typ adresy
int h_length; //dlzka adresy
char** h_addr_list; //smernik na zoznam adries ak ich ma viac
};
Postupnosť bajtov
Ak chceme získať IP adresu v čitateľnom tvare, použijeme funkciu inet_ntoa()
-htons() - krátke celé čísla z hostovej postupnosti do sieťovej (pre porty)
-ntohs() – krátke celé čísla zo sieťovej do hostovej postupnosti (pre porty)
-htonl() – dlhé celé čísla z hostovej do sieťovej postupnosti (pre IP adresy)
-ntohl() – dlhé celé čísla zo sieťovej do hostovej postupnosti (pre IP adresy)
Poznámka
Formovanie adresy pre internetové protokoly sa uskutočnuje pomocou štruktúry sockaddr_in.
Úloha 4.1
Pre podrobnejšie vysvetlenie navštívte man 2 bind
Otázky:
Uveďte názov služby jadra, ktorou priradíme adresu koncovému komunikačnému bodu v rámci zvoleného protokolu.
Správna odpoveď: bind
Uveďte názov zložky/prvku zodpovedajúceho záznamu (štruktúry) pre nastavenie portu koncového komunikačného bodu v rámci protokolu IP (viď 'man 7 ip'; len názov zložky/prvku, bez názvu záznamu)
Správna odpoveď: sin_port
Krok 5: Vytvorenie vyrovnávajúcej pamäti pre pripojenie
Služba jadra listen()
Služba listen()
vytvára vyrovnávajúcu pamäť pre uchovávanie požiadaviek o pripojenie. Ak je front plný a nejaký klient sa pokúsi pripojiť, bude spojenie odmietnuté.
#include <sys/socket.h>
int listen (int socket, int backlog);
Parametre | |
---|---|
socket | určuje jedinečný identifikátor socketu vytvorený službou socket() s adresou priradenou službou bind() |
backlog | určuje maximálne množstvo simultárnych požiadaviek na spojenie |
Poznámka k parametru backlog: Horný limit je špecifikovaný konštantou SOMAXCONN v hlavičkovom súbore
<sys/socket.h>
. Hodnota parametra backlog je nastavená štandardne na hodnotu 5.
Návratová hodnota | |
---|---|
v prípade úspešného vykonania vracia 0 | |
v prípade neúspešného vykonania vráti -1 |
Úloha 5.1
Pre podrobnejšie vysvetlenie navštívte man 2 listen
Otázky:
Krok 6: Výber požiadaviek
Služba jadra accept()
Služba jadra accept() využíva v procese server pre výber požiadaviek z fronty čakajúcich na spojenie a potvrdí ju. Pre každé prijaté spojenie sa vytvorí nový socket. Potom cez tento nový socket prebieha komunikácia. Ak nie sú ďalšie požiadavky na spojenie (front je prázdny), tak služba accept() uspí proces pokiaľ nie je prítomná ďalšia požiadavka na spojenie.
#include <sys/socket.h>
int accept(int socket, struct sockaddr *restrict addr, socklen_t *restrict len);
Parametre | |
---|---|
socket | určuje socket, ktorý bol vytvorený službou socket(), bol zviazaný s adresou a má vytvorený backlog službou listen() |
address | ukazuje na sockaddr štruktúru ktorá obsahuje IP adresu a port klientskeho procesu. Jej formát je určený doménou alebo požadovaným správaním socketu |
address_len | určuje dľžku štruktúry sockaddr, určenú parametrom address |
Poznámka
Ak je address nastavený na NULL tak je potom celý parameter ignrovaný.
Návratová hodnota | |
---|---|
v prípade úspešného vykonania vracia nezáporný descriptor(socket) | |
v prípade neúspešného vykonania vráti -1 |
Úloha 6.1
Pre podrobnejšie vysvetlenie navštívte man 2 listen
Otázky:
Krok 7: Vytvorenie spojenia
Služba jadra connect()
Služba jadra connect()
vytvára spojenie medzi dvoma procesmi. Táto služba vykonáva rôzne činnosti pre každý z nasledujúcich typov socketov:
-ak je socket SOCK_DGRAM, služba `connect()` vytvorí peer adresu. Peer adresa identifikuje socket ktorému sú zaslané všetky dáta následnou službou `send()`. Taktiež identifikuje socket, z ktorého majú byť dáta príjmané. Ale nie je žiadna záruka, že dáta budú doručené. Jedná sa o prenos bez vytvorenia spojenia medzi socketmi(nespojová služba).
-ak je socket SOCK_STREAM, služba `connect()` sa pokúša nadviazať spojenie so socketom špecifikovaným parametrom _serv_addr_ (spojovaná služba).
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
Parametre | |
---|---|
sockfd | špecifikuje deskriptor socketu |
address | ukazuje na sockaddr štruktúru, ktorá obsahuje IP adresu a port procesu na ktorý sa chceme pripojiť |
address_les | určuje dľžku štruktúry sockaddr, určenú parametrom address |
Návratová hodnota | |
---|---|
v prípade úspešného vykonania vracia 0 | |
v prípade neúspešného vykonania vráti -1 |
......................... rozpísanie služieb send a recv samostatne ako služby jadra +++ niečo ku close
Odosielanie a príjem dát
Na odosielanie dát slúži služba send()
.
int send(int s, const void *msg, size_t len, int flags);
Na príjem dát slúži služba recv()
.
int recv(int s, void *buf, size_t len, int flags);
Krok 8: Zaslanie a prijímanie správ
Služba jadra send()
Využíva sa na zaslanie správ do socketu. Služba môže byť využítá len vtedy, ak skocket je v pripojenom stave.
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
Služba jadra recv()
Využíva sa príjmanie správ z socketu.
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
Krok 9: Zatvorenie deskriptora súboru
SLužba jadra close()
Služba close() spôsobí, že deskriptor súboru už ďalej nebude ukazovať na daný súbor. To znamená, že tento deskriptor už nie je možné viac používať.
#include <unistd.h>
int close(int fd);
Úloha 9.1
Pre podrobnejšie vysvetlenie navštívte man 2 send man 2 recv man 2 close
Krok 10: Praktická aplikácia naučených poznatkov
Otázky
Aplikácia služieb v programe
Príklad pooužitia pre server
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#define PORT 8080
int main(int argc, char const *argv[]) {
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
char *hello = "*** Sprava odoslana z programu server.c ***";
//Vytvorenie deskriptoru pre socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0){
perror("socket failed");
exit(EXIT_FAILURE);
}
//Napojenie socketu na port 8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &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);
//Priradenie IP adresy socketu
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, (socklen_t*)&addrlen))<0){
perror("accept");
exit(EXIT_FAILURE);
}
valread = read(new_socket , buffer, 1024);
printf("Pocet precitanych bajtov: %d.\nSprava: %s.\n",valread, buffer);
send(new_socket , hello , strlen(hello) , 0);
printf("Sprava so znenim:\n*** Sprava odoslana z programu server.c ***\nbola odoslana klientovi.\n");
return 0;
}
Príklad použitia pre klienta
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define PORT 8080
int main(int argc, char const *argv[]){
int sock = 0, valread;
struct sockaddr_in serv_addr;
char *hello = "*** Sprava odoslana z programu client.c ***";
char buffer[1024] = {0};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket failed");
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){
perror("Nespravna IP adresa alebo tato adresa nie je podporovana.");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
perror("Spojenie zlyhalo.");
return -1;
}
send(sock , hello , strlen(hello) , 0 );
printf("Sprava so znenim:\n*** Sprava odoslana z programu client.c ***\nbola odoslana serveru.\n");
valread = read( sock , buffer, 1024);
printf("Pocet precitanych bajtov: %d.\nSprava: %s.\n",valread, buffer);
return 0;
}
........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Nadpis: Networking Týždeň: 10 Týždeň-zverejnenia: 1
Krok 11: Úvod
Príprava
Pre prácu na cvičení si stiahnite súbor k cvičeniu.
Obsah súboru:
.
└── 10
├──u1_1.c
├──u1_2.c
└──Makefile
Poznámka
Programy je možné kompilovať pomocou make nazov_suboru.c
Napríklad:
make u1_1.c
Krok 12: Sokety
Model Klient - Server
Komunikačný mechanizmus socket je obojsmernou komunikačnou technológiou. Umožňuje komunikáciu medzi procesmi. Možno s ním pracovať ako so súborom, má pridelený vlastný jedinečný deskriptor.
Jedným zo základných modelov pre komunikáciu medzi procesmi prostredníctvom socketov je model klient–server. Tento model je založený na existencii dvoch typov procesov: procesu-servera a procesu-klienta. Proces-server vykonáva pasívnu úlohu - čaká na požiadavky od klientských procesov, ktorým poskytuje nejakú „službu“. Klienti, ktorí spolupracujú s jedným typom servera, môžu byť rôzneho typu a môžu sa navzájom líšiť používateľským prostredím.
Komunikačný systém | |
---|---|
IP adresa | sieťová adresa stroja, pomocou nej vedia komunikovať sieťové zariadenia |
Port | celočíselný identifikátor komunikujúceho procesu, na ktorom sú vybavované požiadavky procesov |
Protokol TCP/IP pozostáva zo skupiny protokolov, z ktorých pre prácu so socketmi na štvrtej vrstve TCP/IP modelu sa využívajú protokoly uvedené v nasledujúcej tabuľke.
TCP a UDP

Na rozdiel od procesu klient je potrebné na strane procesu server priradiť socketu adresu pomocou služby bind(). Následne je potrebné vytvoriť pomocou služby listen() front, ktorý bude slúžiť na ukladanie požiadaviek na spojenie. Službou accept() sa postupne vyberajú požiadavky na spojenie z frontu. Ak vo fronte nie je žiadna požiadavka na spojenie, proces server čaká - je uspatý. V takomto stave je až do momentu, pokiaľ nejaká požiadavka nedôjde. Služba accept() vracia nový socket, pomocou ktorého je možné komunikovať medzi procesom a klientom, ktorý sa pripája na proces pomocou služby connect().
Úloha 12.1
Pre podrobnejšie vysvetlenie príznakov modelu Klient - Server navštívte: wiki klient-server
Krok 13: Vytvorenie záverečného bodu komunikácie
Služba jadra socket()
Služba socket() vytvára záverečné body komunikácie.
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
Parametre služby socket:
- Parameter domain určuje spôsob adresácie komunikačných uzlov. Môžeme to prirovnať k výberu spôsobu komunikácie medzi ľudmi (telefón, list, e-mail, skype).
Domény | Popis |
---|---|
PF_UNIX | Lokálna komunikácia |
PF_INET | IPv4 internetové protokoly |
PF_INET6 | IPv6 internetové protokoly |
PF_IPX | IPX-Nowell protokoly |
PF_APPLETALK | Appletalk DDP |
- Parameter type určuje typ socketu. Vieme ho prirovnať k službám mobilného operátora (SMS, hovory, internet, MMS).
Možné hodnoty | |
---|---|
SOCK_STREAM | spojená transportná slúžba, využíva sa vtedy ake chceme najprv vytvoriť spojenie medzi socketmi |
SOCK_DGRAM | nespojovaná slúžba, nie je žiadna záruka že správa bude doručená |
- Parameter protocol. Pre naše potreby bude tento parameter nastavený na nulu, čo znamená použitie defaultného protokolu. Ak budeme chcieť vytvoriť socket pre spojovo orientovanú komunikáciu, použijeme službu socket() s týmito parametrami:
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Návratová hodnota | |
---|---|
V prípade úspešného vykonania vráti socket. | |
V prípade neúspešného vykonania vráti -1. |
Aplikácia služby v programe:
#include <sys/socket.h>
#include <stdio.h>
int main(){
//Vytvorenie socketu:
int socket = socket(PF_INET, SOCK_STREAM,0);
//Kontrola správnosti vykonania služby socket():
if(socket == -1){
perror("socket");
}
else{
printf("Socket s id %d bol vytvoreny.\n",socket);
}
return 0;
}
Opis kódu:
Predchadzajúci fragment kódu vytvorí socket pre spojovanú komunikáciu s defaultne nastaveným protokolom.
Výpis z programu má vyzerať následovne:
Socket s id .. bol vytvorený.
Úloha 13.1
Čo ste mali namiesto ..
?
Úloha 13.2
Pre podrobnejšie vysvetlenie služby socket navštívte: man socket
Krok 14: Priradenie IP adresy
Služba jadra bind()
Služba bind() zviaže resp. priradí socketu IP adresu a port. Po zviazaní môžeme socket využívať na komunikáciu medzi procesmi v rámci počítačovej siete.
#include <sys/socket.h>
int bind (int socket, struct sockaddr *address, int address_len);
Parametre služby | |
---|---|
socket | špecifikuje socket(pomocou deskriptora), ktorý má byť zviazaný s adresou |
address | ukazuje na sockaddr štruktúru, formát ktorý je požadovaným správaním socketu |
address_len | určuje dĺžku štruktúry sockaddr, určenú parametrom address |
Poznámka k parametrom: Deskriptor sme získali pomocou služby socket(). Štruktúra sockaddr sa skladá zo štruktúr sockaddr a sockaddr_um.
Návratová hodnota | |
---|---|
V prípade úspešného vykonania vráti 0. | |
V prípade neúspešného vykonania vráti -1. |
Opis štruktúry sockaddr:
Proces potrebuje na nadviazanie spojenia: socket IP adresu stroja a portu, ktorý je na nej určený pre pripájanie.
Adresa je súčasťou štruktúry:
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
Premenné štruktúry | Vysvetlenie |
---|---|
unsigned short sa_family | typ komunikačného socketu AF nie PF |
char sa_data[14] | 14 bytov protokolovej adresy |
Položka štruktúry sa_data[14] obsahuje cieľovú adresu a číslo portu pre daný socket. Pre IPv4 budeme používať odvodenú štruktúru sockaddr_in, ktorá je definovaná v hlavičkovom súbore <netinet/in.h>
v tvare:
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
Premenné štruktúry | Vysvetlenie |
---|---|
short int sin_family | typ komunikačného socketu AF nie PF |
unsigned short int sin_port | 2 byty - číslo portu |
struct in_addr sin_addr | 4 byty - actual IP address |
unsigned char sin_zero[8] | zvyšných 8 bytov nulujeme |
Príklad použitia
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define MYPORT 3490 //Port, na ktorý sa budú používateľlia pripájať.
int main() {
int s;
struct sockaddr_in my_addr; //Štruktúra obsahujúca informácia o mojej adrese.
int sin_size;
//Vytvorenie socketu:
if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
my_addr.sin_family = AF_INET; //naplnenie struktury sockaddr_in
my_addr.sin_port = htons(MYPORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
//Nulovanie zvyšných bytov:
bzero(&(my_addr.sin_zero), 8);
//Zviazanie socketu službou bind():
if(bind(s,(struct sockaddr *)&my_addr, sizeof(struct sockaddr))==-1){
perror("bind");
exit(1);
}
}
Poznámka
Pomocou služby bind() je možné zviazať socket s IP adresou a portom
Opis kódu
Najprv bol vytvorený socket pre spojovanú komunikáciu s defaultne nastaveným protokolom pomocou služby socket()
Aby sa mohla zviazať IP adresa a port so službou bind()
, musí sa najprv naplniť štruktúra sockaddr_in
Všetky služby začínajúce my_addr slúžia na napĺňanie štruktúry sockaddr_in.
Funkcia htons()
zabezpečí transformáciu endianity počítača na endianitu siete. Funkcia bzero()
nám doplní reťazec my_addr.sin_zero o 8 núl.
Rozlíšenie adresy
V prípade, že máme meno uzla (host), ktorý sa má podieľať na komunikácii, jeho IP adresu zistíme pomocou funkcie gethostbyname(char hostname), ktorá vracia smerník na štruktúru hostent, ktorej definícia je nasledovná:
struct hostent {
char* h_name; //Oficiálne meno hostu
char** h_aliases; //Smerník na zoznam aliasov (iných mien)
int h_addrtype; //Typ adresy
int h_length; //Dĺžka adresy
char** h_addr_list; //Smerník na zoznam adries ak ich má viac
};
Postupnosť bajtov
Ak chceme získať IP adresu v čitateľnom tvare, použijeme funkciu inet_ntoa()
-htons() - krátke celé čísla z hostovej postupnosti do sieťovej (pre porty)
-ntohs() – krátke celé čísla zo sieťovej do hostovej postupnosti (pre porty)
-htonl() – dlhé celé čísla z hostovej do sieťovej postupnosti (pre IP adresy)
-ntohl() – dlhé celé čísla zo sieťovej do hostovej postupnosti (pre IP adresy)
Poznámka
Formovanie adresy pre internetové protokoly sa uskutočnuje pomocou štruktúry sockaddr_in.
Úloha 14.1
Pre podrobnejšie vysvetlenie navštívte man 2 bind
Krok 15: Vytvorenie vyrovnávajúcej pamäti pre pripojenie
Služba jadra listen()
Služba listen()
vytvára vyrovnávajúcu pamäť pre uchovávanie požiadaviek o pripojenie. Ak je front plný a nejaký klient sa pokúsi pripojiť, bude spojenie odmietnuté.
#include <sys/socket.h>
int listen (int socket, int backlog);
Parametre | |
---|---|
socket | určuje jedinečný identifikátor socketu vytvorený službou socket() s adresou priradenou službou bind() |
backlog | určuje maximálne množstvo simultárnych požiadaviek na spojenie |
Poznámka
Horný limit parametru backlog je špecifikovaný konštantou SOMAXCONN v hlavičkovom súbore <sys/socket.h>
. Hodnota parametra backlog je nastavená štandardne na hodnotu 5.
Návratová hodnota | |
---|---|
V prípade úspešného vykonania vracia 0. | |
V prípade neúspešného vykonania vráti -1. |
Úloha 15.1
Pre podrobnejšie vysvetlenie navštívte man 2 listen.
Krok 16: Výber požiadaviek
Služba jadra accept()
Služba jadra accept() sa využíva v procese server pre výber požiadaviek z fronty čakajúcich na spojenie a potvrdzuje ich. Pre každé prijaté spojenie sa vytvorí nový socket. Potom cez tento nový socket prebieha komunikácia. Ak nie sú ďalšie požiadavky na spojenie (front je prázdny), tak služba accept() uspí proces pokiaľ nie je prítomná ďalšia požiadavka na spojenie.
#include <sys/socket.h>
int accept(int socket, struct sockaddr *restrict addr, socklen_t *restrict len);
Parametre | |
---|---|
socket | určuje socket, ktorý bol vytvorený službou socket(), bol zviazaný s adresou a má vytvorený backlog službou listen() |
address | ukazuje na sockaddr štruktúru ktorá obsahuje IP adresu a port klientskeho procesu. Jej formát je určený doménou alebo požadovaným správaním socketu |
address_len | určuje dĺžku štruktúry sockaddr, určenú parametrom address |
Poznámka
Ak je address nastavený na NULL tak je potom celý parameter ignrovaný.
Návratová hodnota | |
---|---|
V prípade úspešného vykonania vracia nezáporný descriptor(socket). | |
V prípade neúspešného vykonania vráti -1. |
Úloha 16.1
Pre podrobnejšie vysvetlenie navštívte man 2 listen.
Krok 17: Vytvorenie spojenia
Služba jadra connect()
Služba jadra connect()
vytvára spojenie medzi dvoma procesmi. Táto služba vykonáva rôzne činnosti pre každý z nasledujúcich typov socketov:
-ak je socket SOCK_DGRAM, služba connect()
vytvorí peer adresu. Peer adresa identifikuje socket, ktorému sú zaslané všetky dáta službou send()
. Taktiež identifikuje socket, z ktorého majú byť dáta prijímané. Ale nie je žiadna záruka, že dáta budú doručené. Jedná sa o prenos bez vytvorenia spojenia medzi socketmi(nespojovaná služba),
-ak je socket SOCK_STREAM, služba connect()
sa pokúša nadviazať spojenie so socketom špecifikovaným parametrom serv_addr (spojovaná služba).
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
Parametre | |
---|---|
sockfd | špecifikuje deskriptor socketu |
address | ukazuje na sockaddr štruktúru, ktorá obsahuje IP adresu a port procesu na ktorý sa chceme pripojiť |
address_les | určuje dľžku štruktúry sockaddr, určenú parametrom address |
Návratová hodnota | |
---|---|
V prípade úspešného vykonania vracia 0. | |
V prípade neúspešného vykonania vráti -1. |
Krok 18: Zasielanie, prijímanie správ a zrušenie deskriptora súboru
Služba jadra send()
Využíva sa na zaslanie správ do socketu. Služba môže byť využítá len vtedy, ak skocket je v pripojenom stave.
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
Služba jadra recv()
Využíva sa prijímanie správ zo socketu.
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
Služba jadra close()
Služba close() spôsobí, že deskriptor súboru už ďalej nebude ukazovať na daný súbor. To znamená, že tento deskriptor už nie je možné viac používať.
#include <unistd.h>
int close(int fd);
Úloha 18.1
Pre podrobnejšie vysvetlenie navštívte man 2 send man 2 recv man 2 close.
Príklad pooužitia pre server
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#define PORT 8080
int main(int argc, char const *argv[]) {
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
char *hello = "*** Sprava odoslana z programu server.c ***";
//Vytvorenie deskriptoru pre socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0){
perror("socket failed");
exit(EXIT_FAILURE);
}
//Napojenie socketu na port 8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &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);
//Priradenie IP adresy socketu
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, (socklen_t*)&addrlen))<0){
perror("accept");
exit(EXIT_FAILURE);
}
valread = read(new_socket , buffer, 1024);
printf("Pocet precitanych bajtov: %d.\nSprava: %s.\n",valread, buffer);
send(new_socket , hello , strlen(hello) , 0);
printf("Sprava so znenim:\n*** Sprava odoslana z programu server.c ***\nbola odoslana klientovi.\n");
return 0;
}
Príklad použitia pre klienta
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define PORT 8080
int main(int argc, char const *argv[]){
int sock = 0, valread;
struct sockaddr_in serv_addr;
char *hello = "*** Sprava odoslana z programu client.c ***";
char buffer[1024] = {0};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket failed");
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){
perror("Nespravna IP adresa alebo tato adresa nie je podporovana.");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
perror("Spojenie zlyhalo.");
return -1;
}
send(sock , hello , strlen(hello) , 0 );
printf("Sprava so znenim:\n*** Sprava odoslana z programu client.c ***\nbola odoslana serveru.\n");
valread = read( sock , buffer, 1024);
printf("Pocet precitanych bajtov: %d.\nSprava: %s.\n",valread, buffer);
return 0;
}
Ciele
- Pripraviť si prostredie pre prácu na cvičení
- Porozumieť soketom
- Porozumieť službe bind
- Porozumieť službe listen
- Porozumieť službe accept
- Porozumieť službe connect
- Naučiť sa zasielať a odosielať správy
- Naučiť sa zatvoriť deskriptor súboru
- Pochopiť aplikáciu poznatkov v praxi