Lizdų programavimas yra būdas sujungti du tinklo mazgus, kad būtų galima bendrauti tarpusavyje. Vienas lizdas (mazgas) klausosi tam tikro IP prievado, o kitas lizdas pasiekia kitą, kad sukurtų ryšį. Serveris sudaro klausytojo lizdą, o klientas pasiekia serverį.
Lizdų programavimas plačiai naudojamas momentinių pranešimų programose, dvejetainio srautinio perdavimo ir dokumentų bendradarbiavimo internetinėse srautinio perdavimo platformose ir kt.
Pavyzdys
Šioje C programoje mes keičiamės vienu sveikinimo pranešimu tarp serverio ir kliento, kad parodytume kliento / serverio modelį.
serveris.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('%sn' buffer); send(new_socket hello strlen(hello) 0); printf('Hello message sentn'); // closing the connected socket close(new_socket); // closing the listening socket close(server_fd); return 0; }
klientas.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( 'nInvalid address/ Address not supported n'); return -1; } if ((status = connect(client_fd (struct sockaddr*)&serv_addr sizeof(serv_addr))) < 0) { printf('nConnection Failed n'); return -1; } // subtract 1 for the null // terminator at the end send(client_fd hello strlen(hello) 0); printf('Hello message sentn'); valread = read(client_fd buffer 1024 - 1); printf('%sn' buffer); // closing the connected socket close(client_fd); return 0; }
Kompiliavimas
gcc client.c -o clientgcc server.c -o server
Išvestis
Client:Hello message sentHello from serverServer:Hello from clientHello message sentSocket programavimo komponentai
1. Lizdai
Kištukiniai lizdai yra vienas iš pagrindinių programos komponentų, naudojamų norint pasiekti tinklą, kad būtų galima susisiekti su kitais procesais / mazgais tinkle. Tai tiesiog IP adreso ir prievado numerio derinys, kuris veikia kaip komunikacijos galutinis taškas.
Pavyzdys: 192.168.1.1:8080 kur dvi dvitaškiu atskirtos dalys žymi IP adresas (192.168.1.1) ir prievado numeris (8080).
Lizdų tipai:
- TCP lizdas (stream Socket): Užtikrina patikimą ryšiu pagrįstą ryšį (t. y. TCP protokolas ).
- UDP lizdas (Datagram Socket): Užtikrina greitesnį, bet nepatikimą ryšį be ryšio (t.y. UDP protokolas ).
2. Kliento-serverio modelis
The kliento-serverio modelis reiškia architektūrą, naudojamą lizdų programavimui, kai klientas ir serveris sąveikauja tarpusavyje, kad keistųsi informacija ar paslaugomis. Ši architektūra leidžia klientui siųsti paslaugų užklausas, o serveriui apdoroti ir siųsti atsakymus į šias paslaugų užklausas.
Serverio ir kliento modelio būsenos diagrama
Socket serverio ir kliento modelio būsenos diagramaLizdų programavimas C yra galingas būdas valdyti tinklo ryšį.
Serverio proceso kūrimas
Serveris sukuriamas atliekant šiuos veiksmus:
kada prasideda Q2
1. Lizdų kūrimas
Šis veiksmas apima lizdo sukūrimą naudojant socket() funkciją.
Parametrai:
- sockfd: lizdo deskriptorius sveikasis skaičius (kaip failo rankena)
- domenas: sveikasis skaičius nurodo ryšio domeną. Mes naudojame AF_ LOCAL, kaip apibrėžta POSIX standarte, ryšiui tarp procesų tame pačiame pagrindiniame kompiuteryje. Norėdami susisiekti tarp procesų skirtinguose pagrindiniuose kompiuteriuose, sujungtuose IPV4, naudojame AF_INET ir AF_I NET 6 procesams, sujungtiems IPV6.
- tipas: bendravimo tipas
SOCK_STREAM: TCP (orientuotas į patikimą ryšį)
SOCK_DGRAM: UDP (nepatikimas be ryšio) - protokolas: Interneto protokolo (IP) protokolo reikšmė, kuri yra 0. Tai yra tas pats skaičius, kuris rodomas protokolo lauke paketo IP antraštėje. (daugiau informacijos rasite man protokoluose)
sockfd = socket(domain type protocol)
2. Nustatykite lizdo parinktį
Tai padeda manipuliuoti lizdo parinktimis, nurodytomis failo deskriptoriumi sockfd. Tai visiškai neprivaloma, tačiau tai padeda pakartotinai naudoti adresą ir prievadą. Apsaugo nuo klaidų, tokių kaip: adresas jau naudojamas.
Csetsockopt(sockfd level optname optval socklen_t optlen);
3. Surišti
Sukūrus lizdą, funkcija bind() susieja lizdą su adresu ir prievado numeriu, nurodytu addr (custom data structure). Pavyzdiniame kode mes susiejame serverį su „localhost“, todėl IP adresui nurodyti naudojame INADDR_ANY.
C++bind(sockfd sockaddr *addr socklen_t addrlen);
Parametrai:
- sockfd : lizdo failo aprašas, sukurtas naudojant socket() funkciją.
- adr : žymeklis į struct sockaddr, kuriame yra IP adresas ir prievado numeris, kad būtų galima susieti lizdą.
- addrlen : adr struktūros ilgis.
4. Klausyk
Šiame žingsnyje serveris naudoja funkciją listen (), kuri perkelia serverio lizdą į pasyvų režimą, kuriame laukiama, kol klientas prisiartins prie serverio ir užmegs ryšį. Atsilikimas apibrėžia maksimalų ilgį, iki kurio gali padidėti laukiančių „sockfd“ jungčių eilė. Jei prisijungimo užklausa gaunama, kai eilė yra pilna, klientas gali gauti klaidą su nuoroda ECONNREFUSED.
Clisten(sockfd backlog);
Parametrai :
- sockfd : lizdo failo aprašas, sukurtas naudojant socket() funkciją.
- atsilikimas : skaičius, nurodantis eilės, kurioje yra laukiami ryšiai, dydį, kol serveris laukia, kol bus priimtas ryšys.
5. Priimk
Šiame žingsnyje serveris ištraukia pirmąją prisijungimo užklausą iš laukiančių jungčių, skirtų klausymosi lizdui, eilės sockfd sukuria naują prijungtą lizdą naudodamas priimti () funkcija ir grąžina naują failo deskriptorių, nurodantį tą lizdą. Šiuo metu tarp kliento ir serverio užmezgamas ryšys ir jie yra pasirengę perduoti duomenis.
Cnew_socket= accept(sockfd sockaddr *addr socklen_t *addrlen);
Parametrai:
- sockfd : socket failo deskriptorius, grąžintas socket () ir bind ().
- adr : žymeklis į struct sockaddr, kuriame bus kliento IP adresas ir prievado numeris.
- addrlen : rodyklė į kintamąjį, nurodantį adreso struktūros ilgį.
6. Siųsti/gauti
Šiame žingsnyje serveris gali siųsti arba gauti duomenis iš kliento.
Siųsti (): siųsti duomenis klientui
Csend(sockfd *buf len flags);
Parametrai:
- sockfd : socket failo deskriptorius, grąžintas socket() funkcijos.
- buf : žymeklis į buferį, kuriame yra siunčiami duomenys.
- tik : siunčiamų duomenų baitų skaičius.
- vėliavos : sveikasis skaičius, nurodantis įvairias duomenų siuntimo parinktis, paprastai 0 naudojamas kaip numatytasis elgesys.
Gauti () : gauti duomenis iš kliento.
Crecv( sockfd *buf len flags);
Parametrai:
- sockfd : socket failo deskriptorius, grąžintas socket() funkcijos.
- buf : rodyklė į buferį, kuriame yra saugomi duomenys.
- tik : siunčiamų duomenų baitų skaičius.
- vėliavos : sveikasis skaičius, nurodantis įvairias duomenų siuntimo parinktis, paprastai 0 naudojamas kaip numatytasis elgesys.
6. Uždaryti
Pasibaigus informacijos mainams, serveris uždaro lizdą naudodamas funkciją close() ir išleidžia sistemos išteklius.
Cclose(fd);
Parametrai:
- fd: lizdo failo aprašas.
Kliento pusės proceso kūrimas
Norėdami sukurti kliento procesą, atlikite toliau nurodytus veiksmus.
1. Kištukinio lizdo jungtis
Šis veiksmas apima lizdo sukūrimą, kuris atliekamas taip pat kaip ir serverio lizdo sukūrimas
2. Prisijunkite
Connect() sistemos iškvietimas sujungia failo deskriptoriaus sockfd nurodytą lizdą su adresu, nurodytu adresu addr. Serverio adresas ir prievadas nurodyti adr.
C++connect(sockfd sockaddr *addr socklen_t addrlen);
Parametrai
- sockfd : socket failo deskriptorius, grąžintas socket() funkcijos.
- adr : rodyklė į struct sockaddr, kurioje yra serverio IP adresas ir prievado numeris.
- addrlen : adr.
3. Siųsti/gauti
Šiame žingsnyje klientas gali siųsti arba gauti duomenis iš serverio, o tai atliekama naudojant send() ir recive() funkcijas, panašias į tai, kaip serveris siunčia/gauna duomenis iš kliento.
4. Uždaryti
Pasibaigus informacijos mainams, klientas taip pat turi uždaryti sukurtą lizdą ir išleisti sistemos išteklius naudodamas funkciją close() taip pat, kaip tai daro serveris.
Dažnos problemos ir jų taisymai programuojant Socket
- Ryšio gedimai: Norėdami išvengti ryšio trikdžių, turėtume užtikrinti, kad klientas bandytų prisijungti prie tinkamo IP adresas ir prievadas .
- Prievado surišimo klaidos: Šios klaidos atsiranda, kai prievadą jau naudoja kita programa pagal šį scenarijų susiejimas su tuo prievadu nepavyks. Pabandykite naudoti kitą prievadą arba uždarykite ankstesnę programą naudodami prievadą.
- Blokavimo lizdai: Pagal numatytuosius nustatymus lizdai blokuoja. Tai reiškia, kad skambučiai, tokie kaip accept() arba recv(), lauks neribotą laiką, jei nėra kliento ryšio ar duomenų. Jei reikia, galite nustatyti lizdą į neblokavimo režimą.