Soketi za Udp. Soketi Kuunda Soketi ya Kusikiliza ya TCP

Programu zinazotumia TCP na UDP ni tofauti kimsingi kwa sababu UDP ni itifaki ya datagram isiyotegemewa, isiyo na muunganisho na kimsingi ni tofauti na uhamishaji unaoegemea muunganisho, mkondo-wa-kuaminika wa TCP. Hata hivyo, kuna matukio ambapo ni mantiki kutumia UDP badala ya TCP. Tunazingatia kesi kama hizo katika Sehemu ya 22.4. Baadhi programu maarufu imejengwa kwa kutumia UDP, kama vile DNS (Domain Mfumo wa Jina- Mfumo wa Jina la Kikoa), NFS (Mfumo wa Faili ya Mtandao) na SNMP (Itifaki Rahisi ya Usimamizi wa Mtandao).

Katika Mtini. Mchoro 8.1 unaonyesha wito wa utendakazi kwa mpango wa kawaida wa seva ya mteja wa UDP. Mteja hajaanzisha muunganisho kwenye seva. Badala yake, mteja hutuma tu datagram kwa seva kwa kutumia kitendakazi cha sendto (kilichoelezewa katika sehemu inayofuata), ambayo huchukua anwani ya mpokeaji (seva) kama hoja. Vivyo hivyo, seva haianzishi muunganisho na mteja. Badala yake, seva huita tu kazi ya recvfrom, ambayo inasubiri data kufika kutoka kwa mteja fulani. Kazi ya recvfrom inarudisha anwani ya mteja (kwa ya itifaki hii) pamoja na datagram, ili seva iweze kutuma jibu kwa mteja haswa aliyetuma datagram.

Mchele. 8.1. Utendakazi wa tundu kwa mfano wa seva ya mteja wa UDP

Mchoro 8.1 unaonyesha mchoro wa muda wa hali ya kawaida ya kubadilishana data ya UDP kati ya mteja na seva. Tunaweza kulinganisha mfano huu na ubadilishanaji wa kawaida wa TCP ulioonyeshwa kwenye Kielelezo. 4.1.

Katika sura hii, tutaelezea vipengele vipya vinavyotumiwa na soketi za UDP, recvfrom na sendto, na tutarekebisha muundo wetu wa seva ya mteja ili kutumia UDP. Pia tutaangalia kutumia kitendakazi cha kuunganisha na tundu la UDP na dhana ya makosa ya asynchronous.

8.2. recvfrom na sendto vitendaji

Kazi hizi mbili ni sawa na kazi za kawaida za kusoma na kuandika, lakini zinahitaji hoja tatu za ziada.

ssize_t recvfrom(int sockfd , void * buff , size_t nbytes , bendera za int ,

struct sockaddr * kutoka , socklen_t * adderlen);

ssize_t sendto(int sockfd, const void * buff, size_t nbytes, bendera za int,

const struct sockaddr * to , socklen_t addrlen);

Chaguo zote mbili za kukokotoa hurejesha idadi ya baiti zilizoandikwa au kusomwa kwenye mafanikio, -1 kwa makosa.

Hoja tatu za kwanza, sockfd , buff , na nbytes , ni sawa na hoja tatu za kwanza za vipengele vya kusoma na kuandika: mpini, kielekezi cha bafa ya kusoma au kuandikia, na idadi ya baiti za kusoma au kuandika. .

Tutashughulikia hoja ya bendera katika Sura ya 14, ambapo tunaangalia vitendaji vya recv , send , recvmsg , na sendmsg, kwa sababu kwa sasa tuko katika mfumo wetu. mfano rahisi hazihitajiki. Kwa sasa tutaweka hoja za bendera kuwa sifuri.

Hoja ya kukokotoa kwa kutuma ni muundo wa anwani ya tundu iliyo na anwani ya itifaki (kama vile anwani ya IP na nambari ya mlango) ya lengwa. Saizi ya muundo huu wa anwani ya tundu imebainishwa na hoja ya addrlen. Chaguo za kukokotoa za recvform hujaza muundo wa anwani ya tundu unaoelekezwa na kutoka kwa hoja na anwani ya itifaki ya mtumaji wa datagram. Idadi ya baiti zilizohifadhiwa katika muundo wa anwani ya tundu pia hurejeshwa kwa mchakato wa kupiga simu kama nambari kamili inavyoelekezwa na hoja ya addrlen. Kumbuka kuwa hoja ya mwisho kwa sendto ni thamani kamili, ilhali hoja ya mwisho kwa recvfrom ni kielekezi cha thamani kamili (hoja ya matokeo ya thamani).

Hoja mbili za mwisho kwa chaguo za kukokotoa za recvfrom ni sawa na hoja mbili za mwisho za chaguo la kukokotoa la kukubali: yaliyomo katika muundo wa anwani ya soketi baada ya kukamilika hutuambia ni nani aliyetuma datagramu (katika kesi ya UDP) au ni nani aliyeanzisha muunganisho (katika kesi ya TCP). Hoja mbili za mwisho za kitendakazi cha sendto ni sawa na hoja mbili za mwisho kwa kazi ya kuunganisha: tunajaza muundo wa anwani ya tundu na anwani ya itifaki ya mpokeaji datagram (katika kesi ya UDP) au anwani ya mwenyeji ambayo uunganisho utaanzishwa (katika kesi ya TCP).

Chaguo za kukokotoa zote mbili hurudi kama chaguo za kukokotoa huthamini urefu wa data iliyosomwa au iliyoandikwa. Katika matumizi ya kawaida ya chaguo za kukokotoa za recvfrom na itifaki ya datagram, thamani ya kurejesha ni kiasi cha data ya mtumiaji katika datagramu iliyopokelewa.

Datagram inaweza kuwa na urefu wa sifuri. Kwa UDP, hii hurejesha datagram ya IP iliyo na kichwa cha IP (kawaida baiti 20 za IPv4 au baiti 40 za IPv6), kichwa cha UDP cha baiti 8, na hakuna data. Hii pia inamaanisha kuwa thamani isiyo na maana iliyorejeshwa kutoka kwa recvfrom inakubalika kikamilifu kwa itifaki ya datagram: haionyeshi kuwa mtu mwingine amefunga muunganisho, kama ingekuwa hivyo wakati wa kurudi. thamani ya sifuri kutoka kwa kazi ya kusoma kwenye tundu la TCP. Kwa kuwa itifaki ya UDP hailengi muunganisho, hakuna tukio kama vile kufungwa kwa muunganisho.

Ikiwa kutoka kwa hoja hadi recvfrom ni kielekezi kisicho na maana, basi hoja ya urefu inayolingana (addrlen) lazima pia iwe kielekezi kisicho na maana, kumaanisha kwamba hatuvutiwi na anwani ya mtumaji wa data.

Vitendaji vyote viwili vya recvfrom na sendto vinaweza kutumika na TCP, ingawa kwa kawaida si lazima.

8.3. Seva ya mwangwi ya UDP: kazi kuu

Sasa tutarekebisha muundo wetu rahisi wa seva ya mteja kutoka Sura ya 5 kwa kutumia UDP. Mchoro wa simu za utendaji katika mteja wetu wa UDP na programu za seva huonyeshwa kwenye Mtini. 8.1. Katika Mtini. 8.2 inaonyesha vitendaji vilivyotumika. Orodha ya 8.1 inaonyesha kazi kuu ya seva.

Mchele. 8.2. Mfano rahisi wa seva ya mteja kwa kutumia UDP

Kuorodhesha 8.1. Seva ya mwangwi ya UDP

//udpcliserv/udpserv01.с

1 #pamoja na "unp.h"

3 nyumba ya ulinzi (int argc, char **argv)

6 struct sockaddr_in servaddr, cliaddr;

7 sockfd = Soketi(AF_INET, SOCK_DGRAM, 0);

8 bzero(&servaddr, sizeof(servaddr));

9 servaddr.sin_family = AF_INET;

10 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

12 Bind(sockfd, (SA*)&servaddr, sizeof(servaddr));

13 dg_echo(sodkfd, (SA*)&cliaddr, sizeof(cliaddr));

Unda tundu la UDP, funga kwenye mlango unaojulikana kwa kutumia kitendakazi cha kumfunga

7-12 Tunaunda soketi ya UDP kwa kubainisha SOCK_DGRAM (soketi ya datagram ya IPv4) kama hoja ya pili ya chaguo za kukokotoa tundu. Kama ilivyo katika mfano wa seva ya TCP, anwani ya IPv4 ya chaguo za kukokotoa imebainishwa kama INADDR_ANY , na nambari ya mlango inayojulikana ya seva ni SERV_PORT isiyobadilika kutoka kwa kichwa cha unp.h.

13 Kitendakazi cha dg_echo basi huitwa kushughulikia ombi la mteja na seva.

8.4. Seva ya mwangwi ya UDP: kazi ya dg_echo

Orodha ya 8.2 inaonyesha kazi ya dg_echo.

Kuorodhesha 8.2. dg_echo kazi: kamba za mwangwi kwenye soketi ya datagram

1 #pamoja na "unp.h"

3 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)

6 socklen_t len;

7 char mesg;

10 n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);

11 Sendto(sockfd, mesg, n, 0, pcliaddr, len);

Kusoma datagram, kutafakari kwa mtumaji

8-12 Chaguo hili la kukokotoa ni kitanzi rahisi ambamo datagramu inayofuata inayofika kwenye mlango wa seva inasomwa na chaguo za kukokotoa za recvfrom na kurejeshwa kwa kutumia kitendakazi cha sendto.

Licha ya unyenyekevu wa kazi hii, kuna mambo kadhaa ya kuzingatia maelezo muhimu. Kwanza, chaguo hili la kukokotoa halijakamilika kamwe. Kwa sababu UDP ni itifaki isiyo na muunganisho, hakuna alama sawa na bendera ya mwisho wa faili inayotumika katika TCP.

Pili, kazi hii hukuruhusu kuunda seva ya serial, badala ya ile inayofanana, ambayo tulipokea katika kesi ya TCP. Kwa kuwa hakuna simu ya kazi ya uma, mchakato mmoja wa seva hushughulikia usindikaji wote wa mteja. Kwa ujumla, seva nyingi za TCP ziko sambamba, na seva nyingi za UDP ni za mfululizo.

Kwa soketi katika kiwango cha UDP, datagramu zimeakibishwa kwa njia ya foleni. Hakika, kila soketi ya UDP ina bafa ya kupokea, na kila datagramu inayofika kwenye tundu hilo huwekwa kwenye bafa yake ya kupokea. Mchakato unapoita chaguo za kukokotoa recvfrom, datagramu inayofuata kutoka kwa bafa inarejeshwa kwa mchakato katika mpangilio wa FIFO (Kwanza Katika, Kwanza). Kwa hivyo, ikiwa datagramu nyingi hufika kwenye tundu kabla ya mchakato kusoma data iliyopangwa tayari kwa tundu, basi datagramu zinazoingia huongezwa kwa bafa ya tundu. Lakini bafa hii ina ukubwa mdogo. Tulijadili saizi hii na jinsi ya kuiongeza kwa kutumia chaguo la tundu la SO_RCVBUF katika Sehemu ya 7.5.

Katika Mtini. Mchoro 8.3 unaonyesha ujumuishaji wa muundo wa seva ya mteja wa TCP kutoka Sura ya 5, ambapo wateja wawili huanzisha miunganisho kwenye seva.

Mchele. 8.3. Ujumla wa mfano wa seva ya mteja wa TCP na wateja wawili

Kuna soketi mbili zilizoambatishwa hapa, na kila soketi zilizoambatishwa kwenye nodi ya seva ina bafa yake ya kupokea. Katika Mtini. Mchoro 8.4 unaonyesha kisa ambapo wateja wawili wanatuma datagramu kwa seva ya UDP.

Mchele. 8.4. Ujumla wa mfano wa seva ya mteja wa UDP na wateja wawili

Kuna mchakato mmoja tu wa seva, na ina soketi moja ambayo seva hupokea datagramu zote zinazoingia na ambayo hutuma majibu yote. Soketi hii ina bafa ya kupokea ambamo datagramu zote zinazoingia huwekwa.

Kazi kuu katika Orodha ya 8.1 inategemea itifaki (huunda tundu la familia la AF_INET na kisha kutenga na kuanzisha muundo wa anwani ya tundu ya IPv4), lakini kazi ya dg_echo haitegemei itifaki. Sababu ya dg_echo kazi ni itifaki huru ni kwamba mchakato wa kupiga simu (kwa upande wetu kazi kuu) lazima utenge muundo wa anwani ya tundu ya saizi sahihi kwenye kumbukumbu, na pointer kwa muundo huu pamoja na saizi yake hupitishwa kama hoja kwa dg_echo kitendakazi . Kitendaji cha dg_echo kamwe hakichimbui muundo huu: hupitisha kielekezi kwake kwa vitendaji vya recvfrom na sendto. Chaguo za kukokotoa za recvfrom hujaza muundo huu na anwani ya IP ya mteja na nambari ya bandari, na kwa kuwa kielekezi sawa (pcliaddr) hupitishwa kwa kazi ya kutuma kama anwani lengwa, kwa hivyo datagramu huonyeshwa tena kwa mteja aliyetuma datagramu.

8.5. UDP echo mteja: kazi kuu

Kazi kuu ya mteja wa UDP imeonyeshwa katika Orodha ya 8.3.

Kuorodhesha 8.3. UDP echo mteja

//udpcliserv/udpcli01.c

1 #pamoja na "unp.h"

3 kuu (int argc, char **argv)

6 struct sockaddr_in servaddr;

7 ikiwa (argc!= 2)

8 err_quit("matumizi: udpcli");

9 bzero(&servaddr, sizeof(servaddr));

10 servaddr.sin_family = AF_INET;

11 servaddr.sin_port = htons(SERV_PORT);

12 Inet_pton(AF_INET, argv, &servaddr.sin_addr);

13 sockfd = Soketi(AF_INET, SOCK_DGRAM, 0);

14 dg_cli(stdin, sockfd, (SA*)&servaddr, sizeof(servaddr));

Kujaza muundo wa anwani ya tundu na anwani ya seva

9-12 Muundo wa anwani ya tundu ya IPv4 umejaa anwani ya IP na nambari ya bandari ya seva. Muundo huu utapitishwa kwa kazi ya dg_cli. Huamua mahali pa kutuma datagrams.

13-14 Soketi ya UDP imeundwa na kazi ya dg_cli inaitwa.

8.6. UDP echo mteja: dg_cli kazi

Orodha ya 8.4 inaonyesha kazi ya dg_cli, ambayo hufanya kazi nyingi kwa upande wa mteja.

Kuorodhesha 8.4. Kazi dg_cli: kitanzi cha mteja

1 #pamoja na "unp.h"

7 huku (Fgets(sendline, MAXLINE, fp) != NULL) (

8 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);

9 n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);

10 recvline[n] = 0; /* kubatilisha */

Fputs 11(recvline, stdout);

7-12 Kuna hatua nne katika kitanzi cha usindikaji cha upande wa mteja: kusoma kamba kutoka kwa pembejeo ya kawaida kwa kutumia fgets, kutuma kamba kwa seva kwa kutumia sendto, kusoma jibu lililoonyeshwa la seva kwa kutumia recvfrom, na kuweka kamba iliyoonyeshwa kwa pato la kawaida kwa kutumia. kazi za fputs.

Mteja wetu hakuuliza kernel kukabidhi mlango uliowekwa kwa nguvu kwenye soketi yake (lakini mteja wa TCP alifanya wakati wa kupiga simu unganisha). Kwa soketi ya UDP, mara ya kwanza sendto inaitwa, kernel huchagua mlango uliowekwa kwa nguvu ikiwa hakuna bandari ya ndani ambayo tayari imehusishwa na tundu hilo. Kama ilivyo kwa TCP, mteja anaweza kupiga simu bind kwa uwazi, lakini hii haifanyiki mara chache.

Kumbuka kuwa unapoita chaguo za kukokotoa recvfrom, viashiria visivyofaa vinabainishwa kama hoja ya tano na sita. Hii inaambia kokwa kuwa hatuna nia ya kujua ni nani aliyetuma jibu. Kuna hatari kwamba mchakato wowote, iwe kwenye nodi moja au kwa nyingine yoyote, unaweza kutuma datagram kwa anwani ya IP ya mteja na bandari, ambayo itasomwa na mteja, ikizingatiwa kuwa ni jibu kutoka kwa seva. Tutazingatia hali hii katika sehemu ya 8.8.

Kama ilivyo kwa chaguo la kukokotoa la seva ya dg_echo, kitendakazi cha mteja wa dg_cli hakijitegemei itifaki, lakini kazi kuu ya mteja inategemea itifaki. Kazi kuu hutenga na kuanzisha muundo wa anwani ya tundu ya aina fulani ya itifaki, na kisha hupitisha kazi ya dg_cli pointer kwa muundo pamoja na ukubwa wake.

8.7. Datagrams zilizopotea

Mteja wa UDP na seva katika mfano wetu sio wa kutegemewa. Ikiwa datagramu ya mteja imepotea (tuseme imepuuzwa na kipanga njia fulani kati ya mteja na seva), mteja atazuiwa milele katika simu yake kwa kitendakazi cha recvfrom ndani ya kitendakazi cha dg_cli, akisubiri jibu kutoka kwa seva ambayo kamwe kuja. Vile vile, ikiwa datagramu ya mteja itafika kwenye seva lakini majibu ya seva yamepotea, mteja atazuiwa kabisa katika simu yake ya kitendakazi cha recvfrom. Njia pekee ya kuzuia hali hii ni kuweka muda wa kuisha katika simu ya kitendakazi ya recvfrom ya mteja. Tutaangalia hili katika sehemu ya 14.2.

Kuweka tu muda wa kuisha katika simu ya chaguo la kukokotoa recvfrom bado bado suluhisho kamili. Kwa mfano, ikiwa muda maalum kusubiri kumekwisha na hakuna jibu lililopokelewa, hatuwezi kusema ni nini kibaya - ama datagramu yetu haikufikia seva, au jibu la seva halijarudi. Ikiwa ombi la mteja lilikuwa na ombi kama vile "kuhamisha kiasi fulani cha pesa kutoka kwa akaunti A hadi akaunti B" (kinyume na kesi ya seva yetu rahisi ya mwangwi), basi kungekuwa na tofauti kubwa kati ya kupoteza ombi na kupoteza jibu. . Tutazungumza zaidi kuhusu kuongeza uaminifu kwa muundo wa seva ya mteja wa UDP katika Sehemu ya 22.5.

8.8. Kuangalia jibu lililopokelewa

Mwishoni mwa sehemu ya 8.6, tulitaja kuwa mchakato wowote unaojua nambari ya mlango iliyokabidhiwa ya mteja inaweza kutuma datagramu kwa mteja wetu, na zitachanganywa na majibu ya kawaida ya seva. Tunachoweza kufanya ni kurekebisha simu ya kukokotoa kutoka kwa recvfrom katika Orodha 8.4 ili kurudisha anwani ya IP na mlango wa mtumaji wa jibu, na kupuuza datagramu zozote zinazotoka kwa seva zaidi ya ile tunayotuma datagramu. Walakini, kuna mitego kadhaa hapa, kama tutakavyoona.

Kwanza, tunarekebisha kazi kuu ya mteja (angalia Orodha 8.3) ili kufanya kazi na seva ya kawaida ya echo (tazama Jedwali 2.1). Tunabadilisha mgawo tu

servaddr.sin_port = htons(SERV_PORT);

kazi

servaddr.sin_port = htons(7);

Sasa tunaweza kutumia nodi yoyote inayoendesha seva ya mwangwi ya kawaida na mteja wetu.

Kisha tunaandika upya dg_cli chaguo za kukokotoa ili kutenga muundo tofauti wa anwani ya tundu kwenye kumbukumbu ili kushikilia muundo uliorejeshwa na recvfrom . Tunaionyesha katika Orodha 8.5.

Orodha 8.5. Toleo la chaguo la kukokotoa la dg_cli ambalo hukagua anwani ya tundu iliyorejeshwa

//udpcliserv/dgcliaddr.c

1 #pamoja na "unp.h"

3 dg_cli(FILE *fp, int sockfd, const SA *pseraddr, socklen_t servlen)

6 char sendline, recvline;

7 socklen_t len;

8 struct sockaddr *preply_addr;

9 preply_addr = Malloc(servlen);

10 huku (Fgets(sendline, MAXLINE, fp) != NULL) (

11 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);

12 len = servlen;

13 n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);

14 ikiwa (len != servlen || memcmp(pservaddr, preply_addr, len) != 0) (

15 printf("jibu kutoka kwa %s (limepuuzwa)\n",

18 recvline[n] = 0; /* kubatilisha */

Fputs 19(recvline, stdout);

Kuweka muundo tofauti wa anwani ya tundu kwenye kumbukumbu

9 Tunatenga muundo mwingine wa anwani ya tundu kwenye kumbukumbu kwa kutumia kazi ya malloc. Kumbuka kuwa chaguo la kukokotoa la dg_cli bado ni itifaki inayojitegemea. Kwa kuwa hatujali ni aina gani ya muundo wa anwani ya tundu tunaoshughulikia, tunatumia ukubwa wake pekee katika simu ya chaguo la kukokotoa malloc.

Ulinganisho wa anwani zilizorejeshwa

12-13 Katika simu ya kazi ya recvfrom, tunaiambia kernel kurudisha anwani ya chanzo ya datagram. Kwanza tunalinganisha urefu uliorejeshwa na chaguo za kukokotoa recvfrom kama hoja ya matokeo ya thamani, na kisha kulinganisha miundo ya anwani ya tundu yenyewe kwa kutumia chaguo za kukokotoa za memcmp.

Toleo jipya la mteja wetu hufanya kazi vizuri ikiwa seva iko kwenye seva pangishi iliyo na anwani moja ya IP. Lakini programu hii haiwezi kufanya kazi ikiwa seva ina miingiliano kadhaa ya mtandao (seva ya multihomed). Tunaendesha programu hii kwa kupata nodi ya freebsd4, ambayo ina miingiliano miwili na anwani mbili za IP:

macosx% mwenyeji freebsd4

freebsd4.unpbook.com ina anwani 172.24.37.94

freebsd4.unpbook.com ina anwani 135.197.17.100

macosx% udpcli02 135.197.17.100

jibu kutoka 172.24.37.94:7 (kupuuzwa)

Kulingana na Mtini. 1.7 unaweza kuona kuwa tumeweka anwani ya IP kutoka kwa subnet tofauti. Hii kawaida inakubalika. Utekelezaji mwingi wa IP unakubali datagramu ya IP inayoingia inayolengwa kwa anwani zozote za IP za mwenyeji, bila kujali kiolesura ambacho inafika. Nakala ya RFC 1122 inaita hii mfano dhaifu wa mfumo wa mwisho. Ikiwa mfumo ungetekeleza kile ambacho hati hii inakiita kielelezo dhabiti cha mfumo wa mwisho, inakubali datagramu inayoingia ikiwa tu datagramu itafika kwenye kiolesura ambacho kinakusudiwa.

Anwani ya IP iliyorejeshwa na chaguo za kukokotoa recvfrom (anwani ya IP ya chanzo cha datagramu ya UDP) si anwani ya IP ambayo tulituma datagramu. Seva inapotuma majibu yake, anwani ya IP ya mpokeaji ni 172.24.37.94. Kitendaji cha kuelekeza kernel kwenye nodi freebsd4 huchagua 172.24.37.94 kama kiolesura kinachotoka. Kwa kuwa seva haikuhusisha anwani ya IP na tundu lake (seva inayohusishwa na tundu lake anwani ya ulimwengu, ambayo tunaweza kuthibitisha kwa kuendesha programu ya netstat kwenye nodi freebsd4), kernel huchagua anwani ya chanzo ya datagram ya IP. Anwani hii inakuwa anwani msingi ya IP ya kiolesura kinachotoka. Ikiwa tutatuma datagramu kwa kitu kingine isipokuwa anwani ya msingi ya IP ya kiolesura (yaani, kwa jina lingine, lakabu), basi jaribio letu lililoonyeshwa katika Orodha ya 8.5 pia litashindwa.

Suluhisho mojawapo litakuwa kwa mteja kuangalia jina la kikoa la mwenyeji anayejibu badala ya anwani yake ya IP. Ili kufanya hivyo, jina la seva linatafutwa kwenye DNS (angalia Sura ya 11) kulingana na anwani ya IP iliyorejeshwa na chaguo la kukokotoa recvfrom. Suluhisho lingine ni kufanya seva ya UDP kuunda tundu moja kwa kila anwani ya IP iliyosanidiwa kwenye mwenyeji, funga anwani hiyo ya IP kwenye tundu, piga simu kwenye kila soketi hizo zote (kusubiri baadhi yao itakuwa tayari kusoma) na kisha akajibu kutoka kwenye soketi tayari kwa kusoma. Kwa kuwa soketi iliyotumiwa kwa jibu inahusishwa na anwani ya IP ambayo ilikuwa anwani ya mwisho ya ombi la mteja (vinginevyo datagramu haingewasilishwa kwenye tundu), tunaweza kuwa na uhakika kwamba mtumaji wa jibu na mpokeaji anwani ya ombi ni sawa. Tunaonyesha mifano hii katika Sehemu ya 22.6.

KUMBUKA

Kwenye mfumo wa Solaris na nyingi violesura vya mtandao Anwani ya IP ya mtumaji ya jibu la seva ni anwani ya IP ya mpokeaji wa ombi la mteja. Hali iliyoelezewa katika sehemu hii, inarejelea utekelezaji unaotokana na Berkeley ambao huchagua anwani ya IP ya chanzo kulingana na kiolesura kinachotoka.

8.9. Kuanzisha mteja bila kuanzisha seva

Tutaangalia hali inayofuata ni kuanzisha mteja bila kuanzisha seva. Ikiwa tutafanya hivi na kuingiza mstari mmoja upande wa mteja, hakuna kitakachotokea. Mteja amezuiwa milele katika simu yake ya kukokotoa recvfrom, akisubiri jibu la seva ambalo haliji. Lakini katika katika mfano huu haijalishi kwa vile sasa tunatazamia kupata ufahamu wa kina wa itifaki na kujua kinachoendelea na programu yetu ya mtandao.

Kwanza tunaendesha tcpdump kwenye mwenyeji wa macosx, na kisha kukimbia mteja kwenye mwenyeji sawa, kuweka seva mwenyeji kwa freebsd4. Kisha tunaingia mstari mmoja, lakini mstari huu hauonyeshwa na seva.

macosx% udpcli01 172.24.37.94

Salamu, Dunia tunaingia kwenye mstari huu,

lakini hatupati chochote katika kujibu

Orodha ya 8.6 inaonyesha matokeo ya tcpdump.

Orodha 8.6. tcpdump pato wakati mchakato wa seva haufanyi kazi kwenye nodi ya seva

01 0.0 arp who- has freebsd4 tell macosx

02 0.003576 (0.0036) arp jibu freebsd4 ni-saa 0:40:5:42:d6:de

03 0.003601 (0.0000) macosx.51139 > freebsd4.9877: udp 13

04 0.009781 (0.0062) freebsd4 >

Jambo la kwanza tunaloona ni kwamba ombi na jibu la ARP hupokelewa kabla ya seva pangishi kutuma datagramu ya UDP kwa seva pangishi. (Tuliacha ubadilishanaji huu katika matokeo ya programu ili kusisitiza tena kwamba ombi la ARP hutumwa kila wakati na jibu hupokelewa kabla ya datagram ya IP kutumwa.)

Kwenye mstari wa 3 tunaona kwamba datagram ya mteja imetumwa, lakini seva pangishi hujibu kwenye mstari wa 4 na ujumbe wa ICMP usioweza kufikiwa. (Urefu wa 13 unajumuisha herufi 12 pamoja na ishara mstari mpya.) Hata hivyo, hitilafu hii ya ICMP haijarejeshwa kwa mchakato wa mteja kwa sababu ambazo tutaziorodhesha kwa ufupi hapa chini. Badala yake, mteja amezuiwa kabisa katika simu ya chaguo la kukokotoa recvfrom katika Orodha 8.4. Pia tunakumbuka kuwa ICMPv6 ina hitilafu ya "Port Unreachable" sawa na ICMPv4 (ona Jedwali A.5 na A.6), kwa hivyo matokeo yaliyowasilishwa hapa yanafanana na yale ya IPv6.

Hitilafu hii ya ICMP ni hitilafu isiyolingana. Hitilafu ilisababishwa na chaguo za kukokotoa za sendto, lakini kitendakazi cha sendto kilikamilishwa kama kawaida. Kumbuka kutoka kwa Sehemu ya 2.9 kwamba urejeshaji wa kawaida kutoka kwa operesheni ya matokeo ya UDP inamaanisha tu kwamba datagramu imeongezwa kwenye foleni ya pato la safu ya kiungo. Hitilafu ya ICMP hairudishwi hadi muda fulani upite (4 ms kwa Orodha ya 8.6), ndiyo maana inaitwa asynchronous.

Kanuni ya msingi ni kwamba makosa ya asynchronous hayarudishwi kwa soketi ya UDP isipokuwa tundu limeambatishwa. Tunaonyesha jinsi ya kupiga kazi ya kuunganisha kwenye tundu la UDP katika Sehemu ya 8.11. Sio kila mtu anaelewa kwa nini uamuzi huu ulifanywa wakati soketi zilitekelezwa kwanza. (Mazingatio ya utekelezaji yanajadiliwa kwenye ukurasa wa 748-749.) Fikiria mteja wa UDP kutuma datagrams tatu kwa seva tatu tofauti (yaani, anwani tatu tofauti za IP) juu ya soketi moja ya UDP. Mteja huingiza kitanzi ambacho huita kitendakazi cha recvfrom ili kusoma majibu. Datagramu mbili zinawasilishwa kwa usahihi (hiyo ni, seva ilikuwa ikifanya kazi kwenye nodi mbili kati ya tatu), lakini nodi ya tatu haikuwa ikiendesha seva, na nodi ya tatu inajibu kwa ujumbe wa bandari ya ICMP usioweza kufikiwa. Ujumbe huu wa hitilafu wa ICMP una kichwa cha IP na kichwa cha UDP cha datagram kilichosababisha hitilafu. (Ujumbe wa hitilafu wa ICMPv4 na ICMPv6 daima huwa na kichwa cha IP na yote au sehemu ya kichwa cha UDP ili kuruhusu mpokeaji ujumbe kubaini ni tundu gani lilisababisha hitilafu. Hii inaonyeshwa katika Mchoro 28.5 na 28.6.) Mteja aliyetuma datagramu tatu anapaswa kujua. mpokeaji wa datagramu iliyosababisha hitilafu ili kubainisha ni ipi hasa kati ya datagramu hizo tatu iliyosababisha hitilafu. Lakini kernel inawezaje kuwasilisha habari hii kwa mchakato? Kitu pekee ambacho recvfrom kinaweza kurudi ni thamani ya utofauti wa errno. Lakini chaguo za kukokotoa za recvfrom haziwezi kurudisha anwani ya IP na nambari ya bandari ya mpokeaji wa datagramu ya UDP kimakosa. Kwa hivyo, imeamuliwa kuwa makosa haya ya asynchronous yanarejeshwa kwa mchakato ikiwa tu mchakato umeambatisha tundu la UDP kwa rika moja tu maalum.

KUMBUKA

Linux hurejesha hitilafu nyingi za mlango wa ICMP zisizoweza kufikiwa, hata kwa tundu ambalo halijaambatishwa, isipokuwa chaguo la tundu la SO_DSBCOMPAT limewashwa. Hitilafu zote zisizoweza kufikiwa za wapokeaji zilizoonyeshwa kwenye Jedwali la 1 zinarejeshwa. A.5, isipokuwa makosa ya misimbo 0, 1, 4, 5, 11 na 12.

Tutarudi kwenye suala la hitilafu zisizo za kawaida na soketi za UDP katika Sehemu ya 28.7 na kuonyesha njia rahisi ya kupata hitilafu hizi kwenye soketi ambayo haijaambatishwa kwa kutumia daemoni yetu wenyewe.

8.10. Mfano wa mwisho wa seva ya mteja wa UDP

Katika Mtini. Katika Mchoro 8.5, vitone vikubwa vyeusi vinaonyesha thamani nne zinazopaswa kuwekwa au kuchaguliwa mteja anapotuma datagramu ya UDP.

Mchele. 8.5. Kufupisha mfano wa seva ya mteja wa UDP kutoka kwa mtazamo wa mteja

Mteja lazima abainishe anwani ya IP ya seva na nambari ya mlango ili kuita kitendakazi cha sendto. Kwa kawaida anwani ya IP ya mteja na nambari ya bandari huchaguliwa kiotomatiki na kernel, ingawa tulibaini kuwa mteja anaweza kuita kipengele cha kumfunga. Pia tulibaini kuwa ikiwa maadili haya mawili yamechaguliwa kwa mteja na kernel, basi bandari ya mteja iliyokabidhiwa kwa nguvu huchaguliwa mara moja, mara ya kwanza sendto inaitwa, na haibadilishwi tena. Hata hivyo, anwani ya IP ya mteja inaweza kubadilika kwa kila datagramu ya UDP mteja anayotuma, ikizingatiwa kuwa mteja hafungi anwani maalum ya IP kwenye tundu kwa kutumia kipengele cha kukokotoa. Sababu imefafanuliwa katika Mtini. 8.5: Ikiwa node ya mteja ina interfaces kadhaa za mtandao, mteja anaweza kubadili kati yao (katika Mchoro 8.5, anwani moja inahusu safu ya kiungo iliyoonyeshwa upande wa kushoto, nyingine inahusu moja iliyoonyeshwa upande wa kulia). Katika hali mbaya zaidi ya hali hii, anwani ya IP ya mteja, iliyochaguliwa na kernel kulingana na safu ya kiungo inayotoka, ingebadilika kwa kila datagramu.

Ni nini hufanyika ikiwa mteja atafunga anwani ya IP kwenye tundu lake, lakini kernel itaamua kwamba datagram inayotoka inapaswa kutumwa kutoka kwa safu nyingine ya kiungo? Katika hali hii, datagram ya IP itakuwa na anwani ya IP ya chanzo ambayo ni tofauti na anwani ya IP ya safu ya kiungo inayotoka (ona Zoezi la 8.6).

Katika Mtini. Mchoro 8.6 unaonyesha maadili manne sawa, lakini kutoka kwa mtazamo wa seva.

Mchele. 8.6. Kufupisha mfano wa seva ya mteja wa UDP kutoka kwa mtazamo wa seva

Seva inaweza kujifunza angalau vigezo vinne kwa kila datagramu inayopokea: anwani ya IP ya chanzo, anwani ya IP lengwa, nambari ya kituo cha chanzo, na nambari ya bandari lengwa. Simu zinazorudisha taarifa hii kwa seva za TCP na UDP zinaonyeshwa kwenye Jedwali. 8.1.

Jedwali 8.1. Taarifa zinazopatikana kwa seva kutoka kwa datagram ya IP inayoingia

Seva ya TCP daima ina ufikiaji rahisi wa vipande vyote vinne vya habari kwa soketi iliyounganishwa, na maadili haya manne hubaki mara kwa mara kwa maisha ya muunganisho. Hata hivyo, katika kesi ya muunganisho wa UDP, anwani ya IP lengwa inaweza kupatikana tu kwa kuweka chaguo la tundu IP_RECVDSTADDR kwa IPv4 au IPV6_PKTINFO kwa IPv6 na kisha kupiga simu ya kitendakazi cha recvmsg badala ya chaguo la kukokotoa recvfrom. Kwa sababu UDP haina muunganisho, anwani ya IP lengwa inaweza kubadilika kwa kila datagramu inayotumwa kwa seva. Seva ya UDP pia inaweza kupokea datagramu zinazotumwa kwa mojawapo ya anwani za utangazaji za seva pangishi au anwani ya upeperushaji anuwai, ambayo tunajadili katika Sura ya 20 na 21. Tutaonyesha jinsi ya kubainisha anwani lengwa ya datagramu ya UDP katika Sehemu ya 20.2 baada ya kufafanua kipengele cha recvmsg.

8.11. unganisha kazi ya UDP
KUMBUKA
KUMBUKA
KUMBUKA

Jedwali 8.2

KUMBUKA

Mchele. 8.7. Soketi iliyoambatanishwa ya UDP

Mchele. 8.8

Kupiga simu kuunganishwa mara nyingi kwenye soketi ya UDP

Mchakato ulio na tundu la UDP lililounganishwa linaweza kuita tena kitendakazi cha kuunganisha kwenye tundu hilo kwa:

c - kuweka anwani mpya ya IP na bandari;

c - kata tundu.

Kesi ya kwanza, inayobainisha rika mpya kwa tundu la UDP lililounganishwa, hutofautiana na kutumia kazi ya kuunganisha na tundu la TCP: kwa tundu la TCP, kazi ya kuunganisha inaweza kuitwa mara moja tu.

Ili kutenganisha tundu la UDP, tunaita kipengele cha kukokotoa cha kuunganisha, lakini weka kipengele cha familia cha muundo wa anwani ya tundu (sin_family kwa IPv4 au sin6_family kwa IPv6) hadi AF_UNSPEC . Hii inaweza kusababisha hitilafu ya EAFNOSUPPORT, lakini hii ni kawaida. Ni mchakato wa kuita kitendakazi cha kuunganisha kwenye tundu la UDP ambalo tayari limeunganishwa ambayo inaruhusu tundu kukatwa.

KUMBUKA

Mwongozo wa BSD wa chaguo la kukokotoa la kuunganisha kwa kawaida ulisema: "Soketi za data zinaweza kuvunja miunganisho kwa kuunganisha kwa anwani zisizo sahihi, kama vile anwani tupu." Kwa bahati mbaya, hakuna mwongozo unaosema ni nini "anwani tupu", wala haisemi kuwa kosa hurejeshwa kama matokeo (ambayo ni ya kawaida). Kiwango cha POSIX kinasema kwa uwazi kwamba familia ya anwani lazima iwekwe kuwa AF_UNSPEC, lakini kisha inasema kwamba simu hii ya kuunganisha inaweza kurudisha au isirudishe hitilafu ya EAFNOSUPPORT.

Utendaji

Wakati programu inaita kitendakazi cha sendto kwenye soketi isiyoambatishwa ya UDP, utekelezaji wa kernel inayotokana na Berkeley huunganishwa kwa muda kwenye tundu, kutuma datagram, na kisha kutenganisha kutoka kwenye soketi. Kwa hivyo, kupiga simu kitendakazi cha sendto kutuma datagrams mbili kwa mfuatano kwenye tundu ambalo halijaunganishwa inajumuisha hatua sita zifuatazo zinazofanywa na kernel:

c - kuunganisha tundu;

c - pato la datagram ya kwanza;

c - kukata tundu;

c - kuunganisha tundu;

c - pato la datagram ya pili;

c - kukata tundu.

KUMBUKA

Jambo lingine la kuzingatia ni idadi ya utaftaji kwenye jedwali la uelekezaji. Muunganisho wa kwanza wa muda hutafuta anwani ya IP lengwa katika jedwali la kuelekeza na kuhifadhi (kache) habari hii. Muunganisho wa pili wa muda unabainisha kuwa anwani ya mpokeaji inalingana na anwani iliyoakibishwa kutoka kwa jedwali la kuelekeza (tunachukulia kuwa vitendaji vyote viwili vya sendto vimepewa mpokeaji sawa) na haihitaji kuangalia tena jedwali la uelekezaji.

Programu inapojua kuwa itatuma datagramu nyingi kwa programu rika moja, ni bora zaidi kuambatisha kwa uwazi tundu. Simu ya kuunganisha ikifuatiwa na simu mbili za kuandika sasa itahusisha hatua zifuatazo zinazofanywa na kernel:

c - kuunganisha tundu;

c - pato la datagram ya kwanza;

c - matokeo ya datagram ya pili.

Katika kesi hii, kernel inakili muundo wa anwani ya tundu iliyo na anwani ya IP ya marudio na bandari mara moja tu, na wakati kutuma inaitwa mara mbili, kunakili hufanywa mara mbili. B anabainisha kuwa kuunganisha kwa muda soketi ya UDP iliyokatwa huchangia takriban theluthi moja ya gharama ya kila uhamisho wa UDP.

8.12. dg_cli kazi (inaendelea)

Hebu turejee kwenye kitendakazi cha dg_cli kilichoonyeshwa kwenye Orodha 8.4 na tuiandike upya ili kuita kitendakazi cha kuunganisha. Orodha ya 8.7 inaonyesha kipengele kipya.

Orodha 8.7. Chaguo la kukokotoa la dg_cli kuita kitendakazi cha kuunganisha

//udpcliserv/dgcliconnect.c

1 #pamoja na "unp.h"

3 dg_cli(FILE *fp, int sockfd, const SA *pseraddr, socklen_t servlen)

6 char sendline, recvline;

7 Unganisha(sockfd, (SA*)pseraddr, servlen);

8 huku (Fgets(sendline, MAXLINE, fp) != NULL) (

9 Andika(sockfd, sendline, strlen(sendline));

10 n = Soma(sockfd, recvline, MAXLINE);

11 recvline[n] = 0; /* kubatilisha */

Fputs 12(recvline, stdout);

Mabadiliko kutoka kwa toleo la awali ni nyongeza ya simu ya kukokotoa na uingizwaji wa simu za kukokotoa za sendto na recvfrom na simu za kukokotoa za kuandika na kusoma. Chaguo za kukokotoa za dg_cli husalia kuwa itifaki huru kwa sababu haiangalii muundo wa anwani ya tundu iliyopitishwa kwa chaguo la kukokotoa la kuunganisha. Kazi kuu ya mteja wetu, iliyoonyeshwa katika Orodha ya 8.3, inasalia kuwa sawa.

Ikiwa tutaendesha programu kwenye mwenyeji wa macosx, tukibainisha anwani ya IP ya mwenyeji wa freebsd4 (ambayo haiendeshi seva yetu kwenye bandari 9877), tunapata matokeo yafuatayo:

macosx% udpcli04 172.24.37.94

Salamu, Dunia

kosa la kusoma: Muunganisho umekataliwa

Jambo la kwanza tunalogundua ni kwamba hatupati hitilafu tunapoanzisha mchakato wa mteja. Hitilafu hutokea tu baada ya kutuma datagram ya kwanza kwa seva. Ni utumaji wa datagramu hii ambayo husababisha hitilafu ya ICMP kutoka kwa seva pangishi. Lakini mteja wa TCP anapopiga simu connect , ikibainisha nodi ya seva ambayo haiendeshi mchakato wa seva, unganisha hurejesha hitilafu kwa sababu simu ya kuunganisha husababisha pakiti ya kwanza ya kupeana mkono kwa njia tatu ya TCP kutumwa, na ni pakiti hii ambayo husababisha sehemu ya RST ipokewe kutoka kwa rika (tazama sehemu ya 4.3).

Orodha ya 8.8 inaonyesha matokeo ya tcpdump.

Orodha 8.8. Pato kutoka kwa tcpdump wakati wa kuendesha kazi ya dg_cli

macosx% tcpdump

01 0.0 macosx.51139 > freebsd4 9877:udp 13

02 0.006180 (0.0062) freebsd4 > macosx: icmp: freebsd4 udp port 9877 isiyoweza kufikiwa

Katika meza A.5 pia tunaona kwamba kernel inahusisha hitilafu ya ICMP na hitilafu ya ECONNREFUSED, ambayo inalingana na matokeo ya mfuatano wa ujumbe uliokataliwa wa Muunganisho na chaguo za kukokotoa err_sys.

KUMBUKA

Kwa bahati mbaya, sio punje zote zinarudi Ujumbe wa ICMP tundu la UDP, kama tulivyoonyesha katika sehemu hii. Kwa kawaida, kokwa zinazotokana na Berkeley hurejesha kosa hili, lakini kokwa za System V hazirudishi. Kwa mfano, ikiwa tutatumia mteja sawa kwenye seva pangishi ya Solaris 2.4 na kutumia chaguo za kukokotoa kuunganisha kwenye seva pangishi ambayo haiendeshi seva yetu, kisha kwa kutumia tcpdump tunaweza kuthibitisha kwamba hitilafu isiyoweza kufikiwa ya mlango wa ICMP inarudishwa na seva pangishi, lakini inayosababishwa na kitendakazi cha kusoma kwa mteja hamalizi kamwe. Hali hii imerekebishwa katika Solaris 2.5. UnixWare hairejeshi hitilafu, wakati AIX, Digital Unix, HP-UX na Linux zinafanya.

8.13. Ukosefu wa udhibiti wa mtiririko katika UDP

Orodha 8.9

//udpcliserv/dgcliloop1.c

1 #pamoja na "unp.h"

8char sendline;

Orodha 8.10

//udpcliserv/dgecholoop1.c

1 #pamoja na "unp.h"

3 hesabu ya int tuli;

7 socklen_t len;

8 char mesg;

11 len = clilen;

17 recvfrom_int(int signo)

Orodha 8.11. Pato kwenye nodi ya seva

freebsd % netstat -s -p udp

Datagramu 71208 zimepokelewa

0 na kichwa ambacho hakijakamilika

0 na uga mbaya wa urefu wa data

0 na hundi mbaya

0 bila hundi

832 ilishuka kwa sababu ya kutokuwa na tundu

0 sio kwa pcb ya haraka

Pato la data ya 137685

freebsd % udpser06 zindua seva yetu

mteja hutuma datagrams

^C

freebsd % netstat -s -p udp

Datagramu 73208 zimepokelewa

0 na kichwa ambacho hakijakamilika

0 na uga mbaya wa urefu wa data

0 na hundi mbaya

0 bila hundi

832 ilishuka kwa sababu ya kutokuwa na tundu

Datagramu 16 za utangazaji/upeperushaji nyingi zimeshuka kwa sababu ya kutokuwa na tundu

0 sio kwa pcb ya haraka

Pato la data ya 137685

aix % udpser06

^?

kupokea datagram 2000

Soketi ya UDP ya kupokea bafa

Idadi ya datagramu za UDP zilizowekwa kwenye foleni kwa soketi fulani hupunguzwa na saizi ya bafa yake ya kupokea. Tunaweza kubadilisha hii kwa kutumia chaguo la tundu la SO_RCVBUF, kama tulivyoonyesha katika sehemu ya 7.5. Kwenye FreeBSD, soketi chaguo-msingi ya UDP ya kupokea bafa ni baiti 42,080, ambayo inaruhusu datagramu 30 pekee kati ya baiti 1400 kuhifadhiwa. Ikiwa tutaongeza saizi ya bafa ya soketi, tunaweza kutarajia seva kupokea datagramu za ziada. Kuorodhesha 8.12 ni chaguo za kukokotoa za dg_echo zilizorekebishwa kutoka kwa Orodha ya 8.10 ambayo huongeza ukubwa wa bafa ya soketi hadi 240 KB. Ikiwa tutaendesha seva hii kwenye mfumo wa Jua na mteja kwenye mfumo wa RS/6000, hesabu ya datagramu iliyopokewa itakuwa 103. Kwa kuwa hii ni bora kidogo kuliko mfano wa awali wenye saizi chaguomsingi ya bafa, ni wazi kuwa bado tuko. hakupata suluhu la tatizo.

Orodha 8.12. dg_echo kitendakazi, ambacho huongeza saizi ya bafa ya tundu

//udpcliserv/dgecholooor2.c

1 #pamoja na "unp.h"

2 utupu tuli recvfrom_int(int);

3 hesabu ya int tuli;

5 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)

8 socklen_t len;

9 char mesg;

Mawimbi 10(SIGINT, recvfrom_int);

11 n = 240 * 1024;

12 Setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));

14 len = clilen;

15 Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);

20 recvfrom_int(int signo)

22 printf("\nimepokea %d datagrams\n", count);

KUMBUKA

Kwa nini tunaweka saizi ya bafa ya soketi kuwa baiti 240G-1024 katika Orodha ya 8.12? Soketi chaguo-msingi ya upeo wa ukubwa wa bafa katika BSD/OS 2.1 ni baiti 262,144 (256G-1024), lakini kutokana na jinsi bufa inavyogawiwa kwenye kumbukumbu (ilivyoelezwa katika Sura ya 2), kwa kweli ina mipaka ya baiti 246,723. Mifumo mingi ya awali yenye msingi wa 4.3BSD ilipunguza tundu la kupokea bafa kwa takriban baiti 52,000.

8.14. Kufafanua kiolesura kinachotoka kwa UDP

Unaweza pia kutumia soketi ya UDP iliyoambatishwa kubainisha kiolesura kinachotoka ambacho kitatumika kutuma datagramu kwa mpokeaji mahususi. Hii ni kwa sababu ya athari ya upande wa kazi ya unganisho inayotumika kwa tundu la UDP: kernel huchagua anwani ya IP ya ndani (ikizingatiwa kuwa mchakato bado haujaita bind ili kuiweka wazi). Anwani ya eneo huchaguliwa kwa kuangalia anwani lengwa kwenye jedwali la kuelekeza, kuchukua anwani ya msingi ya IP ya kiolesura ambacho, kulingana na jedwali, datagramu zitatumwa.

Orodha ya 8.13 inaonyesha programu rahisi ya UDP inayounganishwa kwa anwani fulani ya IP kwa kutumia chaguo la kukokotoa la kuunganisha na kisha kuita chaguo la kukokotoa la getsockname, ikitoa anwani ya IP ya ndani na mlango.

Kuorodhesha 8.13. Programu ya UDP inayotumia kipengele cha kuunganisha ili kuamua kiolesura kinachotoka

//udpcliserv/udpcli09.c

1 #pamoja na "unp.h"

3 kuu (int argc, char **argv)

6 socklen_t len;

7 struct sockaddr_in cliaddr, servaddr;

8 ikiwa (argc!= 2)

9 err_quit("matumizi: udpcli");

10 sockfd = Soketi(AF_INET, SOCK_DGRAM, 0);

11 bzero(&servaddr, sizeof(servaddr));

12 servaddr.sin_family = AF_INET;

13 servaddr.sin_port = htons(SERV_PORT);

14 Inet_pton(AF_INET, argv, &servaddr.sin_addr);

15 Unganisha(sockfd, (SA*)&servaddr, sizeof(servaddr));

16 len = sizeof(cliaddr);

17 Getsockname(sockfd, (SA*)&cliaddr, &len);

18 printf("anwani ya eneo %s\n", Sock_ntop((SA*)&cliaddr, len));

Ikiwa tutaendesha programu kwenye seva pangishi ya freebsd iliyo na miingiliano mingi ya mtandao, tutapata matokeo yafuatayo:

freebsd % udpcli09 206.168.112.96

anwani ya ndani 12.106.32.254:52329

freebsd % udpcli09 192.168.42.2

anwani ya ndani 192.168.42.1:52330

freebsd % udpcli09 127.0.0.1

anwani ya eneo 127.0.0.1:52331

Kulingana na Mtini. 1.7 unaweza kuona kwamba tunapoendesha programu mara mbili za kwanza, hoja mstari wa amri ni anwani ya IP kwenye mitandao tofauti ya Ethaneti. Kernel inapeana anwani ya IP ya ndani kwa anwani ya kiolesura cha msingi katika mwafaka Mitandao ya Ethernet. Wakati wa kupiga simu unganisha kwenye soketi ya UDP, hakuna kitu kinachotumwa kwa mwenyeji huyo - ni operesheni ya ndani kabisa ambayo huhifadhi anwani ya IP ya wenzao na bandari. Pia tunaona kuwa kupiga simu kuunganishwa kwenye soketi ya UDP ambayo haijaunganishwa pia inapeana mlango uliowekwa kwa nguvu kwenye tundu.

KUMBUKA

Kwa bahati mbaya, teknolojia hii haifanyi kazi katika utekelezaji wote, ambayo ni kweli hasa kwa kernels zinazotokana na SVR4. Kwa mfano, hii haifanyi kazi kwenye Solaris 2.5, lakini inafanya kazi kwenye AIX, Digital Unix, Linux, MacOS X na Solaris 2.6.

8.15. Seva ya mwangwi ya TCP na UDP kwa kutumia kitendakazi cha kuchagua

Sasa tutaunganisha seva yetu ya mwangwi ya TCP kutoka Sura ya 5 na mfululizo wetu Seva ya mwangwi ya UDP kutoka kwa sura hii hadi seva moja inayotumia chaguo la kukokotoa kuzidisha soketi za TCP na UDP. Orodha ya 8.14 inaonyesha sehemu ya kwanza ya seva hii.

Orodha 8.14. Sehemu ya kwanza ya usindikaji wa seva ya echo ya soketi za TCP na UDP kwa kutumia chaguo la kukokotoa

//udpcliserv/udpsservselect01.c

1 #pamoja na "unp.h"

3 kuu (int argc, char **argv)

5 int listenfd, connfd, udpfd, nready, maxfdp1;

6 char mesg;

7 pid_t childpid;

10 socklen_t len;

11 const int on = 1;

12 struct sockaddr_in cliaddr, servaddr;

13 batili sig_chld(int);

14 /* unda soketi ya kusikiliza ya TCP */

15 listenfd = Soketi(AF_INET, SOCK_STREAM, 0);

16 bzero(&servaddr, sizeof(servaddr));

17 servaddr.sin_family = AF_INET;

18 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

19 servaddr.sin_port = htons(SERV_PORT);

20 Setsockopt(sikilizafd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(imewashwa));

21 Bind(sikilizafd, (SA*)&servaddr, sizeof(servaddr));

22 Sikiliza(sikilizafd, SIKILIZA);

23 /* tengeneza tundu la UDP */

24 udpfd = Soketi(AF_INET, SOCK_DGRAM, 0);

25 bzero(&servaddr, sizeof(servaddr));

26 servaddr.sin_family = AF_INET;

27 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

28 servaddr.sin_port = htons(SERV_PORT);

29 Bind(udpfd, (SA*)&servaddr, sizeof(servaddr));

Kuunda Soketi ya Kusikiliza ya TCP

14-22 Soketi ya kusikiliza ya TCP imeundwa na kuhusishwa na mlango unaojulikana awali kwenye seva. Tunaweka chaguo la tundu SO_REUSEADDR ikiwa kuna viunganisho kwenye bandari hii.

Kuunda tundu la UDP

23-29 Soketi ya UDP pia imeundwa na kuhusishwa na bandari sawa. Hata kama lango lile lile linatumika kwa soketi za TCP na UDP, hakuna haja ya kuweka chaguo la tundu la SO_REUSEADDR kabla ya simu hii kufunga kwa sababu Bandari za TCP usitegemee bandari za UDP.

Orodha ya 8.15 inaonyesha sehemu ya pili ya seva yetu.

Orodha 8.15. Nusu ya pili ya usindikaji wa seva ya echo TCP na UDP kwa kutumia chaguo la kuchagua

udpcliserv/udpserselect01.c

30 Ishara(SIGCHLD, sig_chld); /* haja ya kupiga simu waitpid() */

31 FD_ZERO(&rset);

32 maxfdp1 = max(sikilizafd, udpfd) + 1;

34 FD_SET(sikilizafd, &rset);

35 FD_SET(udpfd, &rset);

36 ikiwa ((nready = select(maxfdp1, &rset, NULL, NULL, NULL))

37 ikiwa (errno == EINTR)

38 kuendelea; /* kurudi kwa () */

40 err_sys("chagua kosa");

42 ikiwa (FD_ISSET(sikilizafd, &rset)) (

43 len = sizeof(cliaddr);

44 connfd = Kubali(sikilizafd, (SA*)&cliaddr, &len);

45 ikiwa ((childpid = Uma()) == 0) ( /* mchakato wa mtoto */

46 Funga(sikilizafd); /* hufunga tundu la kusikiliza */

47 str_echo(connfd); /* Inachakata ombi */

50 Funga(connfd); /* mzazi hufunga tundu lililoambatishwa */

52 ikiwa (FD_ISSET(udpfd, &rset)) (

53 len = sizeof(cliaddr);

54 n = Recvfrom(udpfd, mesg, MAXLINE, 0, (SA*)&cliaddr, &len);

55 Sendto(udpfd, mesg, n, 0, (SA*)&cliaddr, len);

Inasakinisha kidhibiti cha mawimbi cha SIGCHLD

30 Kidhibiti kimesakinishwa kwa mawimbi ya SIGCHLD kwa sababu miunganisho ya TCP itashughulikiwa na mchakato wa mtoto. Tulionyesha kidhibiti hiki cha mawimbi katika Orodha ya 5.8.

Inajitayarisha Kupiga Kazi ya Chagua

31-32 Tunaanzisha seti ya maelezo kwa kazi ya kuchagua na kuhesabu upeo wa maelezo mawili, ambayo tutasubiri wakati tayari.

Kupigia simu kitendakazi cha kuchagua

34-41 Tunaita kipengele cha kuchagua, tukisubiri tundu la TCP au soketi ya UDP tunayosikiliza ili kuwa tayari kusoma. Kwa kuwa kidhibiti chetu cha mawimbi cha sig_chld kinaweza kughairi simu iliyochaguliwa ya kukokotoa, tunashughulikia hitilafu ya EINTR.

Inashughulikia muunganisho mpya wa mteja

42-51 Tunatumia kitendakazi cha kukubali kukubali muunganisho mpya wa mteja, na wakati tundu la kusikiliza la TCP liko tayari kusomwa, tunatumia kitendakazi cha uma ili kuibua mchakato wa mtoto na kuita kitendakazi chetu cha str_echo kwenye mchakato wa mtoto. Huu ni mlolongo sawa wa hatua tulizofuata katika Sura ya 5.

Inachakata datagramu inayoingia

52-57 Ikiwa tundu la UDP liko tayari kusomwa, datagram imefika. Tunaisoma kwa kutumia kipengele cha recvfrom na kuituma kwa mteja kwa kutumia kitendakazi cha sendto.

8.16. Muhtasari

Kubadilisha mteja wetu wa mwangwi na seva ya mwangwi kutumia UDP badala ya TCP ilikuwa rahisi. Lakini wakati huo huo, tulipoteza uwezo mwingi uliotolewa na itifaki ya TCP: kutambua pakiti zilizopotea na kutuma tena, kuangalia ikiwa pakiti zinatoka kwa interlocutor sahihi, nk. Tutarejea kwenye mada hii katika Sehemu ya 22.5 na kuona jinsi tunavyoweza kuboresha utegemezi wa ombi la UDP.

Soketi za UDP zinaweza kutoa makosa ya asynchronous, ambayo ni makosa ambayo yanaripotiwa muda baada ya pakiti kutumwa. Soketi za TCP kila wakati zinaripoti kwa programu, lakini kwa UDP soketi lazima iunganishwe ili kupokea hitilafu hizi.

UDP haina udhibiti wa mtiririko, ambayo ni rahisi sana kuonyesha. Hili kwa kawaida si tatizo kwa sababu programu nyingi za UDP zimeundwa kwa kutumia kielelezo cha jibu la ombi na hazijaundwa kubeba kiasi kikubwa cha data.

Kuna mambo kadhaa ya kuzingatia unapoandika maombi ya UDP, lakini tutayashughulikia katika Sura ya 22 baada ya kuangazia utendakazi wa violesura, utangazaji na utangazaji anuwai.

Mazoezi

1. Tuseme tuna maombi mawili, moja inatumia TCP na nyingine inatumia UDP. Bafa ya kupokea kwa soketi ya TCP ina baiti 4096 za data, na bafa ya kupokea kwa soketi ya UDP ina datagramu mbili za baiti 2048. Programu ya TCP huita kitendakazi cha kusoma na hoja ya tatu ya 4096, na programu ya UDP huita kitendakazi cha recvfrom kwa hoja ya tatu ya 4096. Je, kuna tofauti yoyote kati ya simu hizi?

2. Katika Uorodheshaji 8.2, nini kitatokea ikiwa tutabadilisha hoja ya mwisho ya kitendakazi cha sendto (tulichoipa lebo len) na clilen ya hoja?

3. Kusanya na kuendesha seva ya UDP kutoka kwenye Orodha 8.1 na 8.4, na kisha mteja kutoka kwenye Orodha 8.3 na 8.4. Hakikisha mteja na seva wanafanya kazi pamoja.

4. Endesha programu ya ping kwenye dirisha moja, ukibainisha -i chaguo 60 (tuma pakiti moja kila sekunde 60; mifumo mingine hutumia swichi ya I badala ya i), chaguo la -v (chapisha ujumbe wote wa makosa ya ICMP), na uweke. anwani ya kitanzi yenyewe ( kawaida 127.0.0.1). Tutatumia programu hii kuona hitilafu isiyoweza kufikiwa ya mlango wa ICMP iliyorejeshwa na seva pangishi. Kisha endesha mteja wetu kutoka kwa zoezi la awali kwenye dirisha lingine, ukibainisha anwani ya IP ya nodi fulani ambayo haiendeshi seva. Nini kinaendelea?

5. Kuangalia Mtini. 8.3, tulisema kwamba kila tundu la TCP lililounganishwa lina bafa yake ya kupokea. Je, unadhani soketi ya kusikiliza ina bafa yake ya kupokea?

6. Tumia programu ya soksi (angalia Sehemu B.3) na zana kama vile tcpdump (angalia Sehemu B.5) ili kuthibitisha taarifa katika Sehemu ya 8.10: ikiwa mteja anatumia kipengele cha kuunganisha ili kufunga anwani ya IP kwenye tundu lake, lakini hutuma datagramu inayotokana na kiolesura tofauti, basi datagramu inayotokana ina anwani ya IP ambayo ilihusishwa na tundu, hata kama hailingani na kiolesura cha asili.

7. Kukusanya programu kutoka kwa Sehemu ya 8.13 na kukimbia mteja na seva kwenye nodes tofauti. Weka printf kwenye mteja kila wakati datagram inapoandikwa kwenye tundu. Je, hii inabadilisha asilimia ya pakiti zilizopokelewa? Kwa nini? Piga simu printf kutoka kwa seva kila wakati datagramu inasomwa kutoka kwa soketi. Je, hii inabadilisha asilimia ya pakiti zilizopokelewa? Kwa nini?

8. Je, ni urefu gani mkubwa zaidi tunaoweza kupitisha kwenye kitendakazi cha sendto kwa soketi ya UDP/IPv4, yaani, ni kiasi gani kikubwa zaidi cha data kinachoweza kutoshea kwenye datagramu ya UDP/IPv4? Ni mabadiliko gani katika kesi ya UDP/IPv6?

Rekebisha Orodha 8.4 ili kutuma datagramu moja ya UDP ya ukubwa wa juu zaidi, isome tena, na uchapishe idadi ya baiti zilizorejeshwa na recvfrom .

9. Rekebisha Orodha ya 8.15 ili ilingane na RFC 1122: IP_RECVDSTADDR inapaswa kutumika kwa soketi ya UDP.

Mwishoni mwa Sehemu ya 8.9, tulitaja kuwa makosa ya asynchronous hayarudishwi kwenye tundu la UDP ikiwa tundu halijaunganishwa. Tunaweza kuita kiunganishi kwenye tundu la UDP (tazama sehemu ya 4.3). Lakini hii haitasababisha chochote kama muunganisho wa TCP: hakuna kushikana mikono kwa njia tatu. Kernel hukagua tu kuona ikiwa lengwa linajulikana kuwa haliwezi kufikiwa, kisha hurekodi anwani ya IP ya rika na nambari ya bandari, ambayo iko katika muundo wa anwani ya tundu iliyopitishwa kwa kazi ya kuunganisha, na mara moja inarudisha udhibiti kwenye mchakato wa kupiga simu.

KUMBUKA

Kupakia kitendakazi cha kuunganisha kwa kipengele hiki kipya kwa soketi za UDP kunaweza kutatanisha. Ikiwa mkataba unatumiwa jina la sockname ni anwani itifaki ya ndani, na peername ni anwani ya itifaki ya mbali, basi itakuwa bora kuita kazi hii setpeername. Vivyo hivyo, chaguo la kukokotoa lingeitwa vyema zaidi jinackname.

Kwa kuzingatia hili, ni muhimu kuelewa tofauti kati ya aina mbili za soketi za UDP.

c– Soketi ya UDP ambayo haijaunganishwa ni soketi chaguo-msingi ya UDP iliyoundwa.

c– Soketi iliyounganishwa ya UDP ni matokeo ya kuita kitendakazi cha unganisho kwenye soketi ya UDP.

Tundu la UDP lililounganishwa lina tofauti tatu kutoka kwa tundu lisilounganishwa, ambalo linaundwa kwa default.

1. Hatuwezi tena kuweka anwani ya IP lengwa na mlango kwa ajili ya operesheni ya kutoa. Hiyo ni, tunatumia kitendakazi cha kuandika au kutuma badala ya kitendakazi cha sendto. Chochote kilichoandikwa kwenye tundu la UDP lililounganishwa hutumwa kiotomatiki kwa anwani (kama vile anwani ya IP na mlango) iliyobainishwa na chaguo la kukokotoa la kuunganisha.

KUMBUKA

Sawa na TCP, tunaweza kupigia simu kitendakazi cha sendto kwenye soketi ya UDP iliyoambatishwa, lakini hatuwezi kubainisha anwani lengwa. Hoja ya tano kwa kipengele cha kukokotoa kutuma (kielekezi kwa muundo wa anwani ya tundu) lazima iwe kielekezi, na hoja ya sita (ukubwa wa muundo wa anwani ya tundu) lazima iwe batili. Kiwango cha POSIX kinabainisha kuwa wakati hoja ya tano ni kielekezi kisicho na maana, hoja ya sita inapuuzwa.

2. Badala ya chaguo za kukokotoa recvfrom, tunatumia kitendakazi cha kusoma au recv. Datagramu pekee zilizorejeshwa na kernel kwa operesheni ya ingizo kwenye soketi iliyounganishwa ya UDP ni datagramu zinazotoka kwa anwani iliyobainishwa katika chaguo la kukokotoa la kuunganisha. Datagramu zinazolengwa kwa anwani ya itifaki ya ndani ya soketi ya UDP iliyounganishwa (kama vile anwani ya IP na mlango) lakini inayotoka kwa anwani ya itifaki isipokuwa ile ambayo tundu iliunganishwa kwa kutumia kitendakazi cha kuunganisha hazitumwi kwenye soketi iliyounganishwa. Hii inazuia soketi ya UDP iliyoambatishwa ili kuiruhusu kubadilishana datagramu na rika moja tu.

KUMBUKA

Kwa usahihi, datagrams hubadilishwa na anwani moja tu ya IP, na si kwa interlocutor moja, kwa kuwa inaweza kuwa anwani ya IP ya multicast, na hivyo kuwakilisha kundi la interlocutors.

3. Makosa ya Asynchronous hurejeshwa kwa mchakato tu wakati wa operesheni kwenye tundu la UDP lililoambatishwa. Kama matokeo, kama tulivyokwisha sema, tundu la UDP ambalo halijaunganishwa haipokei makosa yoyote ya asynchronous.

Katika meza 8.2 huleta pamoja sifa zilizoorodheshwa katika aya ya kwanza kama inavyotumika kwa 4.4BSD.

Jedwali 8.2. Soketi za TCP na UDP: anwani ya itifaki lengwa inaweza kubainishwa

KUMBUKA

POSIX inabainisha kuwa operesheni ya kipini ambayo haibainishi anwani lengwa kwenye soketi isiyoambatishwa ya UDP lazima irudishe hitilafu ya ENOTCONN badala ya hitilafu ya EDESTADDRREQ.

Solaris 2.5 huruhusu utendakazi wa sendto, ambao hubainisha anwani lengwa ya soketi ya UDP iliyoambatishwa. POSIX inabainisha kuwa hitilafu ya EISCONN inapaswa kurejeshwa katika hali hii.

Katika Mtini. Sehemu ya 8.7 inatoa muhtasari wa taarifa kuhusu soketi ya UDP iliyoambatishwa.

Mchele. 8.7. Soketi iliyoambatanishwa ya UDP

Maombi huita kazi ya kuunganisha, ikibainisha anwani ya IP na nambari ya bandari ya interlocutor. Kisha hutumia vipengele vya kusoma na kuandika ili kubadilishana data na mhusika mwingine.

Datagramu zinazotoka kwa anwani ya IP au mlango mwingine wowote (ambao tunaashiria kama "???" kwenye Mchoro 8.7) hazitumwi kwa soketi iliyoambatishwa kwa sababu anwani ya IP ya chanzo au mlango wa UDP hailingani na anwani ya itifaki. imeunganishwa kwa kutumia kitendakazi cha kuunganisha. Datagramu hizi zinaweza kuwasilishwa kwa soketi nyingine ya UDP kwenye seva pangishi. Ikiwa hakuna soketi nyingine inayolingana ya datagramu inayoingia, UDP itaipuuza na kutoa ujumbe wa mlango wa ICMP usioweza kufikiwa.

Kwa muhtasari wa yaliyo hapo juu, tunaweza kusema kwamba mteja wa UDP au seva inaweza kuita kitendakazi cha kuunganisha ikiwa tu mchakato huo unatumia tundu la UDP kuwasiliana na mpatanishi mmoja tu. Kwa kawaida ni kiteja cha UDP kinachoita kitendakazi cha kuunganisha, lakini kuna programu ambazo seva ya UDP huwasiliana na mteja mmoja kwa kila muda mrefu(km TFTP), katika hali ambayo mteja na seva huita kitendakazi cha kuunganisha.

Mfano mwingine wa mwingiliano wa muda mrefu ni DNS (Mchoro 8.8).

Mchele. 8.8. Mfano wa wateja wa DNS na seva na kazi ya kuunganisha

Kiteja cha DNS kinaweza kusanidiwa kutumia seva moja au zaidi, kwa kawaida kwa kuorodhesha anwani za IP za seva kwenye faili ya /etc/resolv.conf. Ikiwa kuna seva moja tu iliyoorodheshwa kwenye faili hii (mteja huyo ndiye mstatili wa kushoto kabisa kwenye takwimu), mteja anaweza kupiga simu ya kiunganishi, lakini ikiwa seva nyingi zimeorodheshwa (mstatili wa pili wa kulia kwenye takwimu), mteja hawezi kupiga simu. kazi ya kuunganisha. Kwa kawaida, seva ya DNS pia inashughulikia maombi yoyote ya mteja, hivyo seva haziwezi kuita kazi ya kuunganisha.

Sasa tutaangalia jinsi programu inavyoathiriwa na kutokuwepo kwa udhibiti wowote wa mtiririko katika UDP. Kwanza tutabadilisha utendakazi wetu wa dg_cli ili itume idadi maalum ya datagramu. Haitasoma tena kutoka kwa ingizo la kawaida. Orodha ya 8.9 inaonyesha toleo jipya la chaguo la kukokotoa. Chaguo hili la kukokotoa hutuma datagramu 2000 za UDP za baiti 1400 kila moja kwa seva.

Orodha 8.9. Chaguo la kukokotoa la dg_cli hutuma idadi isiyobadilika ya datagramu kwa seva

//udpcliserv/dgcliloop1.c

1 #pamoja na "unp.h"

2 #fafanua NDG 2000 /* nambari ya data ya kutuma */

3 #fafanua urefu wa DGLEN 1400 /* wa kila datagramu */

5 dg_cli(FILE *fp, int sockfd, const SA *pseraddr, socklen_t servlen)

8char sendline;

10 Sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);

Kisha tunarekebisha seva ili kupokea datagrams na kuhesabu idadi ya datagramu zilizopokelewa. Seva haionyeshi tena datagramu kwa mteja. Orodha ya 8.10 inaonyesha kazi mpya ya dg_echo. Tunapositisha mchakato wa seva kwa kubonyeza kitufe cha kukatiza kwenye terminal (ambayo husababisha ishara ya SIGINT kutumwa kwa mchakato), seva huchapisha nambari ya data iliyopokelewa na kuondoka.

Orodha 8.10. Chaguo za kukokotoa za dg_echo, ambazo huhesabu datagramu zilizopokelewa

//udpcliserv/dgecholoop1.c

1 #pamoja na "unp.h"

2 utupu tuli recvfrom_int(int);

3 hesabu ya int tuli;

5 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)

7 socklen_t len;

8 char mesg;

9 Mawimbi(SIGINT, recvfrom_int);

11 len = clilen;

12 Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);

17 recvfrom_int(int signo)

19 printf("\nkupokea datagramu %d\n", hesabu);

Sasa tunaendesha seva kwenye node ya freebsd, ambayo ni kompyuta polepole Kituo cha SPARCS. Tunaendesha mteja kwenye mfumo wa kasi zaidi wa RS/6000 na mfumo wa uendeshaji wa aix. Wameunganishwa moja kwa moja kwa kila mmoja Kituo cha Ethaneti kwa 100 Mbit / s. Kwa kuongezea, tunaendesha netstat -s kwenye nodi ya seva kabla na baada ya mteja na seva kuanza, kwani matokeo ya takwimu yataonyesha ni data ngapi tulizopoteza. Orodha ya 8.11 inaonyesha matokeo ya seva.

Orodha 8.11. Pato kwenye nodi ya seva

freebsd % netstat -s -p udp

Datagramu 71208 zimepokelewa

0 na kichwa ambacho hakijakamilika

0 na uga mbaya wa urefu wa data

0 na hundi mbaya

0 bila hundi

832 ilishuka kwa sababu ya kutokuwa na tundu

Datagramu 16 za utangazaji/upeperushaji nyingi zimeshuka kwa sababu ya kutokuwa na tundu

1971 ilishuka kwa sababu ya buffers kamili za soketi

0 sio kwa pcb ya haraka

Pato la data ya 137685

freebsd % udpser06 zindua seva yetu

mteja hutuma datagrams

^C Ili kumaliza kazi ya mteja, weka ishara yetu ya kukatiza

freebsd % netstat -s -p udp

Datagramu 73208 zimepokelewa

0 na kichwa ambacho hakijakamilika

0 na uga mbaya wa urefu wa data

0 na hundi mbaya

0 bila hundi

832 ilishuka kwa sababu ya kutokuwa na tundu

Datagramu 16 za utangazaji/upeperushaji nyingi zimeshuka kwa sababu ya kutokuwa na tundu

3941 imeshuka kwa sababu ya buffers kamili za soketi

0 sio kwa pcb ya haraka

Pato la data ya 137685

Mteja alituma datagrams 2000, lakini programu ya seva ilipokea tu 30 kati yao, ikimaanisha kiwango cha hasara cha 98%. Si seva wala mteja anayepokea ujumbe kwamba datagramu hizi zimepotea. Kama tulivyosema, UDP haina uwezo wa kudhibiti mtiririko - haitegemei. Kama tulivyoonyesha, ni rahisi kwa mtumaji wa UDP kufurika bafa ya mpokeaji.

Ikiwa tunatazama matokeo ya netstat, tunaona kwamba jumla ya idadi ya datagrams zilizopokelewa na nodi ya seva (sio programu ya seva) ni 2000 (73,208 - 71,208). Kikaunta iliyodondoshwa kwa sababu ya kaunta kamili ya bafa za soketi inaonyesha ni datagram ngapi zilipokelewa na UDP na kupuuzwa kwa sababu bafa ya tundu la kupokea ilikuwa imejaa. Thamani hii ni 1970 (3941 - 1971), ambayo inapoongezwa kwa matokeo ya hesabu ya datagramu iliyopokelewa ya programu (30), inatoa jumla ya datagramu 2000 zilizopokelewa na nodi. Kwa bahati mbaya, hesabu ya datagramu ya netstat iliyotupwa kwa sababu ya bafa kamili ni pana ya mfumo. Hakuna njia ya kuamua ni maombi gani (kwa mfano, ambayo Bandari za UDP) inaathiri.

Idadi ya datagramu zilizopokelewa na seva katika mfano huu sio ya kuamua. Inategemea mambo mengi, kama vile mzigo wa mtandao, nodi ya mteja na upakiaji wa nodi za seva.

Ikiwa tunaendesha mteja sawa na seva sawa, lakini wakati huu mteja yuko kwenye mfumo wa polepole wa Jua na seva iko kwenye mfumo wa kasi wa RS/6000, hakuna datagramu zinazopotea.

aix % udpser06

^? baada ya mteja kumaliza kufanya kazi, ingiza ishara yetu ya kukatiza

kupokea datagram 2000

Mwishoni mwa Sehemu ya 8.9, tulitaja kuwa makosa ya asynchronous hayarudishwi kwenye tundu la UDP ikiwa tundu halijaunganishwa. Tunaweza kuita kiunganishi kwenye tundu la UDP (tazama sehemu ya 4.3). Lakini hii haitasababisha chochote kama muunganisho wa TCP: hakuna kushikana mikono kwa njia tatu. Kernel hukagua tu kuona ikiwa lengwa linajulikana kuwa haliwezi kufikiwa, kisha hurekodi anwani ya IP ya rika na nambari ya bandari, ambayo iko katika muundo wa anwani ya tundu iliyopitishwa kwa kazi ya kuunganisha, na mara moja inarudisha udhibiti kwenye mchakato wa kupiga simu.

KUMBUKA

Kupakia kitendakazi cha kuunganisha kwa kipengele hiki kipya kwa soketi za UDP kunaweza kutatanisha. Ikiwa mkataba ni kwamba sockname ni anwani ya itifaki ya ndani na jina la mtumiaji ni anwani ya itifaki ya mbali, basi chaguo hili la kukokotoa lingeitwa vyema setpeername. Vivyo hivyo, chaguo la kukokotoa lingeitwa vyema zaidi jinackname.

Kwa kuzingatia hili, ni muhimu kuelewa tofauti kati ya aina mbili za soketi za UDP.

c– Soketi ya UDP ambayo haijaunganishwa ni soketi chaguo-msingi ya UDP iliyoundwa.

c– Soketi iliyounganishwa ya UDP ni matokeo ya kuita kitendakazi cha unganisho kwenye soketi ya UDP.

Tundu la UDP lililounganishwa lina tofauti tatu kutoka kwa tundu lisilounganishwa, ambalo linaundwa kwa default.

1. Hatuwezi tena kuweka anwani ya IP lengwa na mlango kwa ajili ya operesheni ya kutoa. Hiyo ni, tunatumia kitendakazi cha kuandika au kutuma badala ya kitendakazi cha sendto. Chochote kilichoandikwa kwenye tundu la UDP lililounganishwa hutumwa kiotomatiki kwa anwani (kama vile anwani ya IP na mlango) iliyobainishwa na chaguo la kukokotoa la kuunganisha.

KUMBUKA

Sawa na TCP, tunaweza kupigia simu kitendakazi cha sendto kwenye soketi ya UDP iliyoambatishwa, lakini hatuwezi kubainisha anwani lengwa. Hoja ya tano kwa kipengele cha kukokotoa kutuma (kielekezi kwa muundo wa anwani ya tundu) lazima iwe kielekezi, na hoja ya sita (ukubwa wa muundo wa anwani ya tundu) lazima iwe batili. Kiwango cha POSIX kinabainisha kuwa wakati hoja ya tano ni kielekezi kisicho na maana, hoja ya sita inapuuzwa.

2. Badala ya chaguo za kukokotoa recvfrom, tunatumia kitendakazi cha kusoma au recv. Datagramu pekee zilizorejeshwa na kernel kwa operesheni ya ingizo kwenye soketi iliyounganishwa ya UDP ni datagramu zinazotoka kwa anwani iliyobainishwa katika chaguo la kukokotoa la kuunganisha. Datagramu zinazolengwa kwa anwani ya itifaki ya ndani ya soketi ya UDP iliyounganishwa (kama vile anwani ya IP na mlango) lakini inayotoka kwa anwani ya itifaki isipokuwa ile ambayo tundu iliunganishwa kwa kutumia kitendakazi cha kuunganisha hazitumwi kwenye soketi iliyounganishwa. Hii inazuia soketi ya UDP iliyoambatishwa ili kuiruhusu kubadilishana datagramu na rika moja tu.

KUMBUKA

Kwa usahihi, datagrams hubadilishwa na anwani moja tu ya IP, na si kwa interlocutor moja, kwa kuwa inaweza kuwa anwani ya IP ya multicast, na hivyo kuwakilisha kundi la interlocutors.

3. Hitilafu za Asynchronous zinarejeshwa kwa mchakato tu kwa uendeshaji kwenye tundu la UDP lililounganishwa. Kama matokeo, kama tulivyokwisha sema, tundu la UDP ambalo halijaunganishwa haipokei makosa yoyote ya asynchronous.

Katika meza 8.2 huleta pamoja sifa zilizoorodheshwa katika aya ya kwanza kama inavyotumika kwa 4.4BSD.

Jedwali 8.2. Soketi za TCP na UDP: anwani ya itifaki lengwa inaweza kubainishwa

KUMBUKA

POSIX inabainisha kuwa operesheni ya kipini ambayo haibainishi anwani lengwa kwenye soketi isiyoambatishwa ya UDP lazima irudishe hitilafu ya ENOTCONN badala ya hitilafu ya EDESTADDRREQ.

Solaris 2.5 huruhusu utendakazi wa sendto, ambao hubainisha anwani lengwa ya soketi ya UDP iliyoambatishwa. POSIX inabainisha kuwa hitilafu ya EISCONN inapaswa kurejeshwa katika hali hii.

Katika Mtini. Sehemu ya 8.7 inatoa muhtasari wa taarifa kuhusu soketi ya UDP iliyoambatishwa.

Mchele. 8.7. Soketi iliyoambatanishwa ya UDP

Maombi huita kazi ya kuunganisha, ikibainisha anwani ya IP na nambari ya bandari ya interlocutor. Kisha hutumia vipengele vya kusoma na kuandika ili kubadilishana data na mhusika mwingine.

Datagramu zinazotoka kwa anwani ya IP au mlango mwingine wowote (ambao tunaashiria kama "???" kwenye Mchoro 8.7) hazitumwi kwa soketi iliyoambatishwa kwa sababu anwani ya IP ya chanzo au mlango wa UDP hailingani na anwani ya itifaki. imeunganishwa kwa kutumia kitendakazi cha kuunganisha. Datagramu hizi zinaweza kuwasilishwa kwa soketi nyingine ya UDP kwenye seva pangishi. Ikiwa hakuna soketi nyingine inayolingana ya datagramu inayoingia, UDP itaipuuza na kutoa ujumbe wa mlango wa ICMP usioweza kufikiwa.

Kwa muhtasari wa yaliyo hapo juu, tunaweza kusema kwamba mteja wa UDP au seva inaweza kuita kitendakazi cha kuunganisha ikiwa tu mchakato huo unatumia tundu la UDP kuwasiliana na mpatanishi mmoja tu. Kwa kawaida ni kiteja cha UDP kinachoita kitendakazi cha kuunganisha, lakini kuna programu ambazo seva ya UDP huwasiliana na mteja mmoja kwa muda mrefu (kama vile TFTP), ambapo mteja na seva huita muunganisho. kazi.

Mfano mwingine wa mwingiliano wa muda mrefu ni DNS (Mchoro 8.8).

Mchele. 8.8. Mfano wa wateja wa DNS na seva na kazi ya kuunganisha

Kiteja cha DNS kinaweza kusanidiwa kutumia seva moja au zaidi, kwa kawaida kwa kuorodhesha anwani za IP za seva kwenye faili ya /etc/resolv.conf. Ikiwa kuna seva moja tu iliyoorodheshwa kwenye faili hii (mteja huyo ndiye mstatili wa kushoto kabisa kwenye takwimu), mteja anaweza kupiga simu ya kiunganishi, lakini ikiwa seva nyingi zimeorodheshwa (mstatili wa pili wa kulia kwenye takwimu), mteja hawezi kupiga simu. kazi ya kuunganisha. Kwa kawaida, seva ya DNS pia inashughulikia maombi yoyote ya mteja, hivyo seva haziwezi kuita kazi ya kuunganisha.

Sasa tutaangalia jinsi programu inavyoathiriwa na kutokuwepo kwa udhibiti wowote wa mtiririko katika UDP. Kwanza tutabadilisha utendakazi wetu wa dg_cli ili itume idadi maalum ya datagramu. Haitasoma tena kutoka kwa ingizo la kawaida. Orodha ya 8.9 inaonyesha toleo jipya la chaguo la kukokotoa. Chaguo hili la kukokotoa hutuma datagramu 2000 za UDP za baiti 1400 kila moja kwa seva.

Orodha 8.9. Chaguo la kukokotoa la dg_cli hutuma idadi isiyobadilika ya datagramu kwa seva

//udpcliserv/dgcliloop1.c

1 #pamoja na "unp.h"

2 #fafanua NDG 2000 /* nambari ya data ya kutuma */

3 #fafanua urefu wa DGLEN 1400 /* wa kila datagramu */

5 dg_cli(FILE *fp, int sockfd, const SA *pseraddr, socklen_t servlen)

8char sendline;

10 Sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);

Kisha tunarekebisha seva ili kupokea datagrams na kuhesabu idadi ya datagramu zilizopokelewa. Seva haionyeshi tena datagramu kwa mteja. Orodha ya 8.10 inaonyesha kazi mpya ya dg_echo. Tunapositisha mchakato wa seva kwa kubonyeza kitufe cha kukatiza kwenye terminal (ambayo husababisha ishara ya SIGINT kutumwa kwa mchakato), seva huchapisha nambari ya data iliyopokelewa na kuondoka.

Orodha 8.10. Chaguo za kukokotoa za dg_echo, ambazo huhesabu datagramu zilizopokelewa

//udpcliserv/dgecholoop1.c

1 #pamoja na "unp.h"

2 utupu tuli recvfrom_int(int);

3 hesabu ya int tuli;

5 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)

7 socklen_t len;

8 char mesg;

9 Mawimbi(SIGINT, recvfrom_int);

11 len = clilen;

12 Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);

17 recvfrom_int(int signo)

19 printf("\nkupokea datagramu %d\n", hesabu);

Sasa tunaendesha seva kwenye nodi freebsd, ambayo ni kompyuta ya polepole ya SPARCStation. Tunaendesha mteja kwenye mfumo wa kasi zaidi wa RS/6000 na mfumo wa uendeshaji wa aix. Wameunganishwa moja kwa moja kwa kila mmoja kupitia kiungo cha 100 Mbit/s Ethernet. Kwa kuongezea, tunaendesha netstat -s kwenye nodi ya seva kabla na baada ya mteja na seva kuanza, kwani matokeo ya takwimu yataonyesha ni data ngapi tulizopoteza. Orodha ya 8.11 inaonyesha matokeo ya seva.

Orodha 8.11. Pato kwenye nodi ya seva

freebsd % netstat -s -p udp

Datagramu 71208 zimepokelewa

0 na kichwa ambacho hakijakamilika

0 na uga mbaya wa urefu wa data

0 na hundi mbaya

0 bila hundi

832 ilishuka kwa sababu ya kutokuwa na tundu

Datagramu 16 za utangazaji/upeperushaji nyingi zimeshuka kwa sababu ya kutokuwa na tundu

1971 ilishuka kwa sababu ya buffers kamili za soketi

0 sio kwa pcb ya haraka

Pato la data ya 137685

freebsd % udpser06 zindua seva yetu

mteja hutuma datagrams

^C Ili kumaliza kazi ya mteja, weka ishara yetu ya kukatiza

freebsd % netstat -s -p udp

Datagramu 73208 zimepokelewa

0 na kichwa ambacho hakijakamilika

0 na uga mbaya wa urefu wa data

0 na hundi mbaya

0 bila hundi

832 ilishuka kwa sababu ya kutokuwa na tundu

Datagramu 16 za utangazaji/upeperushaji nyingi zimeshuka kwa sababu ya kutokuwa na tundu

3941 imeshuka kwa sababu ya buffers kamili za soketi

0 sio kwa pcb ya haraka

Pato la data ya 137685

Mteja alituma datagrams 2000, lakini programu ya seva ilipokea tu 30 kati yao, ikimaanisha kiwango cha hasara cha 98%. Si seva wala mteja anayepokea ujumbe kwamba datagramu hizi zimepotea. Kama tulivyosema, UDP haina uwezo wa kudhibiti mtiririko - haitegemei. Kama tulivyoonyesha, ni rahisi kwa mtumaji wa UDP kufurika bafa ya mpokeaji.

Ikiwa tunatazama matokeo ya netstat, tunaona kwamba jumla ya idadi ya datagrams zilizopokelewa na nodi ya seva (sio programu ya seva) ni 2000 (73,208 - 71,208). Kikaunta iliyodondoshwa kwa sababu ya kaunta kamili ya bafa za soketi inaonyesha ni datagram ngapi zilipokelewa na UDP na kupuuzwa kwa sababu bafa ya tundu la kupokea ilikuwa imejaa. Thamani hii ni 1970 (3941 - 1971), ambayo inapoongezwa kwa matokeo ya hesabu ya datagramu iliyopokelewa ya programu (30), inatoa jumla ya datagramu 2000 zilizopokelewa na nodi. Kwa bahati mbaya, hesabu ya datagramu ya netstat iliyotupwa kwa sababu ya bafa kamili ni pana ya mfumo. Hakuna njia ya kuamua ni programu zipi (km ni bandari gani za UDP) zimeathiriwa.

Idadi ya datagramu zilizopokelewa na seva katika mfano huu sio ya kuamua. Inategemea mambo mengi, kama vile mzigo wa mtandao, nodi ya mteja na upakiaji wa nodi za seva.

Ikiwa tunaendesha mteja sawa na seva sawa, lakini wakati huu mteja yuko kwenye mfumo wa polepole wa Jua na seva iko kwenye mfumo wa kasi wa RS/6000, hakuna datagramu zinazopotea.

aix % udpser06

^? baada ya mteja kumaliza kufanya kazi, ingiza ishara yetu ya kukatiza

kupokea datagram 2000

Ni wakati wa kutumia Erlang kwa madhumuni yaliyokusudiwa - kutekeleza huduma ya mtandao. Mara nyingi, huduma kama hizo hufanywa kwa msingi wa seva ya wavuti, juu ya itifaki ya HTTP. Lakini tutachukua kiwango cha chini - soketi za TCP na UDP.

Nadhani tayari unajua jinsi mtandao unavyofanya kazi, Itifaki ya Mtandao, Itifaki ya Datagram ya Mtumiaji na Itifaki ya Udhibiti wa Usambazaji ni nini. Mada hii inajulikana kwa watengeneza programu wengi. Lakini ikiwa kwa sababu fulani umeikosa, itabidi kwanza upate na kisha urudi kwenye somo hili.

Soketi ya UDP

Wacha tukumbuke kwa ujumla UDP ni nini:

  • itifaki ya uhamisho wa ujumbe mfupi (Datagram);
  • Usafirishaji wa haraka;
  • hakuna muunganisho unaoendelea kati ya mteja na seva, isiyo na utaifa;
  • uwasilishaji wa ujumbe na agizo la uwasilishaji haujahakikishiwa.

Kufanya kazi na UDP, moduli ya gen_udp inatumiwa.

Wacha tuzindue nodi mbili na tuanzishe mawasiliano kati yao.

Kwenye nodi ya 1, fungua UDP kwenye bandari 2000:

1> (sawa, Soketi) = gen_udp:open(2000, ). (sawa, #Bandari<0.587>}

Kupiga simu gen_udp:fungua/2, tunapitisha nambari ya bandari na orodha ya chaguo. Orodha ya chaguzi zote zinazowezekana ni kubwa sana, lakini tunavutiwa na mbili kati yao:

binary-- tundu linafunguliwa katika hali ya binary. Vinginevyo, tundu linaweza kufunguliwa kwa hali ya maandishi kwa kutaja chaguo orodha. Tofauti ni jinsi tunavyotafsiri data iliyopokelewa kutoka kwa soketi - kama mkondo wa kawaida, au kama maandishi.

(inafanya kazi, kweli)-- tundu wazi ndani hali amilifu, ambayo inamaanisha kuwa data inayofika kwenye soketi itatumwa kama ujumbe kwa kisanduku cha barua cha uzi unaomiliki soketi. Zaidi juu ya hii hapa chini.

Kwenye nodi ya 2, fungua UDP kwenye bandari 2001:

1> (sawa, Soketi) = gen_udp:open(2001, ). (sawa, #Bandari<0.587>}

Na tutatuma ujumbe kutoka nodi ya 1 hadi ya 2:

2> gen_udp:send(Socket, (127,0,0,1), 2001,<<"Hello from 2000">>). sawa

Kupiga simu gen_udp:tuma/4, tunasambaza tundu, anwani na bandari ya mpokeaji, na ujumbe wenyewe.

Anwani inaweza kuwa jina la kikoa kama kamba au atomi, au anwani ya IPv4 kama nakala ya nambari 4, au anwani ya IPv6 kama nakala ya nambari 8.

Kwenye nodi ya 2 tutahakikisha kuwa ujumbe umefika:

2> <0.587>,{127,0,0,1},2000,<<"Hello from 2000">>>) sawa

Ujumbe unafika kama nakala (udp, Soketi, SenderAddress, SenderPort, Packet).

Wacha tutume ujumbe kutoka nodi ya 2 hadi ya 1:

3> gen_udp:send(Socket, (127,0,0,1), 2000,<<"Hello from 2001">>). sawa

Kwenye nodi ya 1, tutahakikisha kuwa ujumbe umefika:

3> safisha (). Shell ilipata (udp, #Port<0.587>,{127,0,0,1},2001,<<"Hello from 2001">>>) sawa

Kama unaweza kuona, kila kitu ni rahisi hapa.

Hali ya tundu inayotumika na tulivu

NA gen_udp, Na gen_tcp, zote zina mpangilio mmoja muhimu: hali ya kufanya kazi na data inayoingia. Hii inaweza kuwa hali inayotumika (inafanya kazi, kweli), au hali ya passiv (hai, si kweli).

Katika hali amilifu, mazungumzo hupokea pakiti zinazoingia kama ujumbe kwenye kisanduku chake cha barua. Na zinaweza kupokelewa na kuchakatwa kwa kupiga simu pokea, kama ujumbe mwingine wowote.

Kwa tundu la udp hizi ni jumbe kama:

(udp, Soketi, SenderAddress, SenderPort, Packet)

tayari tumewaona:

(udp, #Port<0.587>,{127,0,0,1},2001,<<"Hello from 2001">>}

Kwa soketi ya tcp ujumbe sawa:

(tcp, tundu, pakiti)

Hali amilifu ni rahisi kutumia, lakini ni hatari kwa sababu mteja anaweza kufurika kwenye foleni ya ujumbe wa mazungumzo, kukosa kumbukumbu, na kuvunja nodi. Kwa hiyo, hali ya passiv inapendekezwa.

Katika hali ya passiv, data lazima irejeshwe kwa simu gen_udp:recv/3 Na gen_tcp:recv/3:

Gen_udp:recv(Soketi, Urefu, Muda Umekwisha) -> (sawa, (Anwani, Mlango, Pakiti)) | (kosa, Sababu) gen_tcp:recv(Soketi, Urefu, Muda Umekwisha) -> (sawa, Pakiti) | (kosa, sababu)

Hapa tunaonyesha ni ka ngapi za data tunataka kusoma kutoka kwa tundu. Ikiwa data hii iko, basi tunaipokea mara moja. Ikiwa sivyo, simu itazuiwa hadi data ya kutosha ifike. Unaweza kutaja Timeout ili kuepuka kuzuia thread kwa muda mrefu.

Hata hivyo, gen_udp:recv inapuuza hoja ya Urefu na inarudisha data yoyote iliyo kwenye tundu. Au inazuia na kungoja data fulani ikiwa hakuna kitu kwenye tundu. Haijulikani kwa nini hoja ya Urefu iko kwenye API hata kidogo.

Kwa gen_tcp:recv Hoja ya Urefu inafanya kazi kama inavyotarajiwa. Isipokuwa chaguo limebainishwa (pakiti, saizi), ambayo itajadiliwa hapa chini.

Bado kuna chaguo (inafanya kazi, mara moja). Katika hali hii, soketi huanza katika hali amilifu, inapokea pakiti ya kwanza ya data kama ujumbe, na mara moja inabadilisha hali ya passiv.

Soketi ya TCP

Wacha tukumbuke kwa jumla TCP ni nini:

  • itifaki ya kuaminika ya uhamisho wa data inahakikisha utoaji wa ujumbe na utaratibu wa utoaji;
  • uhusiano wa kudumu kati ya mteja na seva, ina hali;
  • ziada ya ziada kwa ajili ya kuanzisha na kufunga miunganisho na kuhamisha data.

Ikumbukwe kwamba kudumisha uhusiano wa mara kwa mara na maelfu mengi ya wateja kwa muda mrefu ni ghali. Viunganisho vyote lazima vifanye kazi kwa kujitegemea, ambayo ina maana katika nyuzi tofauti. Kwa lugha nyingi za programu (lakini sio Erlang) hili ni shida kubwa.

Hii ndiyo sababu itifaki ya HTTP ni maarufu sana, ambayo, ingawa inafanya kazi juu ya tundu la TCP, inamaanisha muda mfupi wa mwingiliano. Hii inaruhusu idadi ndogo ya nyuzi (makumi au mamia) kutumikia idadi kubwa zaidi ya wateja (maelfu, makumi ya maelfu).

Katika baadhi ya matukio, bado kuna haja ya kuwa na miunganisho ya kudumu kati ya mteja na seva. Kwa mfano, kwa mazungumzo au kwa michezo ya wachezaji wengi. Na hapa Erlang ana washindani wachache.

Kufanya kazi na TCP, moduli ya gen_tcp inatumiwa.

Kufanya kazi na tundu la TCP ni ngumu zaidi kuliko kufanya kazi na tundu la UDP. Sasa tuna majukumu ya mteja na seva ambayo yanahitaji utekelezaji tofauti. Hebu fikiria chaguo la utekelezaji wa seva.

Moduli (seva). -uza nje (). start() -> start(1234). start(Port) -> spawn(?MODULE, server, ), sawa. server(Port) -> io:format("start server at port ~p~n", ), (ok, ListenSocket) = gen_tcp:listen(Port, ), ) || ID<- lists:seq(1, 5)], timer:sleep(infinity), ok. accept(Id, ListenSocket) ->io:format("Soketi #~p wait for client~n", ), (ok, _Socket) = gen_tcp:kubali(ListenSocket), io:format("Soketi #~p, session started~n", ), handle_connection (Id, ListenSocket). handle_connection(Id, ListenSocket) -> pokea (tcp, Socket, Msg) -> io:format("Socket #~p got message: ~p~n", ), gen_tcp:send(Socket, Msg), handle_connection(Id , ListenSocket); (tcp_closed, _Socket) ->

Kuna aina mbili za soketi: Sikiliza Soketi Na Kubali Soketi. Kuna Soketi moja tu ya Kusikiliza, inakubali maombi yote ya muunganisho. Unahitaji Soketi nyingi za Kubali, moja kwa kila muunganisho. Thread inayounda tundu inakuwa mmiliki wa tundu. Ikiwa thread ya mmiliki inatoka, tundu imefungwa moja kwa moja. Kwa hiyo, tunaunda thread tofauti kwa kila tundu.

Soketi ya Kusikiliza lazima iwe inaendeshwa kila wakati, na ili kufanya hivi, uzi wa mmiliki wake lazima usitishwe. Kwa hivyo katika seva/1 tumeongeza changamoto timer: usingizi (infinity). Hii itazuia thread na kuizuia kumaliza. Utekelezaji huu, bila shaka, ni wa kielimu. Itakuwa nzuri kutoa uwezo wa kusimamisha seva kwa usahihi, lakini hii haiwezekani hapa.

Soketi ya Kubali na uzi wake inaweza kuundwa kwa nguvu kadri wateja wanavyoonekana. Kwanza, unaweza kuunda thread kama hiyo na kupiga simu gen_tcp:kubali/1 na kusubiri mteja. Simu hii inazuiwa. Inaisha wakati mteja anaonekana. Kisha unaweza kumtumikia mteja wa sasa katika thread hii, na kuunda thread mpya kusubiri mteja mpya.

Lakini hapa tuna utekelezaji tofauti. Tunaunda dimbwi la nyuzi kadhaa mapema, na zote zinangojea wateja. Baada ya kumaliza kazi na mteja mmoja, tundu haijafungwa, lakini inasubiri mpya. Kwa hiyo, badala ya kufungua mara kwa mara soketi mpya na kufunga za zamani, tunatumia bwawa la soketi za muda mrefu.

Hii ni nzuri zaidi wakati kuna idadi kubwa ya wateja. Kwanza, kwa sababu tunakubali miunganisho haraka. Pili, kwa sababu ya ukweli kwamba tunasimamia soketi kwa uangalifu zaidi kama rasilimali ya mfumo.

Minyororo ni ya nodi ya Erlang, na tunaweza kuunda nyingi tunavyopenda. Lakini soketi ni za mfumo wa uendeshaji. Idadi yao ni mdogo, ingawa ni kubwa sana. ( Ni kuhusu kuhusu kikomo cha idadi ya maelezo ya faili ambayo mfumo wa uendeshaji inaruhusu kufunguliwa na mchakato wa mtumiaji, kwa kawaida 2 10 - 2 16).

Saizi yetu ya bwawa ni ya ukubwa wa toy - jozi 5 za soketi za mkondo. Kwa kweli, tunahitaji dimbwi la mamia kadhaa ya jozi kama hizo. Itakuwa vyema pia kuweza kuongeza na kupunguza bwawa hili wakati wa kukimbia ili kukabiliana na mzigo wa sasa.

Kipindi cha sasa na mteja kinachakatwa kwenye chaguo la kukokotoa kushughulikia_muunganisho/2. Inaweza kuonekana kuwa tundu iko katika hali ya kazi, na thread inapokea ujumbe kama (tcp, Soketi, Msg), Wapi Msg-- hii ni data ya jozi inayotoka kwa mteja. Tunatuma data hii kwa mteja, yaani, tunatekeleza huduma ya mwangwi banal :)

Wakati mteja anafunga uunganisho, thread inapokea ujumbe (tcp_closed, _Socket), inarudi kwa kukubali/2 na inasubiri mteja anayefuata.

Hivi ndivyo utendakazi wa seva kama hiyo iliyo na wateja wawili wa telnet inaonekana kama:

$ telnet localhost 1234 Inajaribu 127.0.0.1... Imeunganishwa kwa localhost. Herufi ya Escape ni "^]". hujambo kutoka kwa mteja 1 hujambo kutoka kwa mteja 1 ujumbe fulani kutoka kwa mteja 1 ujumbe fulani kutoka kwa mteja 1 ujumbe mpya kutoka kwa mteja 1 ujumbe mpya kutoka kwa mteja 1 mteja 1 atafunga muunganisho mteja 1 atafunga muunganisho ^] telnet> acha Muunganisho umefungwa.

$ telnet localhost 1234 Inajaribu 127.0.0.1... Imeunganishwa kwa localhost. Herufi ya Escape ni "^]". hujambo kutoka kwa mteja 2 hujambo kutoka kwa mteja 2 ujumbe kutoka kwa mteja 2 ujumbe kutoka kwa mteja 2 mteja 2 bado mteja 2 bado anatumika lakini mteja 2 bado anatumika lakini mteja 2 bado anatumika na sasa mteja 2 atafunga muunganisho na sasa mteja 2 itafunga muunganisho ^] telnet> acha Muunganisho umefungwa.

2> seva:anza (). anzisha seva kwenye bandari 1234 sawa Soketi #1 subiri Tundu la mteja #2 subiri Tundu la mteja #3 subiri Tundu la mteja #4 subiri Tundu la mteja #5 subiri Tundu la mteja #1, kipindi kilianza Soketi #1 ilipata ujumbe:<<"hello from client 1\r\n">> Soketi #1 ilipata ujumbe:<<"some message from client 1\r\n">> Tundu #2, kipindi kilianza Soketi #2 ilipata ujumbe:<<"hello from client 2\r\n">> Soketi #2 ilipata ujumbe:<<"message from client 2\r\n">> Soketi #1 ilipata ujumbe:<<"new message from client 1\r\n">> Soketi #2 ilipata ujumbe:<<"client 2 is still active\r\n">> Soketi #1 ilipata ujumbe:<<"client 1 is going to close connection\r\n">> Tundu #1, kipindi kimefungwa Tundu #1 la kusubiri Tundu #2 la mteja lilipata ujumbe:<<"but client 2 is still active\r\n">> Soketi #2 ilipata ujumbe:<<"and now client 2 is going to close connection\r\n">> Tundu #2, kipindi kimefungwa Tundu #2 subiri mteja

Seva katika hali ya passiv

Hii yote ni nzuri, lakini seva nzuri lazima ifanye kazi katika hali ya passiv. Hiyo ni, inapaswa kupokea data kutoka kwa mteja si kwa namna ya ujumbe kwenye sanduku la barua, lakini kwa kupiga simu gen_tcp:recv/2,3.

Nuance ni kwamba hapa tunahitaji kuonyesha ni data ngapi tunataka kusoma. Je, seva inaweza kujua ni data ngapi mteja aliituma? Kweli, inaonekana, mteja mwenyewe lazima aseme ni data ngapi atatuma. Kwa kufanya hivyo, mteja kwanza hutuma pakiti ndogo ya huduma, ambayo inaonyesha ukubwa wa data yake, na kisha kutuma data yenyewe.

Sasa tunahitaji kuamua ni ka ngapi pakiti hii ya huduma inapaswa kuchukua. Ikiwa ni 1 byte, basi huwezi kuingiza nambari kubwa kuliko 255. Unaweza kuingiza nambari 65535 ndani ya ka 2, na 4294967295 katika byte 4. Byte 1 ni wazi haitoshi. Kuna uwezekano kwamba mteja atahitaji kutuma zaidi ya baiti 255 za data. Kichwa cha baiti 2 ni sawa. Kichwa cha baiti 4 wakati mwingine kinahitajika.

Kwa hivyo, mteja hutuma pakiti ya huduma ya 2-byte inayoonyesha ni data ngapi itaifuata, na kisha data yenyewe:

Msg =<<"Hello">>, Ukubwa = byte_size(Msg), Kichwa =<>, gen_tcp:send(Soketi,<

>),

Msimbo kamili wa mteja:

Moduli(mteja2). -uza nje (). start() -> start("localhost", 1234). start(Host, Port) -> spawn(?MODULE, mteja, ). tuma(Pid, Msg) -> Pid ! (tuma, Msg), sawa. stop(PID) -> Pid ! acha, sawa. client(Host, Port) -> io:format("Mteja ~p huunganisha kwa ~p:~p~n", ), (ok, Socket) = gen_tcp:connect(Host, Port, ), loop(Soketi). kitanzi(Soketi) -> pokea (tuma, Msg) -> io:format("Client ~p send ~p~n", ), Size = byte_size(Msg), Header =<>, gen_tcp:send(Soketi,<

>), kitanzi(Soketi); (tcp, Soketi, Msg) -> io:format("Mteja ~p alipata ujumbe: ~p~n", ), kitanzi(Soketi); stop -> io:format("Mteja ~p hufunga muunganisho na kusimamisha~n", ), gen_tcp:close(Soketi) baada ya 200 -> kitanzi(Soketi) mwisho.

Seva kwanza inasoma ka 2, huamua saizi ya data, na kisha inasoma data yote:

(sawa, Kichwa) = gen_tcp:recv(Soketi, 2),<> = Kichwa, (sawa, Msg) = gen_tcp:recv(Soketi, Ukubwa),

Katika msimbo wa seva kazi kuanza/0 Na kuanza/1 haijabadilika, iliyobaki imebadilika kidogo:

Seva(Port) -> io:format("start server at port ~p~n", ), (ok, ListenSocket) = gen_tcp:listen(Port, ), ) || ID<- lists:seq(1, 5)], timer:sleep(infinity), ok. accept(Id, ListenSocket) ->io:format("Soketi #~p wait for client~n", ), (ok, Socket) = gen_tcp:kubali(ListenSocket), io:format("Soketi #~p, session started~n", ), handle_connection (Id, ListenSocket, Soketi). handle_connection(Id, ListenSocket, Socket) -> case gen_tcp:recv(Socket, 2) of (sawa, Header) -><> = Kichwa, (sawa, Msg) = gen_tcp:recv(Soketi, Ukubwa), io:format("Soketi #~p imepata ujumbe: ~p~n", ), gen_tcp:send(Socket, Msg), handle_connection( kitambulisho, ListenSocket, Soketi); (hitilafu, imefungwa) -> io:format("Soketi #~p, kipindi kimefungwa ~n", ), kubali(Id, ListenSocket) mwisho.

Mfano wa kikao kutoka kwa upande wa mteja:

2> Pid = mteja2:anza(). Mteja<0.40.0>inaunganishwa na "localhost":1234<0.40.0>3> mteja2:tuma (Pid,<<"Hello">>). Mteja<0.40.0>tuma<<"Hello">> sawa Mteja<0.40.0>nimepata ujumbe:<<"Hello">> 4> mteja2:tuma(Pid,<<"Hello again">>). Mteja<0.40.0>tuma<<"Hello again">> sawa Mteja<0.40.0>nimepata ujumbe:<<"Hello again">> 5> mteja2:acha(Pid). Mteja<0.40.0>hufunga muunganisho na kuacha sawa

Na kutoka kwa upande wa seva:

2> seva2:anza(). anzisha seva kwenye bandari 1234 sawa Soketi #1 subiri Tundu la mteja #2 subiri Tundu la mteja #3 subiri Tundu la mteja #4 subiri Tundu la mteja #5 subiri Tundu la mteja #1, kipindi kilianza Soketi #1 ilipata ujumbe:<<"Hello">> Soketi #1 ilipata ujumbe:<<"Hello again">> Tundu #1, kipindi kimefungwa Tundu #1 subiri mteja

Hii yote ni sawa na nzuri, lakini hakuna haja ya kushughulikia kifurushi cha kichwa. Hii tayari imetekelezwa katika gen_tcp. Unahitaji kutaja saizi ya pakiti ya huduma katika mipangilio wakati wa kufungua tundu kwenye upande wa mteja:

(sawa, Soketi) = gen_tcp:connect(Host, Port, ),

na kwa upande wa seva:

(sawa, ListenSocket) = gen_tcp:listen(Port, ),

na hitaji la kuunda na kuchambua vichwa hivi mwenyewe hutoweka.

Kwa upande wa mteja, kutuma ni rahisi:

Gen_tcp:tuma (Soketi, Msg),

na kwa upande wa seva hurahisisha kupata:

Handle_connection(Id, ListenSocket, Socket) -> case gen_tcp:recv(Socket, 0) of (ok, Msg) -> io:format("Soketi #~p got message: ~p~n", ), gen_tcp:tuma (Soketi, Msg), handle_connection(Id, ListenSocket, Socket); (hitilafu, imefungwa) -> io:format("Soketi #~p, kipindi kimefungwa ~n", ), kubali(Id, ListenSocket) mwisho.

Sasa wakati wa kupiga simu gen_tcp:recv/2 tunabainisha Urefu = 0. gen_tcp inajua ni ka ngapi zinahitaji kusomwa kutoka kwa tundu.

Kufanya kazi na itifaki za maandishi

Mbali na chaguo na kichwa cha huduma, kuna mbinu nyingine. Unaweza kusoma kutoka kwa tundu byte moja kwa wakati hadi byte maalum inakabiliwa, inayoashiria mwisho wa pakiti. Hii inaweza kuwa baiti tupu, au herufi mpya.

Chaguo hili ni la kawaida kwa itifaki za maandishi (SMTP, POP3, FTP).

Hakuna haja ya kuandika utekelezaji wako mwenyewe wa kusoma kutoka kwa tundu, kila kitu tayari kinatekelezwa ndani gen_tcp. Unahitaji tu kutaja katika mipangilio ya tundu badala yake (pakiti, 2) chaguo (kifurushi, mstari).

(sawa, ListenSocket) = gen_tcp:listen(Port, ),

Vinginevyo, msimbo wa seva unabaki bila kubadilika. Lakini sasa tunaweza kurudi kwa mteja wa telnet tena.

$ telnet localhost 1234 Inajaribu 127.0.0.1... Imeunganishwa kwa localhost. Herufi ya Escape ni "^]". hujambo hujambo tena hujambo tena ^] telnet> acha Muunganisho umefungwa.

Tutahitaji seva ya TCP, itifaki ya maandishi na mteja wa telnet katika kazi yetu ya kozi.

Soketi dhidi ya Soketi sehemu ya 2, au sema "hapana" kwa itifaki ya TCP - Hifadhi WASM.RU kwenye kumbukumbu

Katika sehemu ya kwanza, iliyojitolea kwa misingi ya kutumia soketi za MSWindows katika programu za kusanyiko, tulizungumza juu ya soketi ni nini, jinsi zinaundwa, na ni vigezo gani vilivyoainishwa. Wakati huo huo, ilitajwa katika kupitisha itifaki ya UDP isiyo na uhusiano, ambayo haihakikishi utoaji wa pakiti, pamoja na utaratibu ambao wanafika kwenye marudio yao. Mfano wa mafunzo kisha ulitumia itifaki yetu tuipendayo ya TCP. Na kila kitu kilikuwa sawa na sisi, lakini mwisho kulikuwa na maswali kadhaa ambayo hayajatatuliwa, haswa, jinsi ya kupanga kubadilishana kati ya kompyuta kadhaa kwenye mtandao, jinsi ya kuhamisha kitu kwa kompyuta nyingi mara moja, nk.

Kwa ujumla, kusoma sehemu ya kwanza sio lazima kabisa kuelewa ya sasa, ingawa nitairejelea kila wakati njiani. Hivyo huenda. Haha...

Kwa hivyo, tunaleta shida: tuna mtandao wa ndani wa, sema, kompyuta kadhaa, tunahitaji kupanga ubadilishanaji wa ujumbe kati ya hizo mbili, na (hiari) kati ya moja na zingine zote.

Nasikia, nasikia kwaya ya maongozi yanayosema, tumia vipengee vilivyojumuishwa vya Windows, kama vile:

wavu tuma 192.168.0.4 Zhenya anatuma salamu kwako!

net tuma Node4 Inasubiri jibu lako!

Kuna pingamizi mbili tu kwa hili. Kwanza, huwezi kujua nini mfumo wetu wa uendeshaji au wengine wanaweza kufanya mipango tayari, tunataka kujifunza jinsi ya kuandika programu zetu wenyewe, sivyo? Na pili, sio ukweli kwamba ujumbe unatoka mtu hadi mtu. Kwa ujumla, mwendeshaji anaweza hajui chochote ... Au hata haipaswi kujua chochote ...

Kwa mimi, jambo muhimu zaidi katika kuweka kazi hii ilikuwa kuhakikisha uwezo wa kuhamisha kitu kwenye kompyuta zote kwenye mtandao mara moja. Hebu fikiria kwamba tuliandika programu fulani ... Nani alisema - Trojan? Hapana, hapana na HAPANA! Hakuna Trojans. Programu ndogo tu (sana) ya uhasibu, kwa mfano. Ambayo baada ya muda iliweza kukaa kwenye kompyuta nyingi kwenye mtandao wetu wa ndani. Na sasa wakati uliowekwa unakuja, ni wakati wa kusawazisha usawa, kwa muhtasari, kwa kusema, matokeo ya robo ... Kila kitu lazima kifanyike haraka na ikiwezekana kwa wakati mmoja. Jinsi ya kufanya hivyo ndani ya mfumo wa nyenzo ambazo tulisoma katika sehemu ya kwanza ilibaki wazi.

Jibu, kama kawaida, linatoka kwa WindowsAPI. Tunatafuta na kupata. Kazi tuma kwa() – hutuma data kwa anwani maalum. Ni nini basi tofauti yake kutoka kwa kazi iliyosomwa tayari katika sehemu ya kwanza? tuma() ? Inageuka kuwa tuma kwa() inaweza kutangaza kwa anwani maalum ya IP. Lakini, tafadhali kumbuka, hii inafanya kazi tu kwa soketi za aina ya SOCK_DGRAM! Na soketi ambazo zilifunguliwa kwa kutumia thamani ya SOCK_DGRAM kama kigezo cha aina ya tundu hufanya kazi kupitia itifaki ya UDP, si TCP! Hii inaweka wazi maana ya kichwa kidogo cha makala hii ... Bila shaka, hii ni kifaa cha fasihi tu, hakuna itifaki iliyo bora au mbaya zaidi kuliko nyingine, wao ni tu ... tofauti, hivyo tu. Ingawa zote mbili ni itifaki safu ya usafiri, ambayo "...hutoa uhamishaji wa data kati ya michakato ya programu." Zote mbili hutumia itifaki ya safu ya mtandao kama vile IP kusambaza (kupokea) data. Kwa njia ambayo wao (data) kisha wanafika safu ya kimwili, i.e. juu ya maambukizi Jumatano ... Na ni aina gani ya Jumatano ni, ni nani anayejua. Labda ni cable ya shaba, au labda sio Jumatano kabisa, lakini Alhamisi, na si cable ya shaba, lakini matangazo ...

Mpango wa mwingiliano wa itifaki za mtandao.

UDPU ser D atagram P itifaki

TCP-T kutolewa C kudhibiti P itifaki

ICMP-I mtandao C kudhibiti M insha P rotokoli (itifaki ya udhibiti wa kubadilishana ujumbe)

ARPA anwani R suluhisho P rotokoli (itifaki ya ugunduzi wa anwani)

Kwa ujumla, ikiwa kuchora hakukusaidia kwa njia yoyote, haijalishi. Ni muhimu kuelewa jambo moja kwamba TCP ni itifaki ya safu ya usafiri ambayo hutoa kuaminika kusafirisha data kati ya michakato ya maombi kwa kuanzisha muunganisho wa kimantiki (msisitizo wangu). Lakini UDP sio. Na zaidi. Mahali fulani pale, juu kiwango cha maombi, programu yetu itapatikana katika mojawapo ya mistatili tupu.

Tumalizie sehemu ya utangulizi hapa na tuendelee kuangalia jinsi ya kuitumia tangu mwanzo.

Ili kuonyesha nyenzo zote, kama kawaida, mfano wa mafunzo hutumiwa, ambao unaweza kupakuliwa< >. Tunaruka ile ya kawaida kwa kila mtu Programu za Windows sehemu na ueleze tu kile kinachohusu uendeshaji wa soketi. Kwanza unahitaji kuanzisha Windows Soketi DLL kwa kutumia kazi WSAStartup() , ambayo itarudisha sifuri ikiwa imefanikiwa, au, vinginevyo, mojawapo ya misimbo ya makosa. Kisha, unapoanzisha dirisha kuu la programu, fungua tundu ili kupokea ujumbe:

    omba soketi, AF_INET, \

    SOCK_DGRAM, \ ; inabainisha aina ya tundu - itifaki ya UDP!

    0; aina ya itifaki

    Ikiwa eax != INVALID_SOCKET ; ikiwa hakuna makosa

    mov hSocket, eax ; kumbuka kushughulikia

Baada ya hayo, kama kawaida, tunahitaji kuwaambia Windows kutuma ujumbe kwa dirisha maalum kutoka kwa tundu tulilofungua:

    omba WSAAsyncSelect, hSocket, hWnd, WM_SOCKET, FD_READ

Wapi hSoketi- maelezo ya tundu
hWnd- kushughulikia kwa dirisha ambalo ujumbe wa utaratibu utatumwa
WM_SOCKET- ujumbe, unaofafanuliwa na sisi katika section.const
FD_SOMA- mask ambayo inabainisha matukio ya kupendeza kwetu, katika kesi hii ni utayari wa data kutoka kwa tundu la kusoma.

Ninasikia, nasikia chorus iliyoshangaa na kukata tamaa kwa sauti yao: waliahidi maombi yaliyofichwa, lakini hapa ni dirisha kuu na yote ... Ukweli ni kwamba huwezi kufanya bila hiyo, kwa sababu ... Mfumo wa uendeshaji hutuma ujumbe wote kwa programu yetu kupitia utaratibu wake wa dirisha. Suluhisho ni rahisi. Ikiwa ni lazima, ficha dirisha hili la programu muhimu zaidi. Vipi? Kwa mfano, toa maoni kwenye mstari:

    omba ShowWindow, hwnd, SW_SHOWNORMAL

au, kwa usahihi zaidi, tumia:

    omba ShowWindow, hwnd, SW_HIDE

Baada ya hayo, programu yetu pia itaanza, dirisha kuu litaundwa, ujumbe wa WM_CREATE utatumwa kwake kutoka kwa Windows na matokeo yote ... Dirisha lake pekee halitaonekana ama kwenye desktop au kwenye barani ya kazi. Ikiwa hii ndio ulitaka, nimefurahi. Anyway, tuendelee...

Ili kufanya hivyo, badilisha nambari ya bandari hadi utaratibu wa mtandao byte kwa kutumia kazi maalum ya API:

    omba htons, Bandari

    mov sin.sin_bandari, shoka

    mov sin.sin_family, AF_INET

    mov sin.sin_addr, INADDR_ANY

Kicheko kidogo cha sauti, sio lazima kuelewa maana ya nakala hii .

Nambari za bandari za soketi zetu zilijadiliwa mwishoni mwa sehemu ya kwanza. Ni vigumu kutoa mapendekezo ya nini wanapaswa kuwa. Kitu pekee ambacho kinaweza kusemwa ni kwamba hawawezi kuwa. Sio busara kujaribu kutumia nambari za bandari zilizofafanuliwa kwa huduma zinazotumiwa sana kama vile:

kupitia itifaki TCP: 20, 21 - ftp; 23 - telnet; 25 - smtp; 80 - http; 139 - huduma ya kikao cha NetBIOS;

kupitia itifaki UDP: 53 - DNS; 137, 138 - NetBIOS; 161 - SNMP;

Bila shaka, API ina kazi maalum getservbyport() , ambayo, ikipewa nambari ya bandari, inarudisha jina la huduma inayolingana. Kwa usahihi zaidi, kazi yenyewe inarudisha pointer kwa muundo, ambao ndani yake kuna pointer kwa jina hili ...

Unaweza kuiita kama hii:

    omba htons, Bandari; badilisha nambari ya bandari kuwa mpangilio wa baiti ya mtandao

    omba getservbyport, shoka, 0;

Kumbuka kile Marejeleo ya Win32 Programmer inasema kuhusu getservbyport:

“...hurejesha kielekezi kwenye muundo ambao unasambazwa na Soketi za Windows. Programu haipaswi kamwe kujaribu kurekebisha muundo huu au sehemu zake zozote. Zaidi ya hayo, nakala moja tu ya muundo huu imetengwamtiririko, kwa hivyo ni lazima programu inakili taarifa yoyote inayohitaji kabla ya simu nyingine yoyote kwa kitendakazi cha Soketi za Windows."

Na hapa kuna muundo yenyewe:

  1. s_jina DWORD ?; pointer kwa kamba yenye jina la huduma

    s_aliases DWORD ?;

    s_port NENO ?; nambari ya bandari

    s_proto DWORD ?;

API pia ina kazi ya "paired", kwa kusema: getservbyname(), ambayo, kulingana na jina la huduma, inarudi taarifa kuhusu nambari ya bandari iliyotumiwa.

Kwa bahati mbaya, hatutaweza kupata manufaa ya vitendo kutoka kwa vipengele hivi. Kwa hivyo, jua kuwa zipo na usahau juu yao ...

    omba funga, hSocket, addr sin, size ya dhambi

    Ikiwa eax == SOCKET_ERROR; ikiwa kuna hitilafu

    omba MessageBox, NULL, ongeza ...

Katika hatua hii, kazi ya maandalizi ya kuunda na kusanidi tundu la kupokea kwa kutumia datagrams inaweza kuchukuliwa kuwa kamili. Hakuna haja ya kuweka tundu ili kusikiliza kwenye bandari kwa kutumia kipengele cha omba sikiliza, kama tulivyofanya kwa soketi ya aina SOCK_STREAM katika sehemu ya kwanza. Sasa katika utaratibu wa dirisha kuu la programu yetu tunaweza kuongeza nambari ambayo itatekelezwa wakati ujumbe wa WM_SOCKET utakapofika kutoka kwa soketi:

    ; ikiwa ujumbe umepokelewa kutoka kwa soketi (hSocket)

    Elseif uMsg == WM_SOCKET

  1. Ikiwa shoka == FD_READ;

  2. Ikiwa shoka == NULL ; hakuna kosa

    ; pokea data (baiti 64) kutoka kwa tundu kwenye bafa ya BytRecu

    omba recv, hSocket, addr BytRecu, 64, 0;

Sasa hebu tuzungumze kuhusu jinsi ya kufungua tundu kwa kutuma ujumbe. Hapa kuna vitendo vyote muhimu vya programu:

    omba soketi, AF_INET, SOCK_DGRAM, 0

      omba htons, Bandari

      mov sin_to.sin_port, shoka

      mov sin_to.sin_family, AF_INET

      omba inet_addr, addr AddressIP

      mov sin_to.sin_addr, eax

    Linapokuja suala la kuhamisha data, unachohitaji kufanya ni:

      omba sendto, hSocket1, addr BytSend1, 64, 0, \

      addr sin_to, sizeof sin_to

    Thamani za parameta wakati wa kupiga kazi hii ya API ni kama ifuatavyo.

    hSoketi1- kushughulikia kwa tundu lililofunguliwa hapo awali
    addrBytSend1- anwani ya bafa iliyo na data ya usambazaji
    64 - ukubwa wa data katika bafa, katika ka
    0 - kiashiria..., katika mfano wa MSDN ni 0 tu
    addrsin_to- pointer kwa muundo ambao una anwani lengwa
    sizeofsin_to- ukubwa wa muundo huu katika ka.

    Ikiwa, wakati wa kutekeleza kitendakazi tuma kwa() Ikiwa hakuna makosa yaliyotokea, basi hurejesha idadi ya baiti zilizohamishwa, vinginevyo pato ni SOCKET_ERROR katika eax.

    Sasa ni wakati wa kuzungumza juu ya anwani ile ile ya matangazo ambayo ilitajwa mwanzoni. Katika muundo tulijaza awali shamba na anwani ya IP lengwa, ikionyesha wapi, kwa kweli, kutuma data. Ikiwa anwani hii ni 127.0.0.1 - kwa kawaida, hakuna mahali popote zaidi kompyuta mwenyewe data zetu hazitapotea. Maandishi yanasema wazi kwamba pakiti iliyotumwa kwa mtandao yenye anwani 127.x.x.x haitatumwa kwenye mtandao wowote. Zaidi ya hayo, kipanga njia au lango haipaswi kamwe kueneza habari ya uelekezaji kwa nambari ya mtandao 127 - anwani hii sio anwani ya mtandao. Ili kutuma "usambazaji" kwa kompyuta zote kwenye mtandao wa ndani mara moja, unahitaji kutumia anwani iliyoundwa kutoka kwa anwani yetu ya IP, lakini kwa zote zilizo katika octet ya chini, kitu kama 192.168.0.255.

    Hiyo ndiyo yote, kwa kweli. Wakati programu inafunga, unahitaji kufunga soketi na kutolewa rasilimali za Soketi za DLL; hii inafanywa kwa urahisi:

      omba closesocket, hSocket

      omba closesocket, hSocket1

      omba WSACleanup

    Kwa programu zenye nyuzi nyingi baada ya WSACleanup shughuli za soketi zimekamilika kwa nyuzi zote.

    Sehemu ngumu zaidi ya nakala hii kwangu ilikuwa kuamua jinsi bora ya kuonyesha utumiaji wa API ya Soketi za Windows. Labda tayari umeona mbinu moja, ukiwa ndani maombi moja soketi zote mbili za kupokea na tundu la kutuma ujumbe zilitumika kwa wakati mmoja. Njia nyingine inaonekana sio ya kuvutia sana, wakati nambari ya moja na nyingine imetenganishwa wazi, hadi na kujumuisha kile kilichopo ndani. maombi tofauti. Mwishoni, pia nilitekeleza njia hii, ambayo inaweza kuwa rahisi kidogo kwa Kompyuta kuelewa. Katika pili<архиве

    Bila kazi hii tuma () itazalisha SOCKET_ERROR!

    Hatimaye, tunaweza kutambua matatizo ya kawaida yanayotokea wakati wa kufanya kazi na soketi. Ili kushughulikia ujumbe wa dirisha unaoonyesha kuwa hali ya tundu imebadilika, tulitumia ujumbe wa moja kwa moja kutoka Windows hadi dirisha kuu la programu kama kawaida. Kuna mbinu nyingine wakati wa kuunda madirisha tofauti kwa kila tundu.

    Kwa ujumla, usindikaji wa ujumbe wa kati na dirisha kuu inaonekana kama njia rahisi kuelewa, lakini bado inaweza kuwa shida katika mazoezi. Ikiwa programu inatumia soketi zaidi ya moja kwa wakati mmoja, inahitaji kuhifadhi orodha ya maelezo ya soketi. Wakati ujumbe kutoka kwa soketi unaonekana, utaratibu wa dirisha kuu katika orodha hutafuta taarifa zinazohusiana na maelezo ya soketi na kutuma ujumbe wa mabadiliko ya hali zaidi kwa utaratibu uliokusudiwa kwa hili. Ambayo tayari humenyuka kwa njia moja au nyingine, hufanya kitu pale ... Mbinu hii inalazimisha usindikaji wa kazi za mtandao kuunganishwa kwenye msingi wa programu, ambayo inafanya kuwa vigumu kuunda maktaba ya kazi za mtandao. Kila mara vipengele hivi vya utendakazi vya mtandao vinapotumika, msimbo wa ziada lazima uongezwe kwa kidhibiti kikuu cha dirisha la programu.

    Katika njia ya pili ya usindikaji wa ujumbe, programu inaunda dirisha lililofichwa ili kuzipokea. Inatumika kutenganisha utaratibu wa dirisha kuu la programu kutoka kwa usindikaji wa ujumbe wa mtandao. Mbinu hii inaweza kurahisisha programu kuu na kurahisisha kutumia msimbo uliopo wa mtandao katika programu zingine. Upande mbaya wa njia hii ni matumizi mengi ya Windows - kumbukumbu ya mtumiaji, kwa sababu Kwa kila dirisha iliyoundwa, kiasi kikubwa kinahifadhiwa.

    Njia ipi ya kuchagua ni juu yako. Kitu kimoja zaidi. Wakati wa kujaribu, unaweza kuhitaji kuzima ngome yako ya kibinafsi. Kwa mfano, Outpost Pro 2.1.275 katika hali ya kujifunza ilijibu jaribio la kuhamisha kwenye tundu, lakini wakati uhamisho uliporuhusiwa kwa mikono, data bado haikufika. Sana kwa UDP. Ingawa hii inaweza kuwa sio hivyo. Hakukuwa na shida na ZoneAlarmPro 5.0.590 yangu katika hali sawa.

    P.S. Wakati nikimaliza sehemu ya pili ya kifungu hicho, kwa bahati mbaya nilipata msimbo wa chanzo wa Trojan kwenye Mtandao katika lugha yetu tunayopenda ya MASM. Kila kitu kinajumuisha na kukimbia, jambo moja ni kwamba mteja hataki kuunganisha kwenye seva, na hata chini ya Windows 2000 sp4 wakati mwingine huanguka na kosa, akisema kwamba programu itafungwa na yote ... kama vile Trojan hii ni kwamba programu haiweki tu kumbukumbu ya kubofya , au "kunasua" faili iliyo na manenosiri na kuituma kwa barua pepe, na ina anuwai ya vitendaji vinavyodhibitiwa kwa mbali, vinavyotekelezwa kwa njia asili kabisa. Ikiwa tunasimamia kuleta biashara hii yote kwa maisha, basi labda sehemu ya tatu itaonekana hivi karibuni, inayotolewa kwa maelezo ya utekelezaji maalum ... Kwa wale ambao wamesoma kwa makini makala zote mbili na kuelewa uendeshaji wa kazi za API za tundu, kuna. hakuna kitu kigumu hapo. Inaonekana ... Kwa njia, mwandishi mwenyewe anaandika katika readme kwamba aliandika (Trojan) kwa madhumuni ya elimu. Oh vizuri. Tutatumia hii.

    DirectOr

Ilisasishwa mwisho: 10/31/2015

Itifaki ya UDP haihitaji muunganisho wa kudumu, na watu wengi wanaweza kuona ni rahisi kufanya kazi na UDP kuliko kwa TCP. Kanuni nyingi wakati wa kufanya kazi na UDP ni sawa na TCP.

Kwanza, tundu linaundwa:

Soketi ya soketi = Soketi mpya(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

Ikiwa tundu lazima lipokee ujumbe, basi unahitaji kuifunga kwa anwani ya karibu na moja ya bandari kwa kutumia njia ya Bind:

IPEndPoint localIP = IPEndPoint mpya(IPAddress.Parse("127.0.0.1"), 5555); tundu.Funga(localIP);

Baada ya hayo, unaweza kutuma na kupokea ujumbe. Ili kupokea ujumbe, tumia njia ya ReceiveFrom():

Data ya Byte = byte mpya; // bafa ya data iliyopokelewa //anwani ambayo data ilitoka EndPoint remoteIp = IPEndPoint mpya(IPAddress.Any, 0); int bytes = socket.ReceiveFrom(data, ref remoteIp);

Kama kigezo, njia hupitishwa safu ya baiti ambayo data inapaswa kusomwa, na sehemu ya mbali ambayo data hii inatoka. Njia inarudisha idadi ya baiti zilizosomwa.

Ili kutuma data, tumia njia ya SendTo():

Ujumbe wa kamba = Console.ReadLine (); data byte = Encoding.Unicode.GetBytes(ujumbe); EndPoint remotePoint = IPEndPoint mpya(IPAddress.Parse("127.0.0.1"), remotePort); kusikilizaSocket.TumaKwa(data, remotePoint);

Safu ya data ya kutumwa, pamoja na anwani ambayo data hii inapaswa kutumwa, hupitishwa kwa mbinu.

Wacha tuunde mpango wa mteja wa UDP:

Kutumia Mfumo; kwa kutumia System.Text; kwa kutumia System.Threading.Tasks; kutumia System.Net; kwa kutumia System.Net.Sockets; nafasi ya jina SocketUdpClient ( Mpango wa darasa ( static int localPort; // port kwa ajili ya kupokea ujumbe tuli int remotePort; // bandari ya kutuma ujumbe tuli Socket listeningSocket; static void Main(string args) ( Console.Write("Ingiza mlango wa kupokea ujumbe: ") ; localPort = Int32.Parse(Console.ReadLine()); Console.Andika("Ingiza lango ili kutuma ujumbe: "); remotePort = Int32.Parse(Console.ReadLine()); Console.WriteLine("Kwa tuma ujumbe, ingiza ujumbe na ubonyeze Enter"); Console.WriteLine(); jaribu ( listeningSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); Task listeningTask = Task mpya(Sikiliza); ListenTask.Start( ); // kutuma ujumbe kwa bandari mbalimbali huku (kweli) (  string message = Console.ReadLine(); data byte = Encoding.Unicode.GetBytes(ujumbe); EndPoint remotePoint = IPEndPoint mpya(IPAddress.Parse("127.0). 0.1"), remotePort); listeningSocket.SendTo(data, remotePoint); ) ) pata (Isiofuata kanuni) ( Console.WriteLine(ex.Message); ) hatimaye ( Funga(); ) ) // uzi wa kukubali miunganisho ya utupu ya kibinafsi Sikiliza() ( jaribu ( //Sikiliza katika anwani ya IPEndPoint localIP = IPEndPoint mpya(IPAddress.Parse("127.0.0.1")), localPort); listeningSocket .Bind(localIP); wakati (kweli) (// pokea ujumbe StringBuilder builder = new StringBuilder(); int byte = 0; // idadi ya baiti zilizopokelewa data ya byte = byte mpya; // buffer kwa data iliyopokelewa / /anwani, s ambayo data ilikuja EndPoint remoteIp = IPEndPoint mpya(IPAddress.Any, 0); fanya ( bytes = listeningSocket.ReceiveFrom(data, ref remoteIp); builder.Append(Encoding.Unicode.GetString(data, 0, bytes) ); ) wakati (kusikilizaSocket.Inapatikana > 0); // pata maelezo ya muunganisho IPEndPoint remoteFullIp = remoteIp kama IPEndPoint; // chapisha ujumbe Console.WriteLine("(0):(1) - (2)", remoteFullIp.Anwani .ToString() , remoteFullIp.Port, builder.ToString()); ) ) kamata (Ila isipokuwa) ( Console.WriteLine(ex.Message); ) hatimaye ( Close(); ) ) // kufunga tundu la faragha tuli Funga() ( ikiwa (listeningSocket != null) ( listeningSocket.Shutdown(SocketShutdown.Both); kusikilizaSoketi.Funga(); listeningSocket = null; )))

Kwanza, mtumiaji huingia kwenye bandari kwa kupokea data na kutuma. Inachukuliwa kuwa programu mbili za mteja ambazo zitaingiliana zinaendesha kwenye mashine moja ya ndani. Ikiwa anwani za wateja ni tofauti, unaweza pia kutoa kwa ajili ya kuweka anwani ya kutuma data.

Baada ya kuingia kwenye bandari, kazi ya kusikiliza ujumbe unaoingia imezinduliwa. Tofauti na seva ya tcp, hakuna haja ya kupiga njia za Sikiliza na Kubali. KATIKA kitanzi kisicho na mwisho tunaweza kupokea data moja kwa moja kwa kutumia njia ya ReceiveFrom(), ambayo huzuia uzi wa kupiga simu hadi data inayofuata ifike.

Njia hii inarudi, kupitia kigezo cha ref, sehemu ya mbali ambayo data ilipokelewa:

IPEndPoint remoteFullIp = remoteIp kama IPEndPoint;

Hiyo ni, licha ya ukweli kwamba katika kesi hii mapokezi na utumaji wa ujumbe hutofautishwa na mteja wa sasa hutuma data tu kwenye bandari iliyoingia hapo awali, tunaweza kuongeza kwa urahisi uwezo wa kujibu ujumbe kwa kutumia data ya hatua ya mbali iliyopokelewa ( anwani na bandari).

Uzi mkuu hutuma ujumbe kwa kutumia njia ya SendTo().

Kwa hivyo, programu hufanya mara moja kazi za seva na mteja.

Sasa hebu tuzindue nakala mbili za programu na tuingize data tofauti kwa bandari. Mteja wa kwanza:

Ingiza mlango wa kupokea ujumbe: 4004 Ingiza bandari ya kutuma ujumbe: 4005 Kutuma ujumbe, ingiza ujumbe na ubonyeze Ingiza 127.0.0.1:4005 - hujambo bandari 4004 mchana mzuri, bandari 4005 hali ya hewa nzuri.

Mteja wa pili:

Ingiza mlango wa kupokea ujumbe: 4005 Ingiza bandari ya kutuma ujumbe: 4004 Ili kutuma ujumbe, ingiza ujumbe na ubonyeze Ingiza hello port 4004 127.0.0.1:4004 - habari za mchana, bandari 4005 127.0.0.1:4004 - hali ya hewa nzuri

35 majibu

Muhtasari

Soketi ya TCP ni mfano wa mwisho, iliyofafanuliwa na anwani ya IP na mlango katika muktadha wa muunganisho maalum wa TCP au hali ya kusikiliza.

Mlango ndio kitambulisho cha uboreshaji, ambayo hubainisha mwisho wa huduma (kinyume na mwisho wa mfano wa huduma au kitambulisho cha kipindi chake).

Soketi ya TCP sio muunganisho, huu ndio mwisho wa muunganisho maalum.

Kunaweza kuwa na miunganisho ya wakati mmoja kwenye sehemu ya mwisho ya huduma, kwa kuwa muunganisho unatambuliwa na ncha za ndani na za mbali, kuruhusu trafiki kuelekezwa kwa mfano maalum wa huduma.

Kunaweza kuwa na soketi moja tu ya msikilizaji kwa anwani fulani na mchanganyiko wa mlango.

Maonyesho

Hili lilikuwa swali la kufurahisha ambalo lilinifanya nifikirie tena baadhi ya mambo ambayo nilifikiri nilijua kwa ndani. Ungefikiria jina kama "tundu" litapewa: inaonekana limechaguliwa ili kuibua picha za sehemu ya mwisho unayounganisha. cable mtandao, kuna ulinganifu mkubwa wa utendaji hapo. Hata hivyo, katika lugha ya mitandao, neno "tundu" hubeba mizigo mingi sana kwamba uchunguzi upya wa makini ni muhimu.

Kwa maana pana, bandari ni mahali pa kuingia au kutoka. Neno la Kifaransa porte, ingawa halitumiki katika muktadha wa mtandao, kwa kweli linamaanisha mlango au lango, na kusisitiza zaidi ukweli kwamba bandari ni sehemu za mwisho za usafiri, iwe unatuma data au vyombo vikubwa vya chuma.

Kwa madhumuni ya mjadala huu, nitapunguza uzingatiaji wa muktadha mitandao ya TCP-IP. Mfano wa OSI ni mzuri sana, lakini haujawahi kutekelezwa kikamilifu, chini sana kupelekwa katika voltage ya juu, mazingira ya juu ya trafiki.

Mchanganyiko wa anwani ya IP na bandari inajulikana sana kama sehemu ya mwisho na wakati mwingine huitwa tundu. Matumizi haya yanahusiana na RFC793, vipimo asili vya TCP.

Muunganisho wa TCP unafafanuliwa na ncha mbili aka soketi.

Sehemu ya mwisho (tundu) imedhamiriwa na mchanganyiko wa anwani ya mtandao na kitambulisho cha bandari. Kumbuka kuwa anwani/bandari haitambulishi kikamilifu tundu (zaidi kuhusu hili baadaye).

Madhumuni ya bandari ni kutofautisha kati ya ncha nyingi kwenye anwani fulani ya mtandao. Tunaweza kusema kwamba bandari ni sehemu ya mwisho iliyoboreshwa. Uboreshaji huu hufanya miunganisho mingi ya wakati mmoja iwezekanavyo kwenye kiolesura kimoja cha mtandao.

Hii ni jozi ya soketi (tuple 4 inayojumuisha anwani ya IP ya mteja, nambari ya bandari ya mteja, anwani ya IP ya seva, na nambari ya bandari ya seva) ambayo inabainisha ncha mbili ambazo zinatambulisha kwa njia ya kipekee kila muunganisho wa TCP kwenye Mtandao. (TCP-IP Illustrated Volume 1, W. Richard Stevens)

Katika lugha nyingi zenye msingi wa C, miunganisho ya TCP huanzishwa na kushughulikiwa kwa kutumia mbinu kwenye mfano wa darasa la Soketi. Ingawa ni kawaida kufanya kazi kwa zaidi ngazi ya juu uondoaji, kawaida mfano wa darasa la NetworkStream, kawaida hutoa rejeleo la kitu cha tundu. Kwa kisimbaji, kitu hiki cha tundu kinaonekana kuwakilisha muunganisho kwa sababu muunganisho umeundwa na kudhibitiwa kwa kutumia mbinu za kitu cha tundu.

Katika C#, ili kuanzisha muunganisho wa TCP (kwa msikilizaji aliyepo), kwanza unaunda TcpClient. Ikiwa hutabainisha mwisho wa mjenzi wa TcpClient, hutumia maadili chaguo-msingi - kwa njia yoyote ile sehemu ya mwisho ya ndani imedhamiriwa. Kisha unaita njia ya Unganisha kwenye mfano ulioundwa. Njia hii inahitaji parameta inayoelezea mwisho mwingine.

Hii yote inachanganya kidogo na inakuongoza kuamini kuwa tundu ni kiunganisho ambacho ni kufuli. Nilikuwa nikifanya kazi chini ya kutokuelewana huku hadi Richard Dorman alipouliza swali.

Baada ya kusoma na kufikiria sana, sasa nina hakika kuwa itakuwa na maana zaidi kuwa na darasa la TcpConnection na mjenzi ambaye huchukua hoja mbili: LocalEndpoint na RemoteEndpoint. Labda unaweza kuunga mkono hoja moja ya RemoteEndpoint wakati maadili chaguo-msingi ya mwisho wa ndani yanakubalika. Hili ni gumu kwenye kompyuta za msingi nyingi, lakini utata unaweza kutatuliwa kwa kutumia jedwali la uelekezaji kwa kuchagua kiolesura chenye njia fupi zaidi ya kufikia mwisho wa mbali.

Uwazi utaimarishwa kwa njia zingine pia. Soketi haijatambuliwa na mchanganyiko wa anwani ya IP na bandari:

[...] TCP hutenganisha sehemu zinazoingia kwa kutumia thamani zote nne zilizo na anwani za ndani na nje ya nchi: anwani ya IP lengwa, nambari ya bandari lengwa, anwani ya IP ya chanzo, na nambari ya kituo cha chanzo. TCP haiwezi kubainisha ni mchakato gani unapokea sehemu inayoingia kwa kuangalia tu lango lengwa. Zaidi ya hayo, ncha moja pekee kati ya [mbalimbali] katika [ nambari hii port], ambayo itakubali maombi yanayoingia ya muunganisho, ndiyo iliyo katika hali ya kusikiliza. (p255, TCP-IP Illustrated Volume 1, W. Richard Stevens)

Kama unavyoona, haiwezekani tu, lakini kuna uwezekano, kwamba huduma ya mtandao ina soketi nyingi zilizo na anwani / bandari sawa, lakini tundu moja tu la msikilizaji kwenye anwani fulani / mchanganyiko wa bandari. Utekelezaji wa kawaida wa maktaba ni darasa la soketi, mfano ambao hutumiwa kuunda na kudhibiti muunganisho. Hii ni bahati mbaya sana kwani inaleta mkanganyiko na imesababisha mkanganyiko mkubwa wa dhana hizo mbili.

Khagrawal haniamini (tazama maoni), kwa hivyo hapa kuna sampuli halisi. Niliunganisha kivinjari cha wavuti kwa http://dilbert.com na kisha kukimbia netstat -an -p tcp . Laini sita za mwisho za matokeo zina mifano miwili ya jinsi anwani na mlango hazitoshi kutambua tundu kwa njia ya kipekee. Kuna miunganisho miwili tofauti kati ya 192.168.1.3 (kituo changu cha kazi) na 54.252.92.236:80

TCP 192.168.1.3:63240 54.252.94.236:80 SYN_SENT TCP 192.168.1.3:63241 54.252.94.236:80 SYN_SENT TCP 192.36:1608.168:168. SYN_SENT TCP 192.168.1.3:63243 207.38.110.62:80 SYN_SENT TCP 192.168 .1.3:64161 65.54.225.168:443 IMEANZISHWA

Kwa kuwa tundu ni hatua ya mwisho ya uunganisho, kuna soketi mbili na mchanganyiko wa anwani / bandari 207.38.110.62:80 na mbili zaidi na mchanganyiko wa anwani / bandari 54.252.94.236:80 .

Nadhani kutoelewana kwa Khagrawal kunatokana na matumizi yangu ya uangalifu sana ya neno "inabainisha". Ninamaanisha "tambua kabisa, kipekee na ya kipekee". Katika mfano hapo juu, kuna ncha mbili na mchanganyiko wa anwani / bandari 54.252.94.236:80. Ikiwa una anwani na mlango, huna maelezo ya kutosha kutenganisha viunganishi hivyo. Hakuna maelezo ya kutosha kutambua tundu.

Nyongeza

Aya ya pili ya sehemu ya 2.7 ya RFC793 inasema:

Uunganisho unafafanuliwa kabisa na jozi ya soketi kwenye ncha. Soketi ya ndani inaweza kushiriki katika viunganisho vingi na soketi mbalimbali za kigeni.

Ufafanuzi huu wa tundu sio muhimu kutoka kwa mtazamo wa programu kwa sababu sio sawa na kitu cha tundu, ambacho ni mwisho wa muunganisho maalum. Kwa mtayarishaji programu, na wengi wa watazamaji hawa ni waandaaji programu, hii ni tofauti muhimu ya utendaji.

Viungo

    TCP-IP Illustrated Volume 1 Protocols, W. Richard Stevens, 1994 Addison Wesley

    Soketi inawakilisha uhusiano mmoja kati ya mbili maombi ya mtandao. Programu hizi mbili zinaendeshwa kwa jina kompyuta tofauti, lakini soketi pia inaweza kutumika kwa mawasiliano ya mwingiliano kwenye kompyuta moja. Programu zinaweza kuunda soketi nyingi ili kuwasiliana na kila mmoja. Soketi ni za pande mbili, kumaanisha kuwa pande zote za muunganisho zina uwezo wa kutuma na kupokea data. Kwa hiyo, tundu linaweza kuundwa kwa kinadharia katika ngazi yoyote ya mfano wa OSI kutoka ngazi ya 2 hadi juu. Watengenezaji wa programu mara nyingi hutumia soketi katika programu ya mtandao, ingawa sio moja kwa moja. Maktaba za kupanga kama vile Winsock huficha maelezo mengi ya kiwango cha chini cha upangaji wa soketi. Soketi zimetumika sana tangu miaka ya mapema ya 1980.

    Bandari inawakilisha sehemu ya mwisho au "chaneli" ya mawasiliano ya mtandao. Nambari za bandari huruhusu programu tofauti kwenye kompyuta moja kutumia rasilimali za mtandao bila kuingiliana. Nambari za bandari ni za kawaida katika upangaji wa mtandao, haswa katika upangaji wa soketi. Wakati mwingine, hata hivyo, nambari za bandari zinaonekana kwa mtumiaji wa nasibu. Kwa mfano, baadhi ya tovuti ambazo mtu hutembelea kwenye Mtandao hutumia URL ifuatayo:

    Pamoja na mlinganisho fulani

    Ingawa kwa soketi mambo mengi ya kiufundi tayari yametolewa maelezo...ningependa kuongeza jibu langu, endapo tu, ikiwa kuna mtu bado hawezi kuhisi tofauti kati ya ip, bandari na soketi

    Fikiria seva S,

    na tuseme mtu X, Y, Z tunahitaji huduma (wacha tuseme huduma ya gumzo) kutoka kwa hii seva ya S

    Anwani ya IP inasemaWHO? seva hiyo ya gumzo "S" ambayo X, Y, Z inataka kuwasiliana nayo

    sawa, unayo "seva ni nani"

    lakini tuseme seva "S" pia inatoa huduma zingine kwa watu wengine, sema "S" hutoa huduma za uhifadhi kwa watu A, B, C

    bandari inasema ---> ipi? huduma ambayo wewe (X, Y, Z) unahitaji, i.e. huduma ya mazungumzo, si huduma ya kuhifadhi

    sawa.. unafahamisha seva kuwa unahitaji huduma ya gumzo na sio kuhifadhi

    una umri wa miaka mitatu na seva inaweza kutaka kutambua zote tatu tofauti

    huja tundu

    Sasa tundu linasemaAmbayo? uhusiano maalum

    yaani tuseme

    soketi 1 kwa kila mtu X

    soketi 2 kwa mtu Y

    na soketi 3 za mtu Z

    Natumai hii inasaidia mtu ambaye bado alikuwa amechanganyikiwa :)

    Kwanza, nadhani tunapaswa kuanza na uelewa mdogo wa nini inachukua kupata kifurushi cha A hadi B.

    Ufafanuzi wa kawaida kwa mtandao ni kutumia mfano wa OSI, ambao hugawanya mtandao katika tabaka nyingi kulingana na madhumuni yake. Kuna mambo machache muhimu ambayo tutazingatia hapa:

    • Safu ya kiungo cha data. Safu hii ina jukumu la kupokea pakiti za data kutoka kwa kifaa kimoja cha mtandao hadi kingine na hukaa juu ya safu ambayo husambaza. Inazungumza kuhusu anwani za MAC na inajua jinsi ya kupata wapangishaji kulingana na anwani zao za MAC (vifaa), lakini hakuna zaidi.
    • Safu ya mtandao ni safu inayoruhusu data kusafirishwa kwa mashine na kuvuka mipaka halisi kama vile vifaa halisi. Safu ya mtandao lazima kimsingi iunge mkono utaratibu wa ziada wa msingi wa anwani ambao unahusiana kwa namna fulani anwani ya kimwili; ingiza anwani ya IP (IPv4). Anwani ya IP inaweza kupata kifurushi chako kutoka A hadi B kupitia Mtandao, lakini haijui chochote kuhusu jinsi ya kushughulikia safari za ndege za mtu binafsi. Hii inachakatwa na safu hapo juu kulingana na habari ya uelekezaji.
    • Safu ya usafiri. Safu hii ina jukumu la kufafanua jinsi maelezo yanavyopatikana kutoka A hadi B na vikwazo vyovyote, ukaguzi au hitilafu kwenye tabia hiyo. Kwa mfano, TCP huongeza maelezo ya ziada kwenye pakiti ili iweze kuzingatiwa ikiwa pakiti zimepotea.

    TCP ina, kati ya mambo mengine, dhana ya bandari. Kwa kweli hizi ni ncha tofauti za data kwenye anwani ile ile ya IP ambayo Soketi ya Mtandao (AF_INET) inaweza kujifunga.

    Jibu fupi fupi.

    A bandari inaweza kuelezewa kama anwani ya ndani ndani ya seva pangishi inayotambulisha programu au mchakato.

    A tundu inaweza kuelezewa kama kiolesura cha programu, kuruhusu programu kuwasiliana na programu au michakato mingine, mtandaoni au ndani ya nchi.

    Kwa kawaida utapata mambo mengi ya kinadharia, lakini mojawapo ya njia rahisi za kutofautisha kati ya hizo mbili ni hii:

    Ili kupokea huduma, unahitaji nambari ya huduma. Nambari hii ya huduma inaitwa bandari. Kama hiyo tu.

    Kwa mfano, HTTP kama huduma inaendeshwa kwenye bandari 80.

    Sasa watu wengi wanaweza kuomba huduma na muunganisho wa seva ya mteja umeanzishwa. Kutakuwa na miunganisho mingi. Kila muunganisho unawakilisha mteja. Ili kusaidia kila muunganisho, seva huunda tundu kwa kila muunganisho ili kusaidia mteja wake.

    Inaonekana kuna majibu mengi kulinganisha tundu na unganisho kati ya PC mbili. Nadhani hii ni uongo kabisa. Soketi imekuwa mwisho kwenye Kompyuta 1, ambayo inaweza au isiunganishwe - bila shaka sote tumetumia vipokezi au soketi za UDP* wakati fulani. Sehemu muhimu ni kwamba inalenga na inafanya kazi. Kutuma ujumbe kwa faili 1.1.1.1:1234 hakuna uwezekano wa kufanya kazi kwa kuwa hakuna tundu la mwisho huo.

    Soketi ni maalum ya itifaki - kwa hivyo utekelezaji wa upekee ni kwamba TCP / na UDP / hutumia * (ipaddress: bandari), tofauti na, kwa mfano, IPX (Mtandao, Node na ... mchezo, tundu - lakini tundu tofauti kuliko chini. neno la jumla "tundu". Nambari za soketi za IPX ni sawa na bandari za IP). Lakini zote hutoa mwisho wa kipekee unaoweza kushughulikiwa.

    Kwa vile IP imekuwa itifaki kuu, bandari (katika masharti ya mtandao) imekuwa umoja huku nambari ya bandari ya UDP au TCP ikiwa sehemu ya anwani ya tundu.

    • UDP haina uhusiano-upande wowote - hii ina maana kwamba mzunguko wa kawaida haujaundwa kati ya ncha mbili. Walakini, kama sehemu ya mwisho bado tunarejelea soketi za UDP. Kazi za API huweka wazi kuwa zote mbili ni rahisi aina tofauti soketi. SOCK_DGRAM ni UDP (inatuma tu ujumbe) na SOCK_STREAM ni TCP (inaunda mzunguko pepe).

      Kitaalam, kichwa cha IP kina anwani ya IP, na itifaki juu ya IP (UDP au TCP) ina nambari ya bandari. Hii inaruhusu matumizi ya itifaki zingine (kama vile ICMP, ambazo hazina nambari za bandari, lakini zina maelezo ya anwani ya IP).

      Haya ni masharti kutoka mawili vikoa tofauti: "bandari" ni dhana kutoka kwa mitandao ya TCP/IP, "soketi" ni API (programu). "Soketi" huundwa (katika msimbo) kwa kuchukua lango, jina la mwenyeji, au adapta ya mtandao na kuzichanganya katika muundo wa data ambao unaweza kutumia kutuma au kupokea data.

      Miunganisho ya TCP-IP ni njia mbili zinazounganisha anwani moja: mchanganyiko wa bandari hadi anwani nyingine: mchanganyiko wa bandari. Kwa hivyo wakati wowote unapofungua muunganisho na mashine ya ndani kwa bandari seva ya mbali(km www.google.com:80), pia unaunganisha nambari mpya bandari kwenye kompyuta yako na muunganisho ili seva iweze kukurejeshea vitu (km 127.0.0.1:65234). Ni muhimu kutumia netstat kutazama miunganisho yako kwenye kompyuta yako:

      > netstat -nWp tcp (kwenye OS X) Miunganisho Inayotumika ya Mtandao Proto Recv-Q Send-Q Anwani ya Ndani ya Nchi (jimbo) tcp4 0 0 192.168.0.6.49871 17.172.232.57.5223 IMEANZISHWA ...

      Tundu ni aina maalum ya maelezo ya faili ambayo hutumiwa na mchakato wa kuomba huduma za mtandao kutoka kwa mfumo wa uendeshaji. Anwani ya tundu ni mara tatu: (itifaki, anwani ya ndani, mchakato wa ndani), ambapo mchakato wa ndani unatambuliwa na nambari ya bandari.

      Katika seti ya TCP/IP, kwa mfano:

      (tcp, 193.44.234.3, 12345)

      Mazungumzo ni njia ya mawasiliano kati ya michakato miwili, hivyo huonyesha uhusiano kati ya hizo mbili. Muungano ni nakala 5 zinazofafanua kikamilifu michakato miwili iliyo na muunganisho: (itifaki, anwani-ya ndani, mchakato wa ndani, anwani ya kigeni, mchakato wa kigeni)

      Katika seti ya TCP/IP, kwa mfano:

      (tcp, 193.44.234.3, 1500, 193.44.234.5, 21)

      inaweza kuwa chama halali.

      Semi-association: (itifaki, anwani ya eneo, mchakato wa ndani)

      (itifaki, anwani ya kigeni, mchakato wa kigeni)

      ambayo hufafanua kila nusu ya unganisho.

      Kiungo cha nusu pia huitwa tundu au anwani ya usafiri. Hiyo ni, tundu ni mwisho wa mawasiliano ambayo inaweza kutajwa na kushughulikiwa kwenye mtandao. Kiolesura cha tundu ni mojawapo ya violesura vya programu vya programu (API) kwa itifaki za mawasiliano. Iliyoundwa kama kiolesura cha programu ya mawasiliano ya ulimwengu wote, ilianzishwa kwanza Mfumo wa UNIX 4.2BSD. Ingawa haikuwa sanifu, ikawa kiwango cha tasnia ya ukweli.

      Bandari ilikuwa sehemu rahisi, ni kitambulisho cha kipekee cha tundu. Soketi ni kitu ambacho michakato inaweza kutumia kuanzisha miunganisho na kuwasiliana na kila mmoja. Tall Jeff alikuwa na mlinganisho mzuri wa simu ambao haukuwa kamilifu, kwa hivyo niliamua kuurekebisha:

      Programu ina jozi ya michakato inayowasiliana kupitia mtandao (jozi ya seva ya mteja). Taratibu hizi hutuma na kupokea ujumbe kwenda na kutoka kwa mtandao kupitia kiolesura cha programu tundu. Kwa kuzingatia mlinganisho uliowasilishwa katika kitabu " Mtandao wa kompyuta: njia ya juu-chini." Kuna nyumba ambayo inataka kuwasiliana na nyumba nyingine. Hapa nyumba ni sawa na mchakato na mlango wa tundu. Mchakato wa kutuma unadhani kuwa upande wa pili wa mlango kuna miundombinu. ambayo itasambaza data hadi kulengwa.Mara tu ujumbe unapofika kutoka kwa upande mwingine, unapitia mlango wa kipokezi (tundu) hadi kwenye nyumba (mchakato).Mchoro huu kutoka kwa kitabu hichohicho unaweza kukusaidia:

      Soketi ni sehemu ya safu ya usafiri, ambayo hutoa mawasiliano ya mantiki na maombi. Hii ina maana kwamba kutoka kwa mtazamo wa maombi, nodi zote mbili zimeunganishwa moja kwa moja, ingawa kuna ruta nyingi na/au swichi kati yao. Kwa hivyo tundu sio kiunganisho yenyewe, ni sehemu ya mwisho ya unganisho. Itifaki za safu ya uchukuzi hutekelezwa tu kwa wapangishaji, sio kwenye vipanga njia vya kati.
      Bandari kutoa njia ya kushughulikia ndani kwa mashine. Lengo kuu ni kuruhusu michakato mingi kutuma na kupokea data kupitia mtandao bila kuingilia michakato mingine (data zao). Soketi zote hutolewa na nambari ya bandari. Sehemu inapofika kwa seva pangishi, safu ya usafiri huchunguza nambari ya bandari lengwa la sehemu hiyo. Kisha huhamisha sehemu kwenye tundu sahihi. Kazi hii ya kutoa data kwenye sehemu ya safu ya usafiri kwenye tundu sahihi inaitwa kudhoofisha. Kisha data ya sehemu hupitishwa kwa mchakato uliowekwa kwenye tundu.

      Soketi ni muundo wa programu yako. Ni zaidi au chini ya faili; ina shughuli kama vile kusoma na kuandika. Si jambo la kimwili; ni njia ya programu yako kurejelea vitu vya kawaida.

      Bandari ni kitu kama kifaa. Kila mwenyeji ana mtandao mmoja au zaidi (kimwili); mwenyeji ana anwani kwenye kila mtandao. Kila anwani inaweza kuwa na maelfu ya bandari.

      Soketi moja pekee inaweza kutumia mlango kwenye anwani. Soketi hutenga bandari kwa njia sawa na kutenga kifaa kwa I/O mfumo wa faili. Mara bandari inapotolewa, hakuna tundu lingine linaloweza kuunganisha kwenye bandari hiyo. Bandari itatolewa wakati tundu limefungwa.

      Tundu ni ncha moja ya njia mbili za mawasiliano kati ya programu mbili zinazoendeshwa kwenye mtandao. Soketi imefungwa kwa nambari ya bandari ili safu ya TCP iweze kutambua programu ambayo data inakusudiwa kutumwa.

      Istilahi ya jamaa ya TCP/IP nadhani inaashiria swali hili. Kwa maneno ya watu wa kawaida:

      PORT ni nambari ya simu ya nyumba maalum katika maalum namba ya Posta. Msimbo wa posta wa jiji unaweza kuzingatiwa kama anwani ya IP ya jiji na nyumba zote katika jiji hilo.

      SOCKET, kwa upande mwingine, ni kama simu iliyoanzishwa kati ya simu za jozi za nyumba zinazozungumza. Simu hizi zinaweza kuanzishwa kati ya nyumba katika jiji moja au nyumba mbili katika miji tofauti. Hii ni ya muda kuweka njia kati ya simu mbili zinazozungumza, ambayo ni SOCKET.

      Bandari na tundu zinaweza kulinganishwa na tawi la benki.

      Nambari ya jengo la Benki ni sawa na anwani ya IP. Benki ina sehemu mbalimbali kama vile:

      1. Idara ya akaunti ya akiba
      2. Idara ya Mikopo ya Kibinafsi
      3. Idara ya mikopo ya nyumba
      4. Idara ya malalamiko

      Hivyo, 1 (Idara ya Akaunti ya Akiba), 2 (Idara ya Mikopo ya Kibinafsi), 3 (Idara ya Mikopo ya Nyumbani) na 4 (Idara ya Kushughulikia Malalamiko) ni bandari.

      Sasa hebu tukuambie kwamba unaenda kufungua akaunti ya akiba, unaenda benki (anwani ya IP), kisha unaenda "idara ya akaunti ya akiba" (bandari #1), kisha unakutana na mfanyakazi mmoja anayefanya kazi katika "akaunti ya akiba ya idara" ". Hebu tuiite SAVINGACCOUNT_EMPLOYEE1 ili kufungua akaunti.

      SAVINGACCOUNT_EMPLOYEE1 ni mpini wako wa soketi, kwa hivyo inaweza kutoka SAVINGACCOUNT_EMPLOYEE1 hadi SAVINGACCOUNT_EMPLOYEEN. Haya yote ni maelezo ya tundu.

      Vivyo hivyo, idara zingine zitakuwa na kazi chini yao na zinafanana na tundu.

      Tundu ni sehemu ya mwisho ya mawasiliano. Soketi haihusiani moja kwa moja na familia ya itifaki ya TCP/IP; inaweza kutumika na itifaki yoyote inayoungwa mkono na mfumo wako. API ya Soketi C inatarajia kupokea kwanza kitu tupu cha soketi kutoka kwa mfumo, ambacho kinaweza kuunganishwa kwa anwani ya tundu ya karibu (kupokea moja kwa moja trafiki inayoingia kwa itifaki zisizo na muunganisho au kukubali maombi ya muunganisho yanayoingia ya itifaki zenye mwelekeo wa unganisho), au hiyo. unaweza kuunganisha kwa anwani ya tundu ya mbali (kwa aina yoyote ya itifaki). Unaweza hata kufanya zote mbili ikiwa unataka kudhibiti zote mbili: anwani ya tundu ya ndani ambayo tundu imefungwa, na anwani ya tundu ya mbali ambayo tundu imefungwa. Kwa itifaki zisizo na muunganisho, kuunganisha tundu sio lazima hata, lakini ikiwa hautafanya hivyo, itabidi pia kupitisha anwani ya marudio na kila pakiti unayotaka kutuma juu ya tundu, ni jinsi gani tundu lingejua mahali pa kutuma? hizo data? Faida ni kwamba unaweza kutumia tundu moja kutuma pakiti kwa anwani tofauti za tundu. Mara baada ya kusanidi tundu na labda hata kuiunganisha, ichukulie kama njia ya mawasiliano ya pande mbili. Unaweza kuitumia kusambaza data kwenye eneo fulani, na eneo lingine linaweza kuitumia kusambaza data kwako. Unachoandika kwenye tundu hutumwa, na kile kilichopokelewa kinasomeka.

      Kwa upande mwingine, bandari ni kitu ambacho itifaki fulani tu kwenye safu ya itifaki ya TCP/IP inayo. Pakiti za TCP na UDP zina bandari. Bandari ni nambari tu. Mchanganyiko wa mlango wa chanzo na lango lengwa hufafanua njia ya mawasiliano kati ya wapangishaji wawili. Kwa mfano, unaweza kuwa na seva ambayo inahitaji kuwa seva rahisi ya HTTP na seva rahisi ya FTP. Ikiwa pakiti sasa itawasili kwa anwani ya seva hii, inajuaje kama ni pakiti ya HTTP au seva ya FTP? Kweli, itajua kwa kuwa seva ya HTTP itakuwa inaendesha kwenye bandari 80 na seva ya FTP itakuwa kwenye bandari 21, kwa hivyo ikiwa pakiti itafika na bandari fikio 80, inakusudiwa kwa seva ya HTTP na sio seva ya FTP . Pia pakiti ina lango la chanzo kwa sababu bila lango la chanzo kama hilo seva inaweza tu kuwa na muunganisho mmoja kwa anwani moja ya IP kwa wakati mmoja. Lango la chanzo huruhusu seva kutofautisha kati ya miunganisho inayofanana: zote zina lango lilelile la kulengwa, kwa mfano lango 80, anwani ile ile ya IP, anwani ya seva sawa na anwani ya IP ya chanzo sawa kwa sababu zote zinatoka kwa mteja mmoja. , lakini kwa kuwa wana bandari tofauti za chanzo, seva inaweza kuwatenganisha. Na seva inapotuma majibu, hufanya hivyo kwenye bandari ambayo ombi lilitoka, kwa njia hii mteja anaweza pia kutofautisha kati ya majibu tofauti anayopokea.

      Lango moja linaweza kuwa na jaketi moja au zaidi zilizounganishwa kwa IP nyingine ya nje, kama vile soketi nyingi.

      TCP 192.168.100.2:9001 155.94.246.179:39255 IMEANZISHWA 1312 TCP 192.168.100.2:9001 171.25.193.9:61832 131832 131832 TCP 1312 TCP 192.168.100.2:9001 171.25.193.9:61832 131832 131832 ESTABLI 131832 ESTABLI. 1 178.62.199.226:37912 IMEANZISHWA 1312 TCP 192.168.100.2:9001 188.193.64.150: 40900 IMEANZISHWA 1312 TCP 192.168.100.2:9001 198.23.194.149:43970 IMEANZISHWA 1312 TCP 192.168.100.2:9001 381298.

      Soketi ni kifupisho kinachotolewa na kernel kwa programu za mtumiaji kwa ingizo/pato la data. Aina ya tundu imedhamiriwa na itifaki yake ya usindikaji, mawasiliano ya IPC, nk. Kwa hivyo ikiwa mtu ataunda tundu la TCP, anaweza kufanya udanganyifu kama kusoma data kwenye soketi na kuiandikia data kwa kutumia njia rahisi na usindikaji wa itifaki ya safu ya chini kama tafsiri za TCP na kusambaza pakiti kwa zile za chini. itifaki za mtandao inafanywa na utekelezaji maalum wa tundu kwenye kernel. Faida ni kwamba mtumiaji sio lazima kuwa na wasiwasi juu ya jinsi ya kushughulika na itifaki maalum na kusoma tu na kuandika data kwenye tundu kama. buffer ya kawaida. Vile vile ni kweli katika kesi ya IPC, mtumiaji anasoma tu na kuandika data kwenye tundu na kernel inashughulikia maelezo yote ya kiwango cha chini kulingana na aina ya soketi iliyoundwa.