Katika mfumo gani wa faili soketi zimesajiliwa. Mfano: DLL salama ya nyuzi nyingi kwa ujumbe wa soketi. Vipengele vya tundu la seva

kiolesura cha simu cha mfumo wa mtandao ( soketi(), funga (), recvfrom(), tuma kwa() n.k.) katika mfumo wa uendeshaji wa UNIX inaweza kutumika kwa mabunda mengine ya itifaki (na kwa itifaki zilizo hapa chini safu ya usafiri).

Wakati wa kuunda tundu, lazima ueleze aina yake kwa usahihi. Uainishaji huu unafanywa kwa kutumia vigezo vitatu vya kupiga simu soketi(). Kigezo cha kwanza kinabainisha ni familia gani ya itifaki ambayo tundu iliyoundwa ni ya familia, na vigezo vya pili na vya tatu vinafafanua itifaki maalum ndani ya familia hiyo.

Kigezo cha pili kinatumika kutaja aina ya kiolesura cha kufanya kazi na tundu - itakuwa tundu la mkondo, tundu la kufanya kazi na datagrams, au nyingine. Kigezo cha tatu kinabainisha itifaki ya aina ya kiolesura ulichopewa. KATIKA Rafu ya itifaki ya TCP/IP Kuna itifaki moja tu ya soketi za mtiririko - TCP na itifaki moja tu ya soketi za datagram - UDP, kwa hivyo kwa itifaki za usafirishaji za TCP/IP kigezo cha tatu kinapuuzwa.

Katika safu zingine za itifaki, kunaweza kuwa na itifaki kadhaa zilizo na aina moja ya kiolesura, kwa mfano, zile za datagram, ambazo hutofautiana katika kiwango cha kuegemea.

Kwa itifaki za usafiri za TCP/IP, tutabainisha kila mara AF_INET iliyobainishwa awali (Anwani ya familia - Mtandao) au kisawe chake PF_INET (Familia ya Itifaki - Mtandao) kama kigezo cha kwanza.

Kigezo cha pili kitachukua thamani zilizobainishwa awali \ SOCK_STREAM za soketi za mtiririko na SOCK_DGRAM kwa soketi za datagram.

Kwa kuwa parameta ya tatu haijazingatiwa kwa upande wetu, tutabadilisha thamani 0 ndani yake.

Kiungo cha habari kuhusu tundu iliyoundwa imewekwa Jedwali la faili za mchakato wazi sawa na jinsi ilivyofanywa kwa pips na FIFOs (tazama warsha 5). Simu ya mfumo inamrudishia mtumiaji kielezi cha faili kinacholingana na kipengee cha jedwali kilicho na watu wengi, ambacho tutakiita kuanzia sasa kielezi cha tundu. Njia hii ya kuhifadhi habari kuhusu soketi inaruhusu, kwanza, michakato ya mtoto kurithi kutoka kwa michakato ya mzazi, na, pili, kutumia kwa soketi baadhi ya simu za mfumo ambazo tayari tunazifahamu kutokana na kufanya kazi na pips na FIFOs: close( ) , soma() , andika() .

Simu ya mfumo ili kuunda tundu

Mfano wa simu ya mfumo

#pamoja na #pamoja na tundu la int (kikoa cha int, aina ya int, itifaki ya int);

Maelezo ya simu ya mfumo

Simu ya mfumo tundu hutumikia kuunda nodi ya mawasiliano ya kawaida katika mfumo wa uendeshaji. Maelezo haya sio maelezo kamili simu ya mfumo, lakini imekusudiwa tu kwa matumizi katika kozi yetu. Kwa habari kamili, angalia Mwongozo wa UNIX.

Kigezo cha kikoa kinafafanua familia ya itifaki ambayo habari itahamishwa. Tutazingatia familia mbili tu kati ya hizi kati ya kadhaa zilizopo. Kuna maadili ya parameta yaliyoainishwa kwao:

  • PF_INET - kwa Familia ya itifaki ya TCP/IP ;
  • PF_UNIX - kwa familia itifaki za ndani UNIX, vinginevyo huitwa kikoa cha UNIX.

Kigezo cha aina huamua semantiki za ubadilishanaji wa habari: ikiwa mawasiliano yatafanywa kupitia ujumbe (datagrams), kwa kuanzisha muunganisho wa mtandaoni au kwa njia nyingine. Tutatumia njia mbili tu za kubadilishana habari na maadili yaliyofafanuliwa kwa paramu ya aina:

  • SOCK_STREAM - kwa mawasiliano kwa kutumia uanzishwaji muunganisho wa mtandaoni ;
  • SOCK_DGRAM - kwa kubadilishana habari kupitia ujumbe.

Kigezo cha itifaki kinabainisha itifaki maalum ya itifaki iliyochaguliwa ya familia na njia ya mawasiliano. Ni muhimu tu ikiwa kuna itifaki kadhaa kama hizo. Kwa upande wetu, familia ya itifaki na aina ya kubadilishana habari huamua kipekee itifaki. Kwa hivyo, tutafikiria parameta hii ni sawa na 0.

Thamani ya kurudi

Ikifaulu, simu ya mfumo hurejesha kielezi cha faili (thamani kubwa kuliko au sawa na 0), ambacho kitatumika kama marejeleo ya nodi ya mawasiliano iliyoundwa katika simu zote zaidi za mtandao. Ikiwa kosa lolote litatokea, thamani hasi inarejeshwa.

Anwani za tundu. Kuweka anwani ya tundu. bind() simu ya mfumo

Mara tu tundu linapoundwa, unahitaji kusanidi anwani yake. Simu ya mfumo hutumiwa kwa hili funga (). Kigezo cha kwanza cha simu lazima kiwe na maelezo ya tundu ambayo anwani yake inasanidiwa. Vigezo vya pili na vya tatu vinataja anwani hii.

Kigezo cha pili lazima kiwe kielekezi kwa muundo wa sockaddr ulio na sehemu za mbali na za ndani za anwani kamili.

Viashiria vya aina ya struct sockaddr * vinapatikana katika simu nyingi za mfumo wa mtandao; hutumiwa kuwasilisha habari kuhusu ni anwani gani tundu ni au inapaswa kushikamana nayo. Hebu tuangalie kwa karibu aina hii ya data. Muundo wa muundo sockaddr umeelezewa kwenye faili kwa njia ifuatayo:

struct sockaddr (short sa_family; char sa_data; );

Utungaji huu wa muundo ni kutokana na ukweli kwamba simu za mfumo wa mtandao zinaweza kutumika kwa familia tofauti za itifaki, ambazo hufafanua nafasi za anwani tofauti kwa anwani za tundu za mbali na za ndani. Kimsingi, aina hii ya data ni kiolezo cha jumla cha kupitisha miundo ya data mahususi kwa kila familia ya itifaki kwa simu za mfumo. Kipengele pekee cha kawaida cha miundo hii ni nyanja fupi ya sa_family (ambayo, kwa kawaida, inaweza kuwa na majina tofauti katika miundo tofauti; jambo muhimu pekee ni kwamba wote ni wa aina moja na ni vipengele vya kwanza vya miundo yao) kuelezea familia ya itifaki. Yaliyomo kwenye uwanja huu yanachambuliwa na simu ya mfumo ufafanuzi sahihi muundo wa habari iliyopokelewa.

Kufanya kazi na familia ya itifaki za TCP/IP tutatumia anwani ya tundu ya aina ifuatayo, iliyoelezwa kwenye faili :

struct sockaddr _in( short sin_family; /* Familia ya itifaki iliyochaguliwa kila wakati ni AF_INET */ bandari fupi isiyotiwa saini; /* Nambari ya bandari 16-bit katika mpangilio wa baiti ya mtandao */ struct in_addr sin_addr; /* Anwani ya kiolesura cha mtandao */ char sin_zero; /* Sehemu hii haitumiki, lakini lazima ijazwe na sufuri */ );

Kipengele cha kwanza cha muundo - sin_family inabainisha familia ya itifaki. Tutaingia ndani yake AF_INET iliyofafanuliwa tayari, ambayo tayari tunaijua (tazama sehemu iliyotangulia).

Sehemu ya mbali ya anwani kamili - anwani ya IP - iko katika muundo wa aina ya muundo in_addr, ambayo tulikutana nayo katika sehemu ya "Kazi za Tafsiri za Anwani ya IP" inet_ntoa(), inet_aton() " .

Ili kuonyesha nambari ya mlango, kipengele cha muundo wa sin_port kinatumika, ambamo nambari ya mlango inapaswa kuhifadhiwa kwa mpangilio wa mtandao. Kuna chaguzi mbili za kubainisha nambari ya bandari: lango lisilobadilika kama anavyotaka mtumiaji na lango ambalo limepewa nasibu. mfumo wa uendeshaji. Chaguo la kwanza linahitaji kubainisha nambari chanya, inayojulikana awali kama nambari ya bandari na kwa itifaki ya UDP kwa kawaida hutumiwa wakati wa kusanidi anwani za soketi na wakati wa kutuma habari kwa kutumia simu ya mfumo. tuma kwa()(tazama sehemu inayofuata). Chaguo la pili linahitaji kubainisha 0 kama nambari ya mlango. Katika hali hii mfumo wa uendeshaji yenyewe hufunga tundu kwa nambari ya bandari ya bure. Njia hii hutumiwa kwa kawaida wakati wa kuanzisha soketi kwa programu za mteja, wakati mtayarishaji hawana haja ya kujua nambari halisi ya bandari mapema.

Je, mtumiaji anaweza kutumia nambari gani ya bandari katika usanidi usiobadilika? Nambari za bandari 1 hadi 1023 zinaweza tu kupewa soketi kwa michakato inayoendeshwa na haki za msimamizi wa mfumo. Kwa kawaida, nambari hizi hupewa huduma za mtandao wa mfumo, bila kujali aina ya mfumo wa uendeshaji unaotumiwa, ili programu za mteja wa mtumiaji zinaweza kuomba huduma kutoka kwa anwani sawa za ndani. Pia kuna idadi ya kutumika sana programu za mtandao, ambayo huendesha michakato na ruhusa za kawaida za mtumiaji (kwa mfano, X-Windows). Kwa programu kama hizi za Shirika la Mtandao za kupeana majina na nambari (


Sura ya 15

Katika sura hii, utafahamishwa kwa njia nyingine ya kuingiliana kati ya michakato, ambayo ni tofauti sana na ile tuliyojadili katika sura ya 13 Na 14. Hadi sasa, zana zote ambazo tumejadili zimetokana na rasilimali zilizoshirikiwa kwenye kompyuta moja. Rasilimali zinaweza kuwa maeneo ya mfumo wa faili, sehemu za kumbukumbu zilizoshirikiwa, au foleni za ujumbe, lakini zinaweza tu kutumiwa na michakato inayoendeshwa kwenye mashine moja.

Berkeley UNIX ilianzisha zana mpya ya mawasiliano, kiolesura cha tundu, ambacho ni kiendelezi cha dhana ya bomba iliyojadiliwa ndani Sura ya 13. Mifumo ya Linux pia ina miingiliano ya tundu.

Unaweza kutumia soketi kwa njia sawa na bomba, lakini zinaunga mkono mawasiliano ndani ya mtandao wa kompyuta. Mchakato kwenye mashine moja unaweza kutumia soketi kuwasiliana na mchakato kwenye mashine nyingine, na hivyo kufanya iwezekane kuwa na mifumo ya seva-teja kusambazwa kwenye mtandao. Michakato inayoendeshwa kwenye mashine moja inaweza pia kutumia soketi.

Kwa kuongeza, kiolesura cha tundu kimepatikana kwenye Windows kupitia soketi za Windows zinazopatikana hadharani au vipimo vya WinSock. Huduma za soketi katika Windows hutolewa na faili ya mfumo wa Winsock.dll. Kwa hiyo, programu zinazoendesha Windows zinaweza kuingiliana kwenye mtandao na kompyuta zinazoendesha Linux na UNIX na kinyume chake, hivyo kutekeleza mifumo ya mteja-server. Ingawa kiolesura cha programu cha WinSock hakifanani kabisa na kiolesura cha tundu katika UNIX, kinategemea soketi sawa.

Hatuwezi kufunika uwezo wote wa mitandao mingi wa Linux katika sura moja, kwa hivyo utapata tu violesura vya msingi vya programu za mtandao vinavyokuruhusu kuandika programu zako za mitandao.

Tutashughulikia mada zifuatazo kwa undani zaidi:

□ jinsi miunganisho ya soketi inavyofanya kazi;

□ sifa za soketi, anwani na ubadilishanaji wa taarifa;

□ habari za mtandao na daemon ya mtandao (inetd/xinetd);

□ wateja na seva.

Soketi ni nini?

Soketi ni njia ya mawasiliano ambayo inaruhusu maendeleo ya mifumo ya seva ya mteja kwa ndani, kwenye mashine moja, au matumizi ya mtandao. Vipengele vya Mfumo wa Uendeshaji wa Linux kama vile pato, miunganisho ya hifadhidata na huduma ya Wavuti, na vile vile huduma za mtandao kama vile

, iliyokusudiwa kwa usajili wa mbali, na kutumika kwa uhamisho wa faili, kwa kawaida hutumia soketi kwa kubadilishana data.

Soketi huundwa na kutumika tofauti na bomba kwa sababu zinaangazia tofauti tofauti kati ya mteja na seva. Utaratibu wa tundu hukuruhusu kuunda wateja wengi waliounganishwa kwenye seva moja.

Viunganisho vya msingi wa tundu

Miunganisho ya soketi inaweza kuzingatiwa kama kupiga simu kwa taasisi. Simu inakuja kwenye shirika na inajibiwa na mpokeaji ambaye anaelekeza simu kwa idara inayofaa (mchakato wa seva) na kutoka hapo hadi kwa mfanyakazi sahihi (tundu la seva). Kila simu inayoingia (mteja) huelekezwa kwenye sehemu ya mwisho inayofaa, na mawakala wa kati wanaweza kushughulikia simu zinazofuata. Kabla ya kuangalia kuanzisha miunganisho kwa kutumia soketi kwenye mifumo ya Linux, tunahitaji kuelewa jinsi wanavyotenda katika programu-tumizi za soketi zinazofahamu muunganisho.

Mara ya kwanza programu ya seva huunda tundu, ambayo, kama kielezi cha faili, inawakilisha rasilimali iliyopewa mchakato wa seva moja. Seva huunda kwa kutumia simu ya mfumo

, na tundu hili haliwezi kushirikiwa na michakato mingine.

Ifuatayo, seva inapeana jina kwenye tundu. Soketi za ndani zilizo na majina ya faili katika mfumo wa faili wa Linux mara nyingi ziko kwenye saraka ya /tmp au /usr/tmp. Kwa soketi za mtandao, jina la faili litakuwa kitambulisho cha huduma (nambari ya mlango/mahali pa ufikiaji) inayohusishwa na mtandao mahususi ambao wateja wanaweza kuunganisha. Kitambulisho hiki kwa kubainisha nambari maalum bandari inayolingana na mchakato sahihi wa seva huruhusu Linux kuelekeza miunganisho inayoingia kwenye njia maalum. Kwa mfano, seva ya Wavuti kwa kawaida huunda tundu kwenye bandari 80, kitambulisho kilichohifadhiwa kwa madhumuni haya. Vivinjari vya wavuti vinajua kutumia port 80 kwa miunganisho yao ya HTTP kwenye Tovuti ambazo mtumiaji anataka kusoma. Soketi inaitwa kwa kutumia simu ya mfumo

. Ifuatayo, mchakato wa seva unasubiri mteja kuunganishwa kwenye tundu lililotajwa. Simu ya mfumo huunda foleni ya miunganisho inayoingia. Seva inaweza kuzikubali kwa kutumia simu ya mfumo.

Wakati seva inaita

, soketi mpya imeundwa ambayo ni tofauti na tundu iliyopewa jina. Soketi hii mpya inatumika tu kuwasiliana na mteja huyu mahususi. Soketi iliyotajwa huhifadhiwa kwa miunganisho ya siku zijazo kutoka kwa wateja wengine. Ikiwa seva imeandikwa kwa usahihi, inaweza kufaidika kutokana na miunganisho mingi. Seva ya Wavuti hufanikisha hili kwa kupeana kurasa kwa wateja wengi kwa wakati mmoja. Katika kesi ya seva rahisi, wateja wote wanaofuata wanasubiri kwenye foleni hadi seva iko tayari tena.

Upande wa mteja wa mfumo kwa kutumia soketi ni rahisi zaidi. Mteja huunda tundu lisilo na jina kwa kutumia simu

. Kisha huita kuunganishwa kwa seva kwa kutumia soketi iliyopewa jina la seva kama anwani.

Baada ya kusakinishwa, soketi zinaweza kutumika kama vifafanuzi vya kiwango cha chini vya faili, hivyo basi kuruhusu ubadilishanaji wa data wa pande mbili.

Mazoezi kamili 15.1 na 15.2.

Zoezi 15.1. Mteja rahisi wa ndani. Tutaangalia simu ya mfumo kwa undani baadaye kidogo tunapojadili maswala kadhaa ya kushughulikia.

1. Jumuisha faili muhimu za kichwa na uweke vigeuzo:


2. Unda soketi kwa mteja:

sockfd = soketi(AF_UNIX, SOCK_STREAM, 0);

3. Taja tundu kama ilivyokubaliwa na seva:

address.sun_family = AF_UNIX;
strcpy (anwani.sun_path, "server_socket");

4. Unganisha tundu lako kwenye tundu la seva:

:
printf("char kutoka kwa seva = %c\n", ch);

Programu hii itaanguka ukijaribu kuiendesha kwa sababu soketi iliyopewa jina bado haijaundwa. (Ujumbe kamili wa hitilafu unaweza kutofautiana kwenye mifumo tofauti.)

Lo: mteja1: Hakuna faili au saraka kama hiyo
Zoezi 15.2. Seva rahisi ya ndani

Ifuatayo ni programu rahisi ya seva, server1.c, ambayo inakubali ombi la uunganisho kutoka kwa mteja. Inaunda tundu la seva, inaipa jina, inaunda foleni ya kusubiri, na inakubali maombi ya muunganisho.

1. Jumuisha faili muhimu za kichwa na uweke vigeuzo:


struct sockaddr_un server_anwani;
struct sockaddr_un mteja_anwani;

2. Ondoa soketi zote za zamani na uunde tundu lisilo na jina la seva:

server_sockfd = tundu(AF_UNIX, SOCK_STREAM, 0);

3. Ipe tundu jina:

server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, "server_socket");

4. Unda foleni ya ombi la muunganisho na usubiri ombi la mteja:

5. Kubali ombi la muunganisho:

(struct sockaddr *)&client_address, &client_len);

6. Soma na uandike data ya mteja kwa kutumia

:

Inavyofanya kazi

Katika mfano huu programu ya seva inaweza tu kuhudumia mteja mmoja kwa wakati mmoja. Inasoma tu herufi iliyopokelewa kutoka kwa mteja, inaiongeza, na kuiandika tena. Katika zaidi mifumo tata ambapo seva inapaswa kufanya kazi zaidi kwa niaba ya mteja, mbinu hii haitafaa kwa sababu wateja wengine hawataweza kuunganisha hadi seva izime. Baadaye utaona mbinu kadhaa zinazoruhusu wateja wengi kuunganishwa.

Unapoendesha programu ya seva, huunda tundu na kusubiri maombi ya uunganisho. Ikiwa unaiendesha kwa nyuma, i.e. itaendeshwa kwa kujitegemea, basi unaweza kuendesha wateja kama kazi za kipaumbele cha juu.

Wakati wa kusubiri maombi ya muunganisho, seva huonyesha ujumbe. Katika mfano uliopewa, seva inangojea ombi kutoka kwa tundu la mfumo, na unaweza kuiona kwa kutumia amri ya kawaida.

.

Ni vyema kufuta soketi baada ya kumaliza kufanya kazi nayo, hata kama programu itaacha kufanya kazi kwa sababu ya kupokea mawimbi. Hii itazuia mfumo wa faili usiwe na vitu vingi vya faili ambazo hazijatumiwa.

$ ls -lF tundu la seva
srwxr-xr-x watumiaji 1 wa neil 0 2007-06-23 11:41 server_socket=

Hapa aina ya kifaa ni tundu, ambayo inaonyeshwa na ishara

kabla ya ruhusa na ishara mwishoni mwa jina. Soketi iliundwa kama faili ya kawaida na haki za ufikiaji zilizorekebishwa na ya sasa. Ikiwa unatumia amri, unaweza kuona seva inayoendesha nyuma. Inaonyeshwa kulala (parameta sawa na ) na kwa hivyo haitumii rasilimali za CPU.
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME KAMANDA
0 1000 23385 10689 17 0 1424 312 361800 S pts/1 0:00 ./server1

Sasa, unapoendesha programu, utafanikiwa kuunganisha kwenye seva. Kwa kuwa tundu la seva lipo, unaweza kuunganisha nayo na kubadilishana data.

char ya kusubiri ya seva kutoka kwa seva = B

Kwenye terminal, pato la seva na mteja limechanganywa, lakini unaweza kuona kwamba seva ilipokea tabia kutoka kwa mteja, ikaongeza, na kuirudisha. Seva kisha inaendelea kufanya kazi na inasubiri mteja anayefuata. Ukiendesha wateja wengi pamoja, watahudumiwa kwa zamu, ingawa matokeo yanaweza kuchanganywa zaidi.

$ ./client1 & ./client1 & ./client1 &

Sifa za tundu

Ili kuelewa kikamilifu simu za mfumo zinazotumiwa katika mfano huu, unahitaji kujua kitu kuhusu mitandao katika mifumo ya UNIX.

Soketi ni sifa ya sifa tatu: kikoa, aina Na itifaki. Pia wana anwani ambayo hutumiwa kama jina la tundu. Miundo ya anwani hutofautiana kulingana na kikoa, pia huitwa familia ya itifaki. Kila familia ya itifaki inaweza kutumia familia moja au zaidi za anwani, ambayo inafafanua umbizo la anwani.

Vikoa vya Soketi

Vikoa hufafanua mazingira ya uendeshaji wa mtandao ambayo muunganisho wa tundu utatumia. Kikoa cha tundu maarufu zaidi ni

, ambayo inahusu mtandao na hutumiwa kwenye mitandao mingi ya ndani ya Linux na, bila shaka, kwenye mtandao yenyewe. Itifaki ya Mtandao ya kiwango cha chini (IP), ambayo ina familia moja tu ya anwani, inaweka njia maalum ya kubainisha kompyuta kwenye mtandao. Inaitwa Anwani ya IP.
Kumbuka

Ili kuondokana na baadhi ya matatizo ya itifaki ya IP ya kawaida yenye idadi ndogo ya anwani zinazopatikana, itifaki ya mtandao ya kizazi kijacho IPv6 ilitengenezwa. Inatumia kikoa cha tundu tofauti

na miundo mingine ya anwani. IPv6 inatarajiwa kuchukua nafasi ya IP, lakini hii itachukua miaka mingi. Ingawa tayari kuna utekelezaji wa IPv6 kwa Linux, kuijadili ni zaidi ya upeo wa kitabu hiki.

Ingawa mashine kwenye Mtandao karibu kila mara zina majina, hubadilishwa kuwa anwani za IP. Mfano wa anwani ya IP ni 192.168.1.99. Anwani zote za IP zinawakilishwa na nambari nne, ambayo kila moja ni chini ya 256, na kuunda kinachojulikana. nne zenye nukta. Wakati mteja anaunganisha kwenye mtandao kwa kutumia soketi, inahitaji anwani ya IP ya kompyuta ya seva.

Huduma kadhaa zinaweza kupatikana kwenye kompyuta ya seva. Mteja anaweza kufikia huduma maalum kwenye kompyuta iliyounganishwa kwenye mtandao kwa kutumia bandari ya IP. Ndani ya mfumo, bandari inatambuliwa na nambari ya kipekee ya biti 16, na nje ya mfumo kwa mchanganyiko wa anwani ya IP na nambari ya bandari. Soketi ni miisho ya mawasiliano ambayo lazima ihusishwe na milango kabla ya uhamishaji wa data kuwezekana.

Seva husubiri maombi ya muunganisho kutoka kwa wateja mahususi. Huduma zinazojulikana zimejitolea nambari za bandari ambazo hutumiwa na mashine zote za Linux na UNIX. Kwa kawaida, lakini si mara zote, nambari hizi huwa chini ya 1024. Mifano ni pamoja na bafa ya uchapishaji ya kichapishi (515),

(513), (21) na (80). La mwisho lililopewa jina ni lango la kawaida la seva za Wavuti. Kwa kawaida, nambari za bandari chini ya 1024 zimehifadhiwa kwa huduma za mfumo na zinaweza kuhudumiwa na michakato yenye haki za mtumiaji mkuu. Kiwango cha X/Open kinafafanua safu thabiti katika faili ya kichwa cha netdb.h ili kuonyesha idadi kubwa zaidi ya bandari zilizohifadhiwa.

Kwa sababu kuna seti ya kawaida ya nambari za bandari kwa huduma za kawaida, kompyuta zinaweza kuunganishwa kwa urahisi bila kukisia nambari sahihi ya mlango. Huduma za mitaa inaweza kutumia anwani zisizo za kawaida za bandari.

Kikoa katika zoezi la kwanza,

, ni kikoa cha mfumo wa faili cha UNIX ambacho kinaweza kutumiwa na soketi ziko kwenye kompyuta moja, labda hata kwenye mtandao. Ikiwa ndivyo, basi itifaki ya kiwango cha chini ni faili I/O, na anwani ni majina ya faili. Soketi ya seva ilitumia anwani ambayo uliona ikitokea kwenye saraka ya sasa wakati uliendesha programu ya seva.

Kwa kuongeza, vikoa vingine vinaweza kutumika:

kwa mitandao kulingana na itifaki za kawaida za ISO na Xerox Mfumo wa Mtandao(mfumo wa mtandao wa Xerox). Hatutazijadili katika kitabu hiki. Aina za soketi

Kikoa cha tundu kinaweza kuwa na njia nyingi za mawasiliano, ambayo kila moja inaweza kuwa nayo sifa tofauti. Katika kesi ya soketi za kikoa

matatizo hayatokei kwa sababu yanatoa ubadilishanaji wa data unaotegemewa wa pande mbili. Katika vikoa vya mtandao, ni muhimu kujua sifa za mtandao wa msingi na athari zao kwenye taratibu mbalimbali za uhamisho wa data.

Itifaki za mtandao hutoa njia mbili za kusambaza data na viwango tofauti vya huduma: vijito Na datagrams.

Soketi za mkondo

Soketi za kutiririsha (kwa kiasi fulani zinafanana na mitiririko ya kawaida ya I/O) hutoa muunganisho ambao ni mtiririko unaotegemewa wa baiti. Kwa hivyo, imehakikishiwa kuwa data haitapotea, kurudiwa au kupangwa upya bila kuonyesha kosa lililotokea. Ujumbe mkubwa hugawanywa, kupitishwa na kuwekwa pamoja tena. Hii ni sawa na mkondo wa faili ambao huchukua kiasi kikubwa cha data na kuigawanya katika vizuizi vidogo kwa kuandika kwenye diski halisi. Soketi za mtiririko zina tabia inayotabirika.

Soketi za mtiririko zilizoelezewa na aina

, hutekelezwa katika kikoa kwa miunganisho kulingana na itifaki za TCP/IP. Kwa kuongeza, hii ni aina ya kawaida ya tundu kwenye . Katika sura hii, tutazingatia soketi za aina, kwa vile hutumiwa mara nyingi katika programu. maombi ya mtandao.
Kumbuka

TCP/IP ni kifupi cha Itifaki ya Udhibiti wa Usambazaji/Itifaki ya Mtandao. Itifaki ya IP ni itifaki ya maambukizi ya pakiti ya kiwango cha chini ambayo hutoa uteuzi wa njia wakati wa kutuma data kwenye mtandao kutoka kwa kompyuta moja hadi nyingine. TCP hutoa kuagiza, udhibiti wa mtiririko, na usambazaji wa usambazaji ili kuhakikisha upitishaji kamili na sahihi kiasi kikubwa data au ujumbe kuhusu hali ya makosa inayolingana.

Soketi za datagram

Tofauti na soketi za datagram za utiririshaji, ambazo zinaelezewa na aina

, usianzisha au kudumisha muunganisho. Kwa kuongeza, kuna kikomo kwa ukubwa wa datagram ambayo inaweza kutumwa. Inapitishwa kama ujumbe mmoja wa mtandao, ambao unaweza kupotea, kurudiwa au kufika kwa wakati, i.e. kabla ya datagrams kutumwa baada yake.

Soketi za Datagram zinatekelezwa kwenye kikoa

kupitia miunganisho ya UDP/IP na kutoa huduma isiyo na mpangilio, isiyotegemewa. (UDP ni fupi kwa Itifaki ya Datagram ya Mtumiaji.) Hata hivyo, zina ufanisi wa rasilimali kwa sababu hazihitaji miunganisho ya mtandao. Wana haraka kwa sababu ... Hakuna muda unaopotea kusanidi muunganisho wa mtandao.

Datagramu ni muhimu kwa maombi ya mara moja kwa huduma za habari, kwa kutoa maelezo ya hali ya kawaida, au kutekeleza uhifadhi wa data uliopewa kipaumbele cha chini. Faida yao ni kwamba kusimamisha seva hakutasababisha usumbufu usiofaa kwa mteja na hautahitaji kuanzisha tena mteja. Kwa sababu seva zinazotumia datagramu kwa kawaida huhifadhi data bila muunganisho, zinaweza kusimamishwa na kuanza tena bila kuingilia wateja wao.

Hii inahitimisha mjadala wetu wa datagrams; kwa habari zaidi, ona sehemu "Datagramu" mwishoni mwa sura hii.

Itifaki za tundu

Ikiwa utaratibu wa uhamishaji data wa kiwango cha chini unaruhusu itifaki nyingi zinazotoa aina ya tundu inayohitajika, unaweza kuchagua itifaki au tundu maalum. Katika sura hii, tutazingatia mtandao wa UNIX na soketi za mfumo wa faili, ambazo hazihitaji kuchagua itifaki tofauti na chaguo-msingi.

Kutengeneza tundu

Simu ya mfumo wa tundu huunda tundu na kurudisha mpini ambayo inaweza kutumika kupata tundu:

#pamoja na
#pamoja na
tundu la int (kikoa cha int, aina ya int, itifaki ya int);

Tundu iliyoundwa ni mwisho mmoja wa mstari wa maambukizi. Kigezo

hubainisha familia ya anwani, kigezo kinabainisha aina ya ubadilishanaji wa data unaotumiwa na tundu hili, na a ndiyo itifaki inayotumika.

Katika meza 15.1 inaonyesha majina ya vikoa.


Jedwali 15.1

Vikoa maarufu vya soketi ni pamoja na

, inayotumika kwa soketi za ndani zinazotekelezwa na mifumo ya faili ya UNIX na Linux, na kutumika kwa soketi za mtandao za UNIX. Soketi za kikoa zinaweza kutumiwa na programu zinazowasiliana kupitia mitandao inayotegemea itifaki ya TCP/IP, ikijumuisha Mtandao. Kiolesura cha Windows Winsock pia hutoa ufikiaji wa kikoa hiki cha tundu.

Kigezo cha aina ya tundu kinabainisha sifa za mawasiliano zitakazotumika kwenye tundu jipya. Thamani zinazowezekana zinaweza kuwa

Na. ni mtiririko uliopangwa, unaotegemewa, unaotegemea muunganisho, unaoelekeza pande mbili wa baiti. Katika kesi ya kikoa cha tundu, aina hii ya mawasiliano kwa chaguo-msingi hutolewa na muunganisho wa TCP ambao umeanzishwa kati ya ncha mbili za tundu za mkondo wakati wa kuunganisha. Data inaweza kuhamishwa kwa njia mbili juu ya kiungo cha tundu. Itifaki za TCP ni pamoja na njia ya kugawanyika na kisha kuunganisha tena ujumbe mkubwa na kutuma tena sehemu zozote ambazo huenda zimepotea kwenye mtandao. - huduma ya datagram. Unaweza kutumia tundu kama hilo kutuma ujumbe na saizi ya juu isiyobadilika (kawaida ndogo), lakini hakuna hakikisho kwamba ujumbe utatumwa au kwamba ujumbe hautapangwa tena kwenye mtandao. Katika kesi ya soketi za kikoa, aina hii ya uhamisho wa data hutolewa na datagrams za UDP (Protocol ya Datagram ya Mtumiaji).

Itifaki inayotumika kwa mawasiliano kawaida huamuliwa na aina ya tundu na kikoa. Kama sheria, hakuna chaguo. Kigezo

inatumika katika hali ambapo chaguo bado hutolewa. Kuweka 0 hukuruhusu kuchagua itifaki ya kawaida inayotumiwa katika mifano yote katika sura hii.

Simu ya mfumo

inarudisha mpini kama kielezi cha faili cha kiwango cha chini. Wakati tundu limeunganishwa kwenye sehemu ya mwisho ya soketi nyingine, unaweza kutumia simu za mfumo kwenye mpini wa tundu kutuma na kupokea data kwa kutumia soketi. Simu ya mfumo hutumiwa kufuta muunganisho wa soketi.

Anwani za tundu

Kila kikoa cha soketi kinahitaji umbizo tofauti la anwani. Katika kikoa

anwani inaelezewa na muundo uliotangazwa katika faili ya kichwa cha sys/un.h:
sa_family_t sun_family; /* AF_UNIX */
char sun_njia; /* Njia ya faili */

Ili kwa anwani aina tofauti inaweza kupitishwa kwa simu za mfumo kusindika soketi, fomati zote za anwani zinaelezewa na muundo sawa ambao huanza na uwanja (katika kwa kesi hii

), ikibainisha aina ya anwani (kikoa cha tundu). Katika kikoa, anwani inatajwa na jina la faili katika uwanja wa muundo.

Kwenye mifumo ya kisasa ya Linux aina

, iliyofafanuliwa katika kiwango cha X/Open kama inavyotangazwa katika faili ya kichwa cha sys/un.h, inafasiriwa kama aina . Kwa kuongeza, saizi iliyoainishwa kwenye uwanja ni mdogo (kwenye Linux, herufi 108 zimeainishwa; kwenye mifumo mingine, jina la kudumu, kwa mfano, linaweza kutumika). Kwa sababu saizi ya muundo wa anwani inaweza kutofautiana, simu nyingi za mfumo wa soketi zinahitaji au kutoa kama matokeo urefu ambao utatumika kunakili muundo fulani wa anwani. anwani imebainishwa kwa kutumia muundo unaoitwa , uliofafanuliwa katika faili netinet/in.h, ambayo ina angalau vipengele vifuatavyo:
short in sin_familia; /* AF_INET */
unsigned short int sin_port; /* Nambari ya bandari */
struct in_addr sin_addr; /* Anwani ya mtandao */

Muundo wa anwani ya IP ya aina in_addr hufafanuliwa kama ifuatavyo:

unsigned long int s_addr;

Baiti nne za anwani ya IP huunda thamani moja ya 32-bit. Soketi ya kikoa

imeelezewa kabisa na anwani ya IP na nambari ya bandari. Kwa mtazamo wa programu, soketi zote hufanya kama maelezo ya faili, na anwani zao zimebainishwa na nambari kamili za kipekee.

Jina la Soketi

Kutengeneza tundu (iliyoundwa kwa kupiga simu

) kupatikana kwa michakato mingine, programu ya seva lazima ipe tundu jina. Soketi za kikoa zinahusishwa na jina la faili lililohitimu kikamilifu kwenye mfumo wa faili, kama ulivyoona kwenye programu ya mfano ya seva1. Soketi za kikoa zinahusishwa na nambari ya mlango wa IP.
#pamoja na
int bind(int socket, const struct sockaddr *anwani, size_t anwani len);

Simu ya mfumo

inapeana anwani iliyobainishwa kwenye kigezo kwa tundu lisilo na jina linalohusishwa na maelezo ya tundu. Urefu wa muundo wa anwani hupitishwa katika parameta:

Urefu na muundo wa anwani hutegemea familia ya anwani. Katika simu ya mfumo

kielekezi cha muundo fulani wa anwani lazima kitupwe kwa aina ya anwani ya jumla.

Baada ya kukamilika kwa mafanikio

inarejesha 0. Ikishindikana, -1 inarejeshwa na kutofautisha kunapewa moja ya maadili yaliyoorodheshwa kwenye jedwali. 15.2.

Jedwali 15.2

Kuunda Foleni ya Soketi

Ili kukubali maombi ya miunganisho ya tundu inayoingia, programu ya seva lazima iunde foleni ili kushikilia maombi yanayosubiri. Inaundwa kwa kutumia simu ya mfumo

.
#pamoja na
int kusikiliza (int socket, int backlog);

Linux inaweza kupunguza idadi ya miunganisho inayosubiri ambayo inaweza kushikiliwa kwenye foleni. Kulingana na simu hii ya juu

huweka urefu wa foleni kuwa . Viunganisho vinavyoingia ambavyo havizidi urefu wa juu wa foleni huhifadhiwa kusubiri tundu; maombi ya baadaye ya muunganisho yatakataliwa na jaribio la muunganisho wa mteja litashindwa. Utaratibu huu unatekelezwa na simu ili maombi ya muunganisho yanayosubiri yaweze kudumishwa wakati programu ya seva inashughulika kushughulikia ombi la mteja wa awali. Mara nyingi sana parameter ni 5. Itarudi 0 juu ya mafanikio na -1 juu ya makosa. Kama ilivyo kwa simu ya mfumo, makosa yanaweza kuonyeshwa na viunga, NA.

Inapokea maombi ya muunganisho

Baada ya kuunda na kutaja tundu, programu ya seva inaweza kusubiri maombi ya kuunganisha kwenye tundu kwa kutumia simu ya mfumo.

:
#pamoja na
int accept(int socket, struct sockaddr *anwani, size_t *address_len);

Simu ya mfumo

inarejesha udhibiti wakati programu ya mteja inajaribu kuunganisha kwenye tundu iliyoainishwa kwenye parameta. Mteja huyu ndiye wa kwanza anayesubiri muunganisho kwenye foleni ya soketi hii. Kazi huunda tundu mpya kwa mawasiliano na mteja na inarudisha kushughulikia kwake. Soketi mpya itakuwa aina sawa na usikilizaji wa soketi ya seva kwa maombi ya muunganisho.

Soketi lazima kwanza ipewe jina kwa kutumia simu ya mfumo

na lazima iwe na foleni ya maombi ya uunganisho, nafasi ambayo imetengwa na simu ya mfumo. Anwani ya mteja anayepiga itawekwa kwenye muundo ulioelekezwa na kigezo. Ikiwa anwani ya mteja haipendezi, kigezo hiki kinaweza kuwekwa kuwa kielekezi tupu.

Kigezo

hubainisha urefu wa muundo wa anwani ya mteja. Ikiwa anwani ya mteja ni ndefu kuliko thamani hii, itapunguzwa. Kabla ya kupiga simu, urefu wa anwani unaotarajiwa lazima ubainishwe kwenye parameta. Baada ya kurudi kutoka kwa simu, urefu halisi wa muundo wa anwani ya mteja anayeomba uunganisho utaanzishwa.

Ikiwa hakuna maombi ya uunganisho yanayosubiri kwenye foleni ya tundu, simu ya kukubali itazuia (kwa hivyo programu haiwezi kuendelea kutekeleza) hadi mteja afanye ombi la uunganisho. Unaweza kubadilisha tabia hii kwa kutumia bendera

kwenye maelezo ya faili ya tundu kwa kuiita katika programu yako kama hii:
bendera za int = fcntl(tundu, F_GETFL, 0);
fcntl(tundu, F_SETFL, O_NONBLOCK | bendera);
inarudisha kielezi kipya cha faili ya tundu ikiwa kuna ombi la mteja linalosubiri muunganisho, na -1 ikiwa kuna hitilafu. Thamani zinazowezekana za hitilafu ni sawa na za simu pamoja na ya ziada mara kwa mara katika hali ambapo bendera imewekwa na hakuna maombi ya muunganisho yanayosubiri. Hitilafu itatokea ikiwa mchakato umeingiliwa wakati wa kuzuia katika chaguo la kukokotoa.

Maombi ya muunganisho

Programu za mteja huunganisha kwenye seva kwa kuanzisha muunganisho kati ya tundu lisilo na jina na soketi ya seva inayosikiliza miunganisho. Wanafanya hivyo kwa kupiga simu

:
#pamoja na
int connect(int socket, const struct sockaddr *anwani, size_t address_len);

Soketi iliyoainishwa kwenye parameta

, inaunganisha kwenye tundu la seva iliyotajwa kwenye parameter, ambayo urefu wake ni sawa na. Soketi lazima ibainishwe na kielezi halali cha faili kilichopatikana kutoka kwa simu ya mfumo.

Ikiwa kazi

inakamilika kwa mafanikio, inarudi 0, ikiwa ni kosa itarudi -1. Hitilafu zinazowezekana wakati huu ni pamoja na maadili yaliyoorodheshwa kwenye jedwali. 15.3.

Jedwali 15.3

Ikiwa muunganisho hauwezi kuanzishwa mara moja, piga simu

itazuiwa kwa muda usiojulikana wa kusubiri. Muda unaoruhusiwa ukipitwa, muunganisho utakatishwa na simu itakatika isivyo kawaida. Walakini, ikiwa simu itakatizwa na ishara inayochakatwa, unganisho utashindwa (na errno set to ) lakini jaribio la uunganisho halitasitishwa - muunganisho utaanzishwa kwa usawa na programu italazimika kuangalia baadaye ili kuona ikiwa ilianzishwa kwa mafanikio.

Kama katika kesi ya simu

, uwezekano wa kuzuia katika simu inaweza kutengwa kwa kuweka bendera katika maelezo ya faili. Katika kesi hii, ikiwa uunganisho hauwezi kuanzishwa mara moja, simu itashindwa na kutofautiana sawa na , na uunganisho utafanywa asynchronously.

Ingawa miunganisho ya asynchronous ni ngumu kushughulikia, unaweza kutumia simu

kwa maelezo ya faili ya tundu ili kuhakikisha kuwa tundu liko tayari kuandikwa. Tutajadili changamoto hiyo baadaye kidogo katika sura hii.

Kufunga tundu

Unaweza kusitisha muunganisho wa tundu kwenye seva au programu ya mteja kwa kupiga simu kitendakazi

, sawa na katika kesi ya maelezo ya faili ya kiwango cha chini. Soketi zinapaswa kufungwa kwa ncha zote mbili. Kwenye seva, hii inapaswa kufanywa wakati inarudi sifuri. Fahamu kuwa simu inaweza kuzuiwa ikiwa soketi ambayo ina data ambayo haijatumwa ni ya aina inayolenga muunganisho na . Utajifunza zaidi kuhusu kuweka chaguo za soketi baadaye katika sura hii.

Mawasiliano kwa kutumia soketi

Sasa kwa kuwa tumeelezea simu za msingi za mfumo zinazohusiana na soketi, hebu tuangalie kwa karibu programu za mfano. Utajaribu kuzifanyia kazi upya kwa kubadilisha tundu la mfumo wa faili na tundu la mtandao. Ubaya wa tundu la mfumo wa faili ni kwamba ikiwa mwandishi hatumii jina kamili la faili, imeundwa kwenye saraka ya sasa ya programu ya seva. Ili kuifanya iwe muhimu katika hali nyingi, unapaswa kuunda tundu kwenye saraka ya umma (kama vile /tmp) inayofaa kwa seva na wateja wake. Katika kesi ya seva za mtandao, inatosha kuchagua nambari ya bandari isiyotumiwa.

Kwa mfano, chagua nambari ya mlango 9734. Hili ni chaguo la kiholela ili kuepuka kutumia bandari za huduma za kawaida (hupaswi kutumia nambari za bandari zilizo chini ya 1024 kwa sababu zimehifadhiwa kwa matumizi ya mfumo). Nambari zingine za bandari na huduma wanazotoa mara nyingi zimeorodheshwa kwenye faili ya mfumo /etc/services. Wakati wa kuandika programu zinazotumia soketi, daima chagua nambari ya bandari ambayo haipo kwenye faili hii ya usanidi.

Kumbuka

Unapaswa kufahamu kwamba kulikuwa na hitilafu ya kimakusudi katika programu za client2.c na server2.c, ambazo utarekebisha katika programu za client3.c na server3.c. Tafadhali usitumie maandishi ya mifano ya client2.c na server2.c katika programu zako mwenyewe.

Utakuwa unaendesha seva yako na programu za mteja kwenye mtandao wa ndani, lakini soketi za mtandao ni muhimu sio tu kwenye mtandao wa ndani; mashine yoyote iliyo na muunganisho wa Mtandao (hata laini ya kupiga simu) inaweza kutumia soketi za mtandao kuwasiliana na kompyuta zingine. . Mpango kulingana na miunganisho ya mtandao, inaweza kutumika hata kwenye kompyuta iliyotengwa ya UNIX, kwani kompyuta kama hiyo kawaida husanidiwa kutumia mtandao wa kawaida au mtandao wa loopback wa ndani unaojumuisha yenyewe pekee. Kwa madhumuni ya onyesho, mfano huu unatumia mtandao pepe, ambao unaweza pia kuwa muhimu kwa utatuzi wa programu za mtandao kwa sababu unaondoa yoyote ya nje. matatizo ya mtandao.

Mtandao wa mtandaoni una kompyuta moja, inayoitwa jadi

, yenye anwani ya IP ya kawaida ya 127.0.0.1. Hii ni mashine ya ndani. Unaweza kupata anwani yake katika faili ya nodi za mtandao nk/wenyeji, pamoja na majina na anwani za nodi zingine zilizojumuishwa kwenye mitandao iliyoshirikiwa.

Kila mtandao ambao kompyuta huwasiliana nao una kiolesura cha maunzi kinachohusishwa nayo. Kompyuta kwenye kila mtandao inaweza kuwa na jina lake na, bila shaka, itakuwa na anwani tofauti za IP. Kwa mfano, mashine ya Neil inayoitwa tilde ina violesura vitatu vya mtandao na kwa hivyo anwani tatu. Zimeandikwa katika /etc/hosts faili kama ifuatavyo.

127.0.0.1 mwenyeji # Kitanzi
192.168.1.1 tilde.localnet # Mtandao wa ndani wa Ethaneti ya kibinafsi
158.152.X.X tilde.demon.co.uk # Laini ya mawasiliano ya Modem

Mstari wa kwanza ni mfano wa mtandao wa kawaida, mtandao wa pili unapatikana kwa kutumia adapta ya Ethernet, na ya tatu ni kiungo cha modem kwa mtoa huduma wa mtandao. Unaweza kuandika programu inayotumia soketi za mtandao kuwasiliana na seva kwa kutumia kiolesura chochote kilichotolewa bila marekebisho yoyote.

Mazoezi kamili 15.3 na 15.4.

Zoezi 15.3. Mteja wa mtandao

Ifuatayo ni programu iliyorekebishwa ya mteja2.c iliyoundwa kutumia muunganisho wa mtandao wa soketi katika mtandao pepe. Ina hitilafu ndogo ya utegemezi wa maunzi, lakini tutaijadili baadaye katika sura hii.

1. Jumuisha maagizo muhimu

na kuweka vigezo:
#pamoja na
#pamoja na
tengeneza sockaddr_in anwani;

2. Unda soketi ya mteja:

3. Weka jina kwenye soketi kama ilivyokubaliwa na seva:

address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = 9734;

Programu iliyosalia ni sawa na mfano uliotolewa mapema katika sura hii. Unapoendesha toleo hili, litaanguka kwa sababu hakuna seva inayoendesha kwenye port 9734 kwenye kompyuta hii.

Lo!: mteja2: Muunganisho umekataliwa

Inavyofanya kazi

Mpango wa mteja hutumia muundo

kutoka kwa faili ya kichwa netinet/in.h kuweka anwani. Anajaribu kuunganisha kwa seva inayopangishwa na seva pangishi iliyo na anwani ya IP ya 127.0.0.1. Programu hutumia kazi kubadilisha uwakilishi wa maandishi wa anwani ya IP kuwa fomu inayofaa kwa kushughulikia tundu. Tazama kurasa za usaidizi mtandaoni kwa inet kwa maelezo zaidi kuhusu vipengele vingine vya kutafsiri anwani. Zoezi 15.4. Seva ya mtandao

Pia unahitaji kurekebisha programu ya seva inayosikiliza miunganisho kwenye nambari ya bandari unayochagua. Ifuatayo ni programu iliyosahihishwa ya seva server2.c.

1. Ingiza faili muhimu za kichwa na uweke vigeuzo:

#pamoja na
#pamoja na

int server_sockfd, mteja_sockfd;

2. Unda soketi isiyo na jina ya seva:

3. Ipe tundu jina:

server_address.sin_port.s_addr = inet_addr("127.0.0.1");
server_address.sin_port = 9734;
server_len = sizeof(server_anwani);
bind(server_sockfd, (struct sockaddr *)&server_anwani, server_len);

Inavyofanya kazi

Programu ya seva huunda tundu la kikoa

na hufanya vitendo vinavyohitajika ili kukubali maombi ya kuunganishwa nayo. Soketi huwasiliana na bandari uliyochagua. Anwani iliyotolewa huamua ni mashine zipi zinazoruhusiwa kuunganishwa. Kwa kubainisha anwani ya mtandao pepe sawa na katika programu ya mteja, unaweka kikomo miunganisho kwenye mashine ya ndani pekee.

Ikiwa unataka kuruhusu seva kuanzisha miunganisho na wateja wa mbali, lazima ubainishe seti ya anwani za IP zinazoruhusiwa. Unaweza kutumia thamani maalum

ili kuonyesha kuwa utakubali maombi ya muunganisho kutoka kwa violesura vyote vinavyopatikana kwenye kompyuta yako. Ikihitajika, unaweza kuweka mipaka ya miingiliano kwenye mitandao tofauti ili kutenganisha miunganisho ya LAN kutoka kwa miunganisho ya WAN. Sahihi ni nambari kamili ya biti 32 inayoweza kutumika katika uga wa muundo wa anwani. Lakini kwanza unahitaji kutatua tatizo.

Agizo la Byte kwenye kompyuta na kwenye mtandao

Ikiwa unaendesha matoleo uliyopewa ya seva na programu za mteja kwenye mashine kulingana na kichakataji cha Intel kinachoendesha Linux, kisha ukitumia amri.

unaweza kuona miunganisho ya mtandao. Amri hii imejumuishwa katika mifumo mingi ya UNIX iliyosanidiwa kwa mitandao. Inaonyesha muunganisho wa seva ya mteja unaosubiri kufungwa. Uunganisho umefungwa baada ya kuchelewa kwa muda mfupi. (Tena, matokeo yanaweza kutofautiana kati ya matoleo ya Linux.)
$ ./server2 & ./client2
Miunganisho inayotumika ya Mtandao (seva za w/o)
tcp 1 0 localhost:1574 localhost:1174 TIME_WAIT mzizi
Kumbuka

Kabla ya kujaribu mifano ifuatayo katika sura hii, hakikisha kuwa umemaliza kuendesha programu za mifano ya upande wa seva kwa sababu zitashindana kwa miunganisho ya mteja na utaona matokeo ya kupotosha. Unaweza kuziondoa zote (pamoja na zile zilizoorodheshwa baadaye katika sura hii) kwa amri ifuatayo:

killall server1 server2 server3 server4 server5

Utaweza kuona nambari za mlango zilizopewa muunganisho wa mteja wa seva. Anwani ya ndani inawakilisha seva, na anwani ya nje inawakilisha mteja wa mbali. (Hata kama mteja amepangishwa kwenye mashine hiyo hiyo, bado inaunganishwa kwenye mtandao.) Ili kuweka soketi zote tofauti, milango ya mteja kwa kawaida ni tofauti na usikilizaji wa soketi ya seva kwa maombi ya muunganisho na ni ya kipekee ndani ya kompyuta.

Inaonyesha anwani ya ndani (tundu la seva) 1574 (au inaweza kuonyesha jina la huduma

) na bandari iliyochaguliwa kwa mfano ni 9734. Kwa nini ni tofauti? Ukweli ni kwamba nambari za bandari na anwani hupitishwa kupitia miingiliano ya tundu kama nambari za binary. Kompyuta tofauti hutumia mpangilio tofauti wa baiti kuwakilisha nambari kamili. Kwa mfano, Kichakataji cha Intel huhifadhi nambari kamili ya biti 32 kama baiti nne mfululizo za kumbukumbu katika mpangilio ufuatao 1-2-3-4, ambapo baiti ya 1 ndiyo muhimu zaidi. Vichakataji vya IBM PowerPC vitahifadhi nambari kamili kwa mpangilio wa baiti ufuatao: 4-3-2-1. Ikiwa kumbukumbu inayotumika kuhifadhi nambari kamili inanakiliwa kwa baiti, kompyuta hizo mbili hazitakubaliana juu ya nambari kamili.

Ili aina tofauti za kompyuta zikubaliane juu ya maana ya nambari za multibyte zilizotumwa kwenye mtandao, ni muhimu kuamua utaratibu wa byte wa mtandao. Kabla ya kutuma data, programu za mteja na seva lazima zibadilishe uwakilishi wao wa ndani wa nambari kamili hadi mwisho wa mtandao. Hii inafanywa kwa kutumia vitendaji vilivyofafanuliwa katika faili ya kichwa cha netinet/in.h. Hizi ni pamoja na zifuatazo:

#pamoja na
haijatiwa saini kwa muda mrefu int htonl(haijasainiwa kwa muda mrefu int hoslong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);

Vitendaji hivi hubadilisha nambari kamili za biti 16 na 32 kutoka mpangilio asili hadi wa mtandao na kinyume chake. Majina yao yanahusiana na jina lililofupishwa la mabadiliko yaliyofanywa, kwa mfano, "mwenyeji kwa mtandao, mrefu" (htonl, kompyuta hadi mtandao, nambari ndefu) na "mwenyeji kwa mtandao, mfupi" (htons, kompyuta hadi mtandao, nambari fupi) . Kwa kompyuta zilizo na mpangilio wa byte wa mtandao, vitendaji hivi hutoa shughuli tupu.

Ili kuhakikisha uagizaji sahihi wakati wa kupitisha nambari kamili ya biti 16, seva yako na mteja lazima watumie vipengele hivi kwenye anwani ya mlango. Mabadiliko yafuatayo yanapaswa kufanywa kwa programu ya seva3.c:

server_address.sin_addr_s_addr = htonl(INADDR_ANY);

Matokeo yamerejeshwa na chaguo la kukokotoa

, hakuna haja ya kubadilisha kwa sababu, kwa ufafanuzi, inarudi matokeo katika utaratibu wa byte wa mtandao. Mabadiliko yafuatayo yanahitajika kufanywa kwa programu ya client3.c:
address.sin_port = htons(9734);

Kwa seva, shukrani kwa matumizi ya mara kwa mara

, mabadiliko yamefanywa ili kuruhusu maombi ya muunganisho kukubaliwa kutoka kwa anwani yoyote ya IP.

Sasa, kwa kutekeleza programu za seva3 na mteja3, utaona nambari sahihi ya bandari inayotumiwa uhusiano wa ndani:

Proto Recv-Q Send-Q Anwani ya Ndani ya Mtumiaji Anwani ya Kigeni (Jimbo).
tcp 1 0 localhost:9734 localhost:1175 TIME_WAIT mzizi
Kumbuka

Ikiwa unatumia kompyuta ambayo ina umbizo mwenyewe Uwakilishi kamili ni sawa na mpangilio wa byte wa mtandao, hutaona tofauti yoyote. Lakini ili kuhakikisha mwingiliano sahihi kati ya wateja na seva na usanifu tofauti, ni muhimu kutumia daima kazi za uongofu.

Taarifa za mtandao

Hadi sasa, programu za mteja na seva zilikuwa na anwani na nambari za bandari zilizokusanywa ndani yao. Katika programu za jumla zaidi za seva na mteja, unaweza kutumia data ya mtandao ili kubaini ni anwani na bandari zipi zitatumika.

Ikiwa una ruhusa ya kufanya hivyo, unaweza kuongeza seva yako kwenye orodha ya huduma zinazojulikana katika faili ya /etc/services, ambayo hutoa majina kwa nambari za bandari ili wateja waweze kutumia majina ya huduma ya ishara badala ya nambari.

Kwa njia hiyo hiyo, kujua jina la kompyuta, unaweza kuamua anwani ya IP kwa kupiga kazi za database za mwenyeji ambazo zitapata anwani hizi. Wanafanya hivyo kwa kushauriana na faili za usanidi kama vile n.k/wapangishi au huduma za taarifa za mtandao kama vile NIS (Huduma za Taarifa za Mtandao, ambazo zamani zilijulikana kama Kurasa za Manjano) na DNS ( Jina la Kikoa Huduma, huduma ya jina la kikoa).

Vitendo vya hifadhidata vya Mwenyeji vinatangazwa katika faili ya kichwa cha kiolesura cha netdb.h:

struct hostnti *gethostbyaddr(const void* addr, size_t len, int type);
struct hosting* gethostbyname(const char* name);

Muundo uliorejeshwa na kazi hizi lazima, angalau, uwe na vipengele vifuatavyo.

char *h_name; /* Jina la nodi */
char **h_aliases; /* Orodha ya majina ya utani */
int h_addtype; /* Aina ya anwani */
int h_length; /* Urefu wa anwani katika baiti */
char **h_addr_list /* Orodha ya anwani (agizo la baiti ya mtandao) */

Ikiwa hakuna ingizo katika hifadhidata linalolingana na seva pangishi au anwani, vipengele vya kukokotoa vya taarifa vitarejesha kielekezi batili.

Vile vile, taarifa kuhusu huduma na nambari za bandari zinazohusiana zinaweza kupatikana kwa kutumia vipengele vya habari vya huduma:

muundo wa seva *getservbyname(const char *name, const char *proto);
struct seva *getservbyport(int port, const char *proto);

Kigezo

hubainisha itifaki ambayo itatumika kuunganisha kwenye huduma, ama "tcp" kwa miunganisho ya TCP ya aina , au "udp" kwa datagramu za UDP za aina .

Muundo

ina angalau vipengele vifuatavyo:
char *s_name; /* Jina la huduma */
char **s_aliases; /* Orodha ya lakabu (majina ya ziada) */
int s_bandari; /* Nambari ya bandari ya IP */
char *s_proto; /* Aina ya huduma, kawaida "tcp" au "udp" */

Unaweza kuunganisha maelezo ya kompyuta kutoka kwa hifadhidata ya mwenyeji kwa kupiga chaguo la kukokotoa

na kuonyesha matokeo yake. Kumbuka kwamba anwani lazima igeuzwe kuwa aina inayofaa na uende kutoka kwa mgongano wa mtandao hadi kwa mfuatano unaoweza kuchapishwa kwa kutumia ubadilishaji uliofafanuliwa kama ifuatavyo:
#pamoja na
char *inet_ntoa(struct in_addr in);

Chaguo za kukokotoa hubadilisha anwani ya nodi ya Mtandao kuwa mfuatano wa umbizo la nukta nne. Inarudi -1 kwa makosa, lakini kiwango cha POSIX hakifafanui makosa maalum. Kipengele kingine kipya utakachotumia ni

:
int gethostname(char *jina, urefu wa jina la int);

Chaguo hili la kukokotoa linaandika jina la nodi ya sasa kwa mfuatano uliobainishwa na kigezo

. Jina la nodi litakuwa mfuatano uliokatishwa batili. Hoja ina urefu wa jina la mfuatano na ikiwa jina la nodi iliyorejeshwa itazidi urefu huu itapunguzwa. Chaguo la kukokotoa linarudisha 0 kwenye mafanikio na -1 kwenye makosa. Tena, mende hazijafafanuliwa katika kiwango cha POSIX.

Fanya Mazoezi 15.5.

Zoezi 15.5. Taarifa za mtandao

Mpango huu getname.c hupata taarifa kuhusu kompyuta.

1. Kama kawaida, ingiza faili za kichwa zinazofaa na utangaze vigeu:


char *mwenyeji, **majina, ** addrs;

2. Wape kigezo

thamani ya hoja iliyotolewa wakati wa kupiga programu, au kwa chaguo-msingi jina la mashine ya mtumiaji:

3. Piga simu kitendakazi cha gethostbyname na uripoti hitilafu ikiwa hakuna taarifa inayopatikana:

fprintf(stderr, "haiwezi kupata maelezo ya mwenyeji: %s\n", mwenyeji);

4. Onyesha jina la nodi na lakabu zozote zinazoweza kuwa nazo:

printf("matokeo ya mwenyeji %s:\n", mwenyeji);
printf("Jina: %s\n", hostinfo->h_name);
printf("%s", *majina); majina++;

5. Ikiwa nodi iliyoombwa sio nodi ya IP, ripoti hii na ukamilishe utekelezaji:

ikiwa (hostinfo->h_addrtype != AF_INET) (
fprintf(stderr, "si mwenyeji wa IP!\n");

6. Vinginevyo, chapisha anwani ya IP:

addrs = hostinfo->h_addr_list;
printf("%s", inet_ntoa(*(muundo katika_addr*)*adds));

Kuamua mwenyeji kwa anwani fulani ya IP, unaweza kutumia kazi

. Unaweza kuitumia kwenye seva ili kujua ni wapi mteja anaomba muunganisho kutoka.

Inavyofanya kazi

Mpango wa getname huita kitendakazi cha gethostbyname ili kupata maelezo ya mwenyeji kutoka kwa hifadhidata ya mwenyeji. Inaonyesha jina la kompyuta, lakabu zake (majina mengine ambayo kompyuta inajulikana), na anwani za IP inazotumia kwenye violesura vyake vya mtandao. Kwenye moja ya mashine za waandishi, kuendesha mfano na kubainisha jina tilde kama hoja ilisababisha matokeo ya miingiliano miwili: mtandao wa Ethernet na laini ya mawasiliano ya modemu.

Wakati wa kutumia jina la mwenyeji

, mtandao pepe umewekwa:

Sasa unaweza kurekebisha programu yako ya mteja ili kuunganisha kwa seva pangishi yoyote iliyotajwa kwenye mtandao. Badala ya kuunganisha kwa seva katika mfano wako, utaunganisha kwa huduma ya kawaida na utaweza kurejesha nambari ya bandari.

Mifumo mingi ya UNIX na baadhi ya mifumo ya uendeshaji ya Linux hufanya mfumo wao wa saa na tarehe kupatikana kama huduma ya kawaida inayoitwa

. Wateja wanaweza kuunganisha kwenye huduma hii ili kujua seva inafikiria nini kuhusu saa na tarehe ya sasa. Zoezi la 15:6 linaonyesha programu ya mteja, getdate.c, ambayo hufanya hivyo. Zoezi 15.6. Muunganisho kwa huduma ya kawaida

1. Anza na maagizo ya kawaida

na matangazo:
int main(int argc, char *argv) (

2. Tafuta anwani ya mwenyeji na uripoti hitilafu ikiwa anwani haipatikani:

hostinfo = gethostbyname(mwenyeji);

3. Hakikisha kuna huduma kwenye kompyuta yako

:
servinfo = getservbyname("mchana", "tcp");
printf("bandari ya mchana ni %d\n", ntohs(servinfo->s_port));

4. Unda tundu:

sockfd = soketi(AF_INET, SOCK_STREAM, 0);

5. Unda anwani ya muunganisho:

address.sin_family = AF_INET;
address.sin_port = servinfo->s_port;
address.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list;

6. Kisha unganisha na upate habari:

matokeo = unganisha(sockfd, (muundo sockaddr *)&anwani, len);
result = soma(sockfd, buffer, sizeof(bafa));

Unaweza kutumia programu

kupata muda wa siku kutoka kwa nodi yoyote ya mtandao inayojulikana.
soma baiti 26: 24 JUN 2007 06:03:03 BST

Ukipokea ujumbe wa makosa kama vile

Lo: tarehe ya kupata: Muunganisho umekataliwa
oops: getdate: Hakuna faili kama hiyo au saraka

sababu inaweza kuwa kwamba huduma haijawezeshwa kwenye kompyuta unayounganisha

. Tabia hii imekuwa kawaida kwenye mifumo mingi ya kisasa ya Linux. Katika sehemu inayofuata, utaona jinsi ya kuwezesha huduma hii na zingine.

Inavyofanya kazi

Unapoendesha programu hii, unaweza kubainisha mwenyeji ili kuunganisha. Nambari ya bandari ya huduma

inavyofafanuliwa na kazi ya hifadhidata ya mtandao, ambayo inarudisha habari kuhusu huduma za mtandao kwa njia sawa na wakati wa kupata habari kuhusu node ya mtandao. Programu inajaribu kuunganisha kwa anwani ambayo imeorodheshwa kwanza kwenye orodha ya anwani za ziada za mwenyeji aliyebainishwa. Ikiwa uunganisho umefanikiwa, programu inasoma habari iliyorejeshwa na huduma ya mchana, kamba ya tabia iliyo na tarehe na wakati wa mfumo.

Daemon ya mtandao (xinetd/inetd)

Mifumo ya UNIX ambayo hutoa idadi ya huduma za mtandao mara nyingi hufanya hivyo kwa kutumia msimamizi. Kipindi hiki (Internet daemon xinetd au inetd) husikiliza kwa wakati mmoja maombi ya muunganisho yenye anwani nyingi za poti. Wakati mteja anaunganisha kwa huduma, programu ya daemon huanza seva inayolingana. Kwa mbinu hii, seva hazihitaji kufanya kazi kila mara; zinaweza kuzinduliwa kwa mahitaji.

Kumbuka

Katika mifumo ya kisasa ya Linux, jukumu la daemon ya mtandao linachezwa na programu ya xinetd. Ilichukua nafasi ya programu asilia ya UNIX inetd, ambayo bado unaweza kuipata katika matoleo ya hivi karibuni zaidi. mifumo ya mapema Linux na mifumo mingine kama UNIX.

Programu ya xinetd kawaida husanidiwa kwa kutumia kiolesura cha picha ili kudhibiti huduma za mtandao, lakini pia unaweza kuhariri faili za usanidi za programu moja kwa moja. Hizi ni pamoja na /etc/xinetd.conf faili na faili katika saraka ya /etc/xinetd.d.

Kila huduma inayotolewa na xinetd ina faili ya usanidi katika saraka ya /etc/xinetd.d. xinetd husoma faili hizi zote za usanidi wakati wa kuanzisha na tena inapopokea amri inayofaa.

.
# Chaguomsingi: imezimwa
# Maelezo: seva ya mchana. Hili ni toleo la tcp.

Faili ifuatayo ya usanidi ni ya huduma ya kuhamisha faili.

# Chaguomsingi: imezimwa
# Seva ya FTP vsftpd inashughulikia miunganisho ya FTP. Anatumia
# kwa uthibitishaji, majina ya watumiaji ambayo hayajasimbwa na
Nenosiri #, vsftpd imeundwa kuwa salama.
# Kumbuka: Faili hii ina usanidi wa kuanzisha vsftpd kwa xinetd.
# Faili ya usanidi ya programu ya vsftpd yenyewe iko ndani
# log_on_success += DURATION USERID
Soketi ambayo programu inaunganishwa kwa kawaida hushughulikiwa na programu yenyewe ya xinetd (imewekwa alama ya ndani) na inaweza kuwashwa kwa kutumia aina ya soketi (tcp) au aina ya soketi (udp).

Huduma ya kuhamisha faili

inaunganisha tu na soketi za aina na hutolewa na programu ya nje, katika kesi hii vsftpd. Daemon itaendesha programu hii ya nje wakati mteja ataunganisha kwenye port .

Ili kuwezesha mabadiliko ya usanidi wa huduma, unaweza kuhariri usanidi wa xinetd na kutuma ishara ya kukata simu kwenye mchakato wa daemon, lakini tunapendekeza kutumia njia rafiki kusanidi huduma. Ili kuruhusu mteja wako kuunganisha kwenye huduma

, Wezesha huduma hii kwa kutumia zana zinazotolewa na mfumo wa Linux. Kwenye mifumo ya SUSE na openSUSE, huduma zinaweza kusanidiwa kutoka kwa SUSE Kituo cha Kudhibiti(Kituo cha Kudhibiti cha SUSE) kama inavyoonyeshwa kwenye Mtini. 15.1. Matoleo Kofia Nyekundu(zote Enterprise Linux na Fedora) zina kiolesura cha usanidi sawa. Inawezesha huduma kwa maombi ya TCP na UDP.

Mchele. 15.1


Kwa mifumo inayotumia inetd badala ya xinetd, ifuatayo ni dondoo sawa kutoka kwa faili ya usanidi ya inetd, /etc/inetd.conf, ambayo inetd hutumia kuamua wakati wa kuanzisha seva:

#
# Echo, tupa, mchana na chaji hutumiwa sana
mkondo wa mchana tcp nowait mzizi wa ndani
mchana dgram udp kusubiri mzizi ndani
# Hizi ni huduma za kawaida.
ftp mkondo tcp-nowait mzizi /usr/sbin/tcpd /usr/sbin/wu.ftpd
telnet mkondo tcp nowait mzizi /usr/sbin/tcpd /usr/sbin/in.telnetd
# Mwisho wa faili ya inetd.conf.

Kumbuka kuwa katika mfano wetu, huduma ya ftp inatolewa na programu ya nje wu.ftpd. Ikiwa mfumo wako unatumia daemon ya inetd, unaweza kubadilisha seti ya huduma zinazotolewa kwa kuhariri faili ya /etc/inetd.conf (a # mwanzoni mwa mstari inaonyesha kuwa hii ni mstari wa maoni) na kuanzisha upya mchakato wa inetd. Hii inaweza kufanyika kwa kutuma ishara ya hang-up kwa kutumia amri

. Ili kurahisisha mchakato huu, baadhi ya mifumo imesanidiwa kuwa na programu ya inetd kuandika kitambulisho chake kwa faili. Vinginevyo, unaweza kutumia amri:

Chaguzi za tundu

Kuna chaguo nyingi ambazo zinaweza kutumika kudhibiti tabia ya miunganisho ya soketi - nyingi sana kuelezea kwa undani katika sura hii. Ili kudhibiti vigezo tumia chaguo la kukokotoa

:
#pamoja na
int setsockopt(tundu la int, kiwango cha int, int option_name,
const void * chaguo thamani, size_t chaguo len);

Unaweza kuweka vigezo katika viwango tofauti vya uongozi wa itifaki. Ili kuweka chaguo kwenye ngazi ya tundu lazima ueleze

sawa Ili kuweka vigezo kwenye kiwango cha chini cha itifaki (TCP, UDP, nk), weka kigezo cha kiwango kwa nambari ya itifaki (iliyopatikana ama kutoka kwa faili ya kichwa cha netinet/in.h au kutoka kwa kazi ).

Katika hoja

jina la kigezo kinachobainishwa limeonyeshwa; hoja ina thamani ya urefu wa baiti isiyobadilika ambayo hupitishwa bila kubadilishwa kwa kidhibiti cha itifaki cha kiwango cha chini.

Vigezo vya kiwango cha tundu vinafafanuliwa katika faili ya kichwa cha sys/socket.h na inajumuisha vile vilivyoonyeshwa kwenye Jedwali. 15.4 maadili.


Jedwali 15.5

Chaguo

na uchukue thamani kamili kwa kuweka au kuwezesha (1) na uweke upya au uzime (0). Kigezo kinahitaji muundo wa aina , iliyofafanuliwa katika faili sys/socket.h, ambayo inabainisha hali ya kigezo na thamani ya muda wa kuchelewa. inarudisha 0 ikiwa imefanikiwa na -1 vinginevyo. Kurasa za mwongozo wa usaidizi mtandaoni zinaelezea Chaguzi za ziada na makosa.

Wateja Wengi

Kufikia sasa katika sura hii, umeona jinsi soketi hutumiwa kutekeleza mifumo ya seva ya mteja, ya ndani na inayofanya kazi kwenye mtandao. Mara tu muunganisho wa msingi wa tundu unapoanzishwa, hufanya kama maelezo ya faili wazi ya kiwango cha chini na kama bomba za kuelekeza pande mbili.

Sasa tunahitaji kuzingatia kesi ya wateja wengi kuunganisha kwa seva wakati huo huo. Umeona kwamba wakati programu ya seva inapokea ombi la uunganisho kutoka kwa mteja, tundu jipya linaundwa, na tundu la awali la kusikiliza maombi ya uunganisho linaendelea kupatikana kwa maombi yanayofuata. Ikiwa seva haiwezi kukubali mara moja maombi ya muunganisho ya baadaye, yatasalia kwenye foleni ya kusubiri.

Ukweli kwamba soketi asili bado inaweza kufikiwa, na kwamba soketi hufanya kama maelezo ya faili, hutupatia mbinu ya kuwahudumia wateja wengi kwa wakati mmoja. Ikiwa seva itaita kitendakazi

ili kuunda nakala ya pili yenyewe, tundu wazi litarithiwa na mchakato mpya wa mtoto. Kisha itaweza kubadilishana data na mteja aliyeunganishwa, huku seva kuu itaendelea kukubali maombi ya uunganisho yanayofuata. Kwa kweli, unahitaji kufanya mabadiliko rahisi sana kwa programu yako ya seva, kama inavyoonyeshwa katika Zoezi 15.7.

Kwa kuwa unaunda michakato ya watoto lakini haungojei ikamilike, unapaswa kufanya seva ipuuze mawimbi

, kuzuia kuibuka kwa michakato ya zombie. Zoezi 15.7. Seva kwa wateja wengi

1. Programu ya seva4.c huanza kwa njia sawa na seva ya mwisho iliyojadiliwa na nyongeza muhimu ya maagizo.

kwa ishara ya faili ya kichwa.h. Vigezo na taratibu za kuunda na kutaja tundu zinabaki sawa:
int server_sockfd, mteja_sockfd;
tengeneza sockaddr_in server_anwani;
tengeneza sockaddr_in mteja_anwani;
server_sockfd = tundu(AF_INET, SOCK_STREAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_port = htons(9734);
server_len = sizeof(server_anwani);
bind(server_sockfd, (struct sockaddr *)&server_anwani, server_len);

2. Unda foleni ya muunganisho, puuza maelezo ya kusimamisha mchakato wa mtoto, na usubiri maombi ya mteja:

sikiliza(server_sockfd, 5);
ishara(SIGCHLD, SIG_IGN);
printf("seva inasubiri\n");

3. Kubali ombi la muunganisho:

mteja_len = sizeof(anwani_ya_mteja);
client_sockfd = kubali(server_sockfd,
(struct_sockaddr*)&mteja_anwani, &client_len);

4. Piga simu

kuunda mchakato kwa mteja fulani na kufanya ukaguzi ili kubaini kama wewe ni mzazi au mtoto: . Kuchelewa kwa sekunde tano kunahitajika ili kuonyesha hii:
soma(mteja_sockfd, &ch, 1);
andika(mteja_sockfd, &ch, 1);
funga(mteja_sockfd);

6. Vinginevyo, lazima uwe mzazi na kazi yako na mteja huyu imekamilika:

funga (tundu_la_mteja);

Nambari hii inajumuisha kucheleweshwa kwa sekunde tano wakati wa kuchakata ombi la mteja ili kuiga ukokotoaji wa seva au ufikiaji wa hifadhidata. Ikiwa ungefanya hivi kwenye seva iliyopita, kila utekelezaji wa programu ya mteja3 ungechukua sekunde tano. Ukiwa na seva mpya, utaweza kuchakata programu nyingi za mteja3 sambamba na muda uliopita wa zaidi ya sekunde tano.

$ ./client3 & ./client3 & ./client3 & ps x

Inavyofanya kazi

Programu ya seva sasa huunda mchakato mpya wa mtoto kushughulikia kila mteja, kwa hivyo unaweza kuona jumbe nyingi zinazosubiri seva huku programu kuu ikiendelea kusubiri maombi mapya ya muunganisho. Katika pato la amri

(iliyohaririwa) inaonyesha seva ya mchakato mkuu4 ikiwa na PID ya 26566 inayongojea wateja wapya, wakati michakato mitatu ya mteja3 inahudumiwa na watoto watatu wa seva. Baada ya kusitisha kwa sekunde tano, wateja wote hupokea matokeo yao na kuondoka. Michakato ya seva ya watoto pia hutoka, ikiacha mchakato mmoja tu wa seva kuu.

Programu ya seva itatumia simu

kushughulikia wateja wengi. Katika utumizi wa hifadhidata, hii inaweza kuwa sio suluhisho bora, kwa sababu ... Programu ya seva inaweza kuwa kubwa kabisa, na kwa kuongeza kuna shida ya kuratibu simu za hifadhidata kutoka kwa nakala nyingi za seva. Kweli, unachohitaji ni njia ya kushughulikia wateja wengi kwa seva moja bila kuzuia na kungoja maombi ya mteja kuwasilishwa. Suluhisho la tatizo hili linahusisha usindikaji wa maelezo mengi ya faili wazi wakati huo huo na sio mdogo kwa programu tumizi. Hebu fikiria kazi.

chagua

Mara nyingi sana wakati wa kuunda programu za Linux, unaweza kuhitaji kuangalia hali ya safu ya pembejeo ili kubaini hatua inayofuata ya kuchukua. Kwa mfano, programu ya mawasiliano kama vile kiigaji cha mwisho kinahitaji njia bora ya kusoma kwa wakati mmoja kutoka kwenye kibodi na kutoka. bandari ya serial. Kwenye mfumo wa mtumiaji mmoja, kitanzi cha "kusubiri kwa bidii" kingefanya kazi, kikichanganua mara kwa mara ingizo la data na kuisoma mara tu inapoonekana. Tabia hii inapoteza sana wakati wa CPU.

Simu ya mfumo

huruhusu programu kusubiri data kufika (au towe likamilike) kwenye vifafanuzi vingi vya kiwango cha chini cha faili kwa wakati mmoja. Hii ina maana kwamba programu ya emulator ya mwisho inaweza kuzuia hadi iwe na kazi ya kufanya. Vivyo hivyo, seva inaweza kushughulika na wateja wengi kwa kungoja maombi kwenye soketi nyingi wazi kwa wakati mmoja. hufanya kazi kwenye miundo ya data, ambayo ni seti za maelezo ya faili wazi. Ili kusindika seti hizi, seti ya macros hufafanuliwa:
#pamoja na #pamoja na
batili FD_ZERO(fd_set *fdset);
FD_CLR batili(int fd, fd_set *fdset);
utupu FD_SET(int fd, fd_set *fdset);
int FD_ISSET(int fd, fd_set *fdset);

Kama majina yao yanavyopendekeza, macros

huanzisha muundo kwa seti tupu, na huweka na kufuta vitu vya seti inayolingana na maelezo ya faili iliyopitishwa kama paramu, na jumla inarudisha thamani isiyo ya kawaida ikiwa maelezo ya faili iliyorejelewa ni sehemu ya muundo ulioelekezwa na kigezo. Idadi ya juu ya maelezo ya faili katika muundo wa aina inatajwa na mara kwa mara. inaweza pia kutumia thamani kwa muda kuisha ili kuzuia kuzuia bila kikomo. Thamani hii imeainishwa kwa kutumia muundo. Inafafanuliwa katika faili ya sys/time.h na ina vipengele vifuatavyo:
time_t tv_sec; /* Sekunde */
tv_usec ndefu; /* Sekunde ndogo */
, iliyofafanuliwa katika sys/types.h, ni nambari kamili. Simu ya mfumo inatangazwa kama ifuatavyo:
#pamoja na
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set * makosa, struct timeval * timeout);

Wito. Ikiwa parameta ni pointer isiyo na maana na hakuna shughuli kwenye soketi, simu inaweza kuzuia kwa muda usiojulikana. inarejesha udhibiti kwenye programu, seti ya maelezo itarekebishwa ili kuonyesha maelezo ya kusoma au kuandika tayari au makosa. Ili kuziangalia, unapaswa kutumia macro kuamua ni maelezo gani yanahitaji kuzingatiwa. Inawezekana kubadilisha thamani ya muda kuisha ili kuonyesha muda uliosalia kabla ya muda unaofuata kutokea, lakini tabia hii haijabainishwa na kiwango cha X/Open. Muda wa kuisha ukipitwa, seti zote za vishikizo vitafutwa.

Simu iliyochaguliwa hurejesha jumla ya idadi ya vishikio katika seti zilizobadilishwa. Ikiwa itashindwa itarudi -1 na kuweka thamani ya kutofautisha

, kuelezea kosa. Makosa yanayowezekana - Fanya mazoezi 15.8. Zoezi 15.8. Kazi

Programu ifuatayo inaonyesha jinsi ya kutumia chaguo la kukokotoa: select.c. Zaidi mfano tata utaona baadaye kidogo. Programu inasoma data kutoka kwa kibodi ( pembejeo ya kawaida- maelezo 0) na muda wa kusubiri wa sekunde 2.5. Data inasomwa tu wakati ingizo liko tayari. Ni kawaida kupanua programu ili kujumuisha maelezo mengine, kama vile mistari ya mfululizo na soketi, kulingana na asili ya programu.

1. Anza kama kawaida kwa maagizo

na matamko, na kisha uanzishe kushughulikia uingizaji wa kibodi:

2. Subiri ingizo kutoka kwa faili ya stdin kwa upeo wa sekunde 2.5:

result = chagua(FD_SETSIZE, &testfds, (fd_set *)NULL,

3. Baada ya wakati huu, angalia

. Ikiwa hapakuwa na pembejeo, programu itafanya kitanzi tena. Ikiwa kosa linatokea, programu itaisha:

4. Ukikumbana na shughuli fulani inayohusiana na maelezo ya faili unaposubiri, soma ingizo kutoka kwa stdin na uchapishe wakati wowote herufi ya EOL (mwisho wa mstari) inapopokelewa, kabla ya mseto wa funguo kubofya. +:

$ Seva inaweza kutumia chaguo la kukokotoa

wakati huo huo kwenye soketi inayosubiri maombi ya uunganisho na soketi za uunganisho wa mteja. Mara baada ya shughuli kunaswa, jumla inaweza kutumika kuzunguka kwa maelezo yote ya faili iwezekanavyo na kuona ni zipi zinazotumika.

Ikiwa tundu la usikilizaji wa ombi la muunganisho liko tayari kwa ingizo, hiyo inamaanisha kuwa mteja anajaribu kuunganishwa na unaweza kupiga simu kitendakazi.

bila hatari ya kuzuia. Ikiwa mpini wa mteja unaonyesha kuwa tayari, inamaanisha kuna ombi la mteja linalokusubiri ulisome na kulichakata. Kusoma baiti 0 kunamaanisha kuwa mchakato wa mteja umetoka na unaweza kufunga soketi na kuiondoa kwenye seti yako ya mpini.

Fanya Mazoezi 15.9.

Zoezi 15.9. Programu iliyoboreshwa ya seva ya mteja

1. Katika mfano wa mwisho wa programu server5.c, utajumuisha faili za kichwa sys/time.h na sys/ioctl.h badala ya signal.h iliyotumiwa katika programu ya awali, na utangaze vigeu kadhaa vya ziada ili kushughulikia simu.

:
int server_sockfd, mteja_sockfd;
tengeneza sockaddr_in server_anwani;
tengeneza sockaddr_in mteja_anwani;

2. Unda tundu la seva na uipe jina:

server_sockfd = tundu(AF_INET, SOCK_STREAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(9734);
server_len = sizeof(server_anwani);
bind(serversockfd, (struct sockaddr *)&server_anwani, server_len);

3. Unda foleni ya maombi ya uunganisho na uanzishe seti

kusindika pembejeo ya tundu
seva_sockfd

6. Ikiwa shughuli imerekodiwa

, inaweza kuwa ombi la muunganisho mpya, na unaongeza inayofaa kwa seti ya maelezo:
mteja_len = sizeof(anwani_ya_mteja);
client_sockfd = kubali(server_sockfd,
(struct sockaddr*)&client_address, &client_len);
FD_SET(mteja_sockfd, &readfds);
printf("kuongeza mteja kwenye fd %d\n", client_sockfd);

Ikiwa sio seva inayofanya kazi, basi mteja anafanya kazi. Ikipokelewa

, mteja hupotea na anaweza kuondolewa kutoka kwa seti ya vipini. Vinginevyo, "unamtumikia" mteja, kama katika mifano iliyopita.
printf("kuondoa mteja kwenye fd %d\n", fd);

Kwa ukamilifu, mlinganisho uliotajwa mwanzoni mwa sura uko kwenye Jedwali. Mchoro 15.5 unaonyesha ulinganifu kati ya miunganisho ya soketi na mazungumzo ya simu.


Jedwali 15.5

Datagrams

Katika sura hii, tuliangazia programu-tumizi zinazowasiliana na wateja wao kwa kutumia miunganisho ya TCP yenye tundu. Kuna hali ambazo gharama ya kuanzisha na kudumisha uhusiano wa tundu sio lazima.

Mfano mzuri itakuwa huduma

, iliyotumika mapema katika programu ya getdate.c. Unaunda tundu, fanya muunganisho, soma jibu moja, na ufunge muunganisho. Shughuli nyingi sana kupata tarehe tu! inapatikana pia kupitia miunganisho ya UDP kwa kutumia datagramu. Ili kuitumia, tuma tu datagramu moja kwa huduma na upokee datagramu moja katika jibu lililo na tarehe na wakati. Ni rahisi.

Huduma zinazotolewa juu ya itifaki ya UDP hutumiwa katika hali ambapo mteja anahitaji kuunda ombi fupi kwa seva, na anatarajia jibu moja fupi. Ikiwa gharama ya muda wa CPU ni ya chini vya kutosha, seva inaweza kutoa huduma hiyo kwa kushughulikia maombi ya mteja moja baada ya nyingine na kuruhusu mfumo wa uendeshaji kudumisha foleni ya maombi yanayoingia. Mbinu hii hurahisisha usanidi wa seva.

Kwa sababu UDP si huduma isiyothibitishwa, unaweza kupoteza datagram au majibu ya seva. Ikiwa data ni muhimu kwako, unaweza kuhitaji kupanga wateja wako wa UDP kwa uangalifu, ukiangalia hitilafu na ujaribu tena inapohitajika. Kwa mazoezi, takwimu za UDP zinaaminika sana kwenye mitandao ya ndani.

Ili kufikia huduma iliyotolewa na itifaki ya UDP, unapaswa kutumia simu za mfumo

na , lakini badala ya kutumia na kwenye tundu, unatumia simu mbili za mfumo maalum kwa datagrams: na .
/* Anza na pamoja na matamko ya kawaida. */

int main(int argc, char *argv) (
ikiwa (argc == 1) mwenyeji = "localhost";
/* Hutafuta anwani ya mwenyeji na kuripoti hitilafu ikiwa haitaipata. */
hostinfo = gethostbyname(mwenyeji);
fprintf(stderr, "hakuna mwenyeji: %s\n", mwenyeji);
/* Huangalia uwepo wa huduma ya mchana kwenye kompyuta. */
servinfo = getservbyname("mchana", "udp");
fprintf(stderr, "hakuna huduma ya mchana\n");
printf("bandari ya mchana ni %d\n", ntohs(servinfo->s_port));
/* Huunda soketi ya UDP. */
sockfd = tundu(AF_INEТ, SOCK_DGRAM, 0);
/* Hutoa anwani ya matumizi katika sendto/recvfrom... simu */
address.sin_family = AF_INET;
address.sin_port = servinfo->s_port;
address.sin_addr = *(struct in_addr*)*hostinfo->h_addr_list;
result = sendto(sockfd, buffer, 1, 0, (struct sockaddr *)&anwani, len);
matokeo = recvfrom(sockfd, buffer, sizeof(bafa), 0,
(struct sockaddr *)&anwani, &len);
printf("soma baiti %d: %s", tokeo, bafa);

Kama unaweza kuona, mabadiliko madogo tu yanahitajika. Kama hapo awali, unatafuta huduma

kwa kutumia simu, lakini kubainisha huduma ya datagram kwa kuomba itifaki ya UDP. Soketi ya datagram imeundwa kwa kutumia simu na . Anwani lengwa imebainishwa kama hapo awali, lakini sasa badala ya kusoma kutoka kwenye soketi, lazima utume datagram.

Kwa kuwa hufanyi muunganisho dhahiri kwa huduma zinazotegemea UDP, lazima uwe na njia ya kuarifu seva kwamba unataka jibu. Katika kesi hii, unatuma datagram (kwa mfano wetu, unatuma byte moja kutoka kwa bafa ambayo unataka kupokea jibu) kwa huduma na hutuma tarehe na wakati katika jibu.

Simu ya mfumo

hutuma datagram kutoka kwa bafa hadi kwenye tundu kwa kutumia anwani ya tundu na urefu wa anwani. Simu hii kwa kweli ina mfano ufuatao:

int sendto(int sockfd, void *bafa, size_t len, bendera za ndani,

struct sockaddr *to, socklen_t tolen);

Katika kesi ya matumizi ya kawaida, parameter

inaweza kuachwa sifuri.

Simu ya mfumo wa recvfrom inasubiri datagram kwenye muunganisho wa soketi na anwani iliyotolewa na kuiweka kwenye bafa. Simu hii ina mfano ufuatao:

int recvfrom(int sockfd, void *bafa, size_t len, bendera za int, na muda wa kuisha ili kubaini kama data imefika, kama ilivyo kwa seva zinazotegemea muunganisho. Vinginevyo, unaweza kutumia kengele kukomesha operesheni ya kupata data (tazama sura ya 11).

Muhtasari

Katika sura hii, tulipendekeza njia nyingine ya michakato ya kuwasiliana - soketi. Wanawezesha maendeleo ya kusambazwa kweli maombi ya seva ya mteja zinazoendeshwa katika mazingira ya mtandao. Maelezo mafupi yalitolewa kuhusu baadhi ya vipengele vya habari vya hifadhidata ya seva pangishi ya mtandao na jinsi Linux inavyoshughulikia huduma za kawaida za mfumo kwa kutumia damoni za Mtandao. Umefanyia kazi mifano kadhaa ya programu ya seva-teja inayoonyesha uchakataji na mtandao wa wateja wengi.

Kwa kumalizia, umezoea simu ya mfumo

, ambayo hukuruhusu kuarifu programu kuhusu shughuli ya pembejeo na pato kwenye maelezo kadhaa ya faili wazi na soketi mara moja.

Mabomba yaliyopewa jina yanafaa kwa kuandaa mawasiliano ya mwingiliano katika kesi ya michakato inayoendesha kwenye mfumo mmoja na katika kesi ya michakato inayoendesha kwenye kompyuta iliyounganishwa kwa kila mmoja na mtandao wa ndani au wa kimataifa. Uwezo huu ulionyeshwa kwa kutumia mfumo wa seva-teja uliotengenezwa katika Sura ya 11, kuanzia na Mpango wa 11.2.

Walakini, bomba na visanduku vya barua (ambavyo tutatumia neno la kawaida "bomba zilizopewa jina" kwa ajili ya kurahisisha ikiwa tofauti kati yao sio muhimu) zina shida kwamba sio kiwango cha tasnia. Hii inafanya kuwa vigumu kusambaza programu kama zile zilizojadiliwa katika Sura ya 11 hadi mifumo isiyo ya Windows, ingawa mabomba yaliyotajwa hayana itifaki na yanaweza kuendeshwa juu ya itifaki nyingi za kawaida za sekta kama vile TCP/IP.

Uwezo wa kuingiliana na mifumo mingine hutolewa katika Windows kwa msaada wa soketi za Windows Soketi - analog inayolingana na karibu kabisa ya Soketi za Berkeley, ambazo hutumika kama kiwango cha tasnia ya ukweli. Sura hii inaonyesha matumizi ya Windows Sockets (au "Winsock") API kwa kutumia mfumo wa mteja/seva uliorekebishwa kutoka Sura ya 11. Mfumo unaotokana unaweza kufanya kazi katika mitandao ya kimataifa, kwa kutumia itifaki ya TCP/IP, ambayo, kwa mfano, inaruhusu seva kukubali maombi kutoka kwa wateja wa UNIX au mifumo mingine yoyote isiyo ya Windows.

Wasomaji wanaofahamu kiolesura cha Soketi za Berkeley wanaweza kuruka mbele kwa mifano ambayo haitumii soketi pekee, bali pia kutambulisha uwezo mpya wa seva na kuonyesha mbinu za ziada za kufanya kazi na maktaba zinazotoa usaidizi kwa njia salama.

Kwa kuongeza mwingiliano wa msingi wa viwango kati ya mifumo tofauti, kiolesura cha Winsock kinawapa waandaaji programu ufikiaji wa itifaki na programu za kiwango cha juu kama vile ftp, http, RPC, na COM, ambazo kwa pamoja hutoa seti tajiri ya miundo ya kiwango cha juu ambayo inasaidia uunganishaji wa mitandao kwa mifumo na usanifu tofauti.

Sura hii inatumia mfumo maalum wa seva ya mteja kama utaratibu wa kuonyesha kiolesura cha Winsock, na seva inaporekebishwa, vipengele vipya vya kuvutia vitaongezwa kwayo. Hasa, tutatumia kwa mara ya kwanza Sehemu za kuingia za DLL(Sura ya 5) na katika mchakato wa seva za DLL.(Vipengele hivi vipya vingeweza kujumuishwa katika toleo la asili la programu katika Sura ya 11, lakini hili lingeondoa mawazo yako kutoka kwa kutengeneza usanifu msingi wa mfumo.) Hatimaye, mifano ya ziada itakuonyesha jinsi ya kuunda salama, iliyoingia upya, yenye nyuzi nyingi. maktaba.

Kwa sababu kiolesura cha Winsock lazima kilingane na viwango vya sekta, kanuni zake za kutaja majina na mitindo ya upangaji ni tofauti kwa kiasi fulani na zile tulizokutana nazo na vipengele vya Windows vilivyoelezwa hapo awali. Kwa kusema kweli, API ya Winsock sio sehemu ya Win32/64. Kwa kuongeza, Winsock hutoa utendaji wa ziada usio na viwango; Kazi hizi hutumiwa tu wakati muhimu kabisa. Faida zingine zinazotolewa na Winsock ni pamoja na kuboreshwa kwa utumiaji wa programu zinazotolewa kwa mifumo mingine.

Soketi za Windows

API ya Winsock iliundwa kama kiendelezi cha API ya Soketi ya Berkley kwa mazingira ya Windows na kwa hivyo inatumika kwenye mifumo yote ya Windows. Faida za Winsock ni pamoja na zifuatazo:

Nambari iliyopo iliyoandikwa kwa API ya Soketi za Berkeley inatumwa moja kwa moja.

Mifumo ya Windows inaweza kuunganishwa kwa urahisi katika mitandao inayotumia toleo la IPv4 la itifaki ya TCP/IP na toleo linalozidi kuenea la IPv6. Juu ya kila kitu kingine, IPv6 inaruhusu anwani ndefu za IP, na kuvunja kizuizi cha anwani cha baiti 4 cha IPv4.

Soketi zinaweza kutumika kwa kushirikiana na I/O iliyopishana ya Windows (Sura ya 14), ambayo, miongoni mwa mambo mengine, huruhusu seva kuongeza ukubwa kadri idadi ya wateja wanaofanya kazi inavyoongezeka.

Soketi zinaweza kuzingatiwa kama vishikio vya faili (za aina HANDLE) unapotumia vitendaji vya ReadFile na AndikaFile na, pamoja na mapungufu fulani, unapotumia vitendaji vingine, kama vile soketi hutumika kama vishikizo vya faili katika UNIX. Kipengele hiki huja kwa manufaa unapohitaji kutumia bandari za kukamilisha za I/O na I/O zisizolingana.

Pia kuna viendelezi vya ziada, visivyoweza kubebeka.

Kuanzisha Winsock

API ya Winsock inasaidiwa na maktaba ya DLL (WS2_32.DLL), ili kufikia ambayo lazima uunganishe maktaba ya WS_232.LIB kwenye programu. DLL hii lazima ianzishwe kwa kutumia kitendakazi kisicho cha kawaida, maalum cha Winsock WSAStartup, ambacho lazima kiwe chaguo la kwanza la kukokotoa la Winsock linaloitwa na programu. Wakati hauitaji tena utendakazi wa Winsock, unapaswa kupiga kitendakazi cha WSACleanup. Kumbuka. Kiambishi awali cha WSA kinasimama kwa "Soketi za Windows zisizolingana ...". Hatutumii vifaa vya hali ya asynchronous ya Winsock hapa, kwani wakati hitaji linatokea la kufanya shughuli za asynchronous, tunaweza na tutatumia nyuzi.

Ingawa kazi za WSAStartup na WSACleanup lazima ziitwe, zinaweza kuwa kazi zisizo za kawaida tu unazopaswa kushughulika nazo. Kitendo cha kawaida ni kutumia maagizo ya #ifdef preprocessor kuangalia thamani ya ishara thabiti _WIN32 (kawaida huamuliwa na Visual C++ wakati wa kukusanya), na kusababisha utendakazi wa WSA kuitwa tu unapoendesha kwenye Windows). Kwa kweli, mbinu hii inadhania kuwa nambari iliyobaki ni huru ya jukwaa.

int WSAStartup(WORD wVersionRequired, LPWSADATA ipWSAData);
Chaguo

wVersionRequired - inaonyesha nambari ya toleo kuu DLL, ambayo unahitaji na unaweza kutumia. Kwa kawaida, toleo la 1.1 linatosha kutoa ushirikiano wowote unaoweza kuhitaji na mifumo mingine. Hata hivyo, Winsock 2.0 inapatikana kwenye mifumo yote ya Windows, ikiwa ni pamoja na Windows 9x, na inatumika katika mifano iliyo hapa chini. Toleo la 1.1 linachukuliwa kuwa halitumiki na linaacha kutumika polepole.

Chaguo za kukokotoa hurejesha nonzero ikiwa toleo la DLL uliloomba halitumiki.

Byte ya chini ya parameta ya wVersionRequired inaonyesha nambari ya toleo kuu, na byte ya juu inaonyesha toleo dogo. Kwa kawaida MAKEWORD macro hutumiwa; kwa hivyo usemi MAKEWORD(2,0) unawakilisha toleo la 2.0.

ipWSAData ni kielekezi kwa muundo wa WSADATA ambao unarudisha maelezo ya usanidi wa DLL, ikijumuisha nambari kuu inayopatikana ya toleo. Unaweza kusoma kuhusu jinsi ya kutafsiri yaliyomo kwenye usaidizi wa mtandaoni wa Visual Studio.

Unaweza kutumia chaguo za kukokotoa za WSAGetLastError kupata maelezo zaidi ya hitilafu, lakini chaguo za kukokotoa za GetLastError pia zinafaa kwa madhumuni haya, kama vile kipengele cha kukokotoa cha ReportError kilichotengenezwa katika Sura ya 2.

Wakati programu inapokamilika kufanya kazi, au wakati hakuna haja tena ya kutumia soketi, unapaswa kupiga kitendakazi cha WSACleanup ili maktaba ya WS_32.DLL ambayo inadumisha soketi iweze kutoa rasilimali zilizotengwa kwa mchakato huu.

Kutengeneza tundu

Mara tu Winsock DLL inapoanzishwa, unaweza kutumia vitendaji vya kawaida (Berkeley Sockets) ili kuunda soketi na miunganisho inayoruhusu seva kuwasiliana na wateja au kuwasiliana kati ya wenzao kwenye mtandao.

Aina ya data ya SOCKET ya Winsock inafanana na aina ya data ya Windows HANDLE, na inaweza hata kutumika pamoja na chaguo la kukokotoa la ReadFile na vitendaji vingine vya Windows vinavyohitaji vipini vya HANDLE. Ili kuunda (au kufungua) tundu, tumia tundu.

tundu la SOCKET (int af, aina ya int, itifaki ya int);
Chaguo

Aina ya data ya SOCKET inafafanuliwa vyema kama aina ya data ya int, kwa hivyo nambari ya UNIX inasalia kubebeka bila kulazimika kuomba aina za data za Windows.

af - inaashiria familia ya anwani, au itifaki; Ili kutaja itifaki ya IP (sehemu ya itifaki ya TCP/IP ambayo inawajibika kwa itifaki ya Mtandao), unapaswa kutumia thamani PF_INET (au AF_INET, ambayo ina thamani sawa ya nambari, lakini kwa kawaida hutumiwa wakati wa kupiga kazi ya kuunganisha) .

aina - inaonyesha aina ya mawasiliano: mawasiliano yanayolenga muunganisho, au utiririshaji (SOCK_STREAM), na mawasiliano ya datagramu (SOCK_DGRAM), ambayo kwa kiasi fulani inalinganishwa na mirija iliyopewa jina na masanduku ya barua, mtawalia.

itifaki - haitumiki tena ikiwa af imewekwa kwa AF_INET; tumia thamani 0.

Ikiwa itashindwa, kazi ya tundu inarudi INVALID_SOCKET.

Winsock inaweza kutumika na itifaki zingine isipokuwa TCP / IP kwa kubainisha maadili tofauti ya parameta ya itifaki; tutatumia itifaki ya TCP/IP pekee.

Kama ilivyo kwa vitendakazi vingine vyote vya kawaida, jina la chaguo la kukokotoa la soketi lazima lisiwe na herufi kubwa. Huku ni kuondoka kutoka kwa mikataba ya Windows na inaendeshwa na haja ya kuzingatia viwango vya sekta.

Vipengele vya tundu la seva

Katika mjadala ufuatao chini seva itaeleweka kama mchakato unaopokea maombi ya kuunda muunganisho kupitia bandari fulani. Ingawa soketi, kama mirija iliyotajwa, inaweza kutumika kuunda miunganisho ya rika-kwa-rika, kufanya tofauti hii kati ya rika ni rahisi na huonyesha tofauti katika jinsi mifumo miwili inavyowasiliana.

Isipokuwa imebainishwa vinginevyo, aina ya soketi katika mifano yetu itakuwa SOCK_STREAM kila wakati. Soketi za SOCK_DGRAM zitajadiliwa baadaye katika sura hii.

Kufunga tundu

Hatua inayofuata ni kumfunga tundu kwa anwani yake na hatua ya mwisho(mwisho) (mwelekeo wa chaneli ya mawasiliano kutoka kwa programu kwenda kwa huduma). Simu kwa soketi ikifuatiwa na simu ya kufunga ni sawa na kuunda bomba lililopewa jina. Hata hivyo, hakuna majina ambayo yanaweza kutumika kutofautisha soketi za kompyuta fulani. Badala yake, mwisho wa huduma ni nambari ya bandari(nambari ya bandari). Seva yoyote iliyotolewa inaweza kuwa na ncha nyingi. Mfano wa chaguo la kukokotoa umepewa hapa chini.

int bind(SOCKET s, const struct sockaddr *saddr, int namelen);
Chaguo

s ni tundu lisilofungwa lililorejeshwa na kazi ya tundu.

saddr - Imejazwa kabla ya simu na kubainisha itifaki na maelezo mahususi ya itifaki kama ilivyoelezwa hapa chini. Miongoni mwa mambo mengine, muundo huu una nambari ya bandari.

namelen - toa thamani kwa sizeof(sockaddr).

Ikifaulu, chaguo la kukokotoa hurejesha thamani 0, vinginevyo SOCKET_ERROR. Muundo wa sockaddr hufafanuliwa kama ifuatavyo:

typedef muundo sockaddr SOCKADDR, *PSOCKADDR;

Mwanachama wa kwanza wa muundo huu, sa_family, anaashiria itifaki. Mwanachama wa pili, sa_data, inategemea itifaki. Toleo la mtandao la muundo wa sa_data ni muundo wa sockaddr_in:

dhambi_familia fupi; /* AF_INET */
struct in_addr sin_addr; /* Anwani ya IP ya baiti 4 */
typedef muundo sockaddr_in SOCKADDR_IN, *PSOCKADDR IN;

Kumbuka matumizi ya aina fupi kamili ya data kwa nambari ya mlango. Kwa kuongeza, nambari ya bandari na taarifa nyingine lazima zihifadhiwe kwa mpangilio ufaao wa baiti, huku baiti muhimu zaidi ikiwekwa katika nafasi kubwa ya mwisho, ili kuhakikisha upatanifu wa binary na mifumo mingine. Muundo wa sin_addr una muundo s_addr, uliojazwa na anwani ya IP ya baiti 4, kama vile 127.0.0.1, inayoonyesha mfumo ambao ombi lake la muunganisho linapaswa kukubaliwa. Kwa ujumla, maombi kutoka kwa mfumo wowote yataridhika, kwa hivyo INADDR_ANY inapaswa kutumika, ingawa kigezo hiki cha ishara lazima kibadilishwe hadi umbizo sahihi, kama inavyoonyeshwa katika kijisehemu cha msimbo hapa chini.

Chaguo za kukokotoa za inet_addr zinaweza kutumika kubadilisha mfuatano wa maandishi ulio na anwani ya IP hadi umbizo linalohitajika, kwa hivyo kishiriki cha sin_addr.s_addr cha kigezo cha sockaddr_in kinaanzishwa kama ifuatavyo:

sa.sin_addr.s_addr = inet_addr("192 .13.12.1");

Soketi iliyounganishwa ambayo itifaki, nambari ya bandari, na anwani ya IP hufafanuliwa wakati mwingine hujulikana kama tundu iliyopewa jina(jina la tundu).

Kuweka tundu linalohusika katika hali ya kusikiliza

Kitendaji cha kusikiliza hufanya seva ipatikane ili kuunda muunganisho na mteja. Hakuna kipengele sawa cha mabomba yaliyotajwa.

int listen(SOCKET s, int nQueueSize);

Kigezo cha nQueueSize kinabainisha idadi ya maombi ya muunganisho ambayo unakusudia kupanga kwenye tundu. Katika Winsock 2.0 thamani ya parameter hii haina kikomo cha juu, lakini katika toleo la 1.1 ni mdogo na kikomo cha SOMAXCON (ya 5).

Kukubali maombi ya muunganisho wa mteja

Hatimaye, seva inaweza kusubiri muunganisho na mteja kwa kutumia kitendakazi cha kukubali, ambacho hurejesha soketi mpya iliyounganishwa ili kutumika kwa shughuli za I/O. Kumbuka kuwa soketi asili, ambayo sasa iko katika hali ya usikilizaji, inatumika tu kama kigezo cha chaguo la kukokotoa la kukubali na haihusiki moja kwa moja katika shughuli za I/O.

Kazi ya kukubali inazuia hadi ombi la uunganisho lipokewe kutoka kwa mteja, wakati ambapo inarudisha tundu mpya la I/O. Ingawa zaidi ya upeo wa kitabu hiki, inawezekana kuunda soketi zisizozuia, na seva (Programu 12.2) hutumia thread tofauti kupokea ombi, kukuwezesha kuunda seva zisizozuia pia.

SOCKET kukubali(SOCKET s, LPSOCKADDR lpAddr, LPINT lpAddrLen);
Chaguo

s - tundu la kusikiliza. Kuweka tundu katika hali ya kusikiliza, lazima kwanza uite tundu, funga, na usikilize vitendaji.

lpAddr ni kielekezi kwa muundo wa sockaddr_in ambao hutoa anwani ya mfumo wa mteja.

lpAddrLen ni kielekezi cha kigezo ambacho kitakuwa na saizi ya muundo uliorejeshwa wa sockaddr_in. Kabla ya kupiga simu ukubali, utaftaji huu lazima uanzishwe kwa sizeof(struct sockaddr_in).

Kukata na kufunga soketi

Ili kufunga soketi, tumia kazi ya kuzima, jinsi. Jinsi hoja inaweza kuchukua moja ya maadili mawili: 1, kuonyesha kwamba muunganisho unaweza kufungwa kwa ajili ya kutuma ujumbe tu, na 2, ikionyesha kwamba muunganisho unaweza kufungwa kwa kutuma na kupokea ujumbe. Kazi ya kuzima haitoi rasilimali zinazohusiana na tundu, lakini inahakikisha kwamba data zote zinatumwa na kupokelewa kabla ya tundu kufungwa. Walakini, mara kazi ya kuzima inapoitwa, programu haifai kutumia tena tundu.

Unapomaliza kutumia tundu, unapaswa kuifunga kwa kupiga kazi ya closesocket(SOCKET s). Seva hufunga kwanza tundu iliyoundwa na kitendakazi cha kukubali, badala ya tundu la kusikiliza linaloundwa na chaguo la kukokotoa la soketi. Seva inapaswa kufunga soketi ya kusikiliza tu inapozima au inapoacha kukubali maombi ya muunganisho wa mteja. Hata ukichukulia tundu kama mpini wa HANDLE na kutumia vitendaji vya ReadFile na AndikaFile, huwezi kuharibu tundu kwa kuita kitendakazi cha CloseHandle pekee; Kwa kufanya hivyo unapaswa kutumia kazi ya closesocket.

Mfano: Kutayarisha na Kupokea Maombi ya Muunganisho wa Wateja

Kijisehemu kifuatacho cha msimbo kinaonyesha jinsi ya kuunda soketi na kukubali maombi ya muunganisho wa mteja.

Mfano huu hutumia vitendakazi viwili vya kawaida, htons ("mwenyeji kwa ufupi wa mtandao") na htonl ("mwenyeji hadi mtandao mrefu"), ambazo hubadilisha nambari kamili hadi itifaki ya IP inayohitajika.

Nambari ya mlango wa seva inaweza kuwa nambari yoyote kutoka kwa safu inayoruhusiwa kwa nambari fupi, lakini kwa mtumiaji amefafanuliwa huduma kwa kawaida hutumia nambari katika masafa 1025-5000. Nambari za mlango wa chini zimehifadhiwa kwa huduma zinazojulikana kama vile telnet au ftp, wakati nambari za mlango wa juu zimehifadhiwa kwa huduma zingine za kawaida.

tengeneza sockaddr_in SrvSAddr; /* Muundo wa anwani ya seva. */
tengeneza sockaddr_in ConnectAddr;
AddrLen = sizeof(ConnectAddr);
sockio = kubali(SrvSock, (muundo sockaddr *) &ConnectAddr, &AddrLen);
... Kupokea maombi na kutuma majibu ...

Kazi za mteja wa tundu

Kituo cha mteja kinachotaka kuanzisha muunganisho na seva lazima pia kitengeneze tundu kwa kupiga simu kitendakazi cha soketi. Hatua inayofuata ni kwa seva kuanzisha uunganisho, na kwa kuongeza, lazima ueleze nambari ya bandari, anwani ya mwenyeji na maelezo mengine. Kuna kazi moja tu ya ziada - kuunganisha.

Kuanzisha muunganisho wa mteja na seva

Ikiwa kuna seva iliyo na tundu katika hali ya kusikiliza, mteja anaweza kuunganisha kwa kutumia kazi ya kuunganisha.

int connect(SOCKET s, LPSOCKADDR lpName, int nNameLen);
Chaguo

s ni tundu iliyoundwa kwa kutumia kazi ya tundu.

lpName ni kielekezi cha muundo wa sockaddr_in ulioanzishwa kwa thamani za nambari ya mlango na anwani ya IP ya mfumo na soketi inayohusishwa na mlango uliobainishwa ambao uko katika hali ya kusikiliza.

Anzisha nNameLen na sizeof (muundo sockaddr_in).

Thamani ya kurudi ya 0 inaonyesha kukamilika kwa chaguo za kukokotoa, ilhali thamani ya SOCKET_ERROR inaonyesha hitilafu, ambayo inaweza kuwa kutokana na, miongoni mwa mambo mengine, kutokuwa na soketi ya kusikiliza. anwani maalum.

Soketi s sio lazima ihusishwe na mlango kabla ya kitendakazi cha muunganisho kuitwa, ingawa hii inaweza kuwa hivyo. Ikiwa ni lazima, mfumo hutenga bandari na huamua itifaki.

Mfano: kuunganisha mteja kwenye seva

Kijisehemu cha msimbo kilichoonyeshwa hapa chini huunganisha mteja na seva. Hii inahitaji simu mbili tu za kazi, lakini muundo wa anwani lazima uanzishwe kabla ya kitendakazi cha kuunganisha kuitwa. Uchunguzi makosa iwezekanavyo haipo hapa, lakini mipango halisi inapaswa kuijumuisha. Mfano unachukulia kuwa anwani ya IP (mfuatano wa maandishi kama "192.76.33.4") imebainishwa katika hoja ya mstari wa amri ya argv.

ClientSAddr.sin_addr.s_addr = inet_addr(argv);
ConVal = unganisha(ClientSock, (muundo sockaddr *)&ClientSAddr, sizeof(ClientSAddr));

Kutuma na kupokea data

Programu zinazotumia soketi hubadilishana data kwa kutumia vitendakazi vya kutuma na recv, ambavyo mifano yake inakaribia kufanana (kielekezi cha bafa cha kitendakazi cha kutuma hutanguliwa na kirekebishaji cha const). Ifuatayo ni mfano tu wa kitendakazi cha kutuma.

int send(SOCKET s, const char * lpBuffer, int nBufferLen, int nFlags);

Thamani ya kurudi ni idadi ya baiti zilizohamishwa. SOCKET_ERROR inaonyesha hitilafu.

nFlags zinaweza kutumiwa kuonyesha uharaka wa ujumbe (kama vile ujumbe wa dharura), na thamani ya MSG_PEEK hukuruhusu kuona data iliyopokelewa bila kuisoma.

Jambo muhimu zaidi unahitaji kukumbuka ni kwamba kutuma na recv hufanya kazi sio atomiki(atomiki) na kwa hivyo hakuna hakikisho kwamba data iliyoombwa itatumwa au kupokelewa. Uwasilishaji wa ujumbe "mfupi" ("utumaji mfupi") ni nadra sana, ingawa inawezekana, ambayo pia ni kweli kuhusiana na upokeaji wa ujumbe "fupi" ("upokeaji mfupi"). Dhana ya ujumbe kwa maana iliyokuwa nayo katika kesi ya mabomba yaliyotajwa haipo hapa, na kwa hiyo ni lazima uangalie thamani ya kurudi na kutuma tena au kupokea data mpaka yote yamepitishwa.

Kazi za ReadFile na AndikaFile pia zinaweza kutumika na soketi, tu katika kesi hii, wakati wa kupiga kazi, lazima utupe tundu kwa aina ya HANDLE.

Ulinganisho wa mabomba yenye jina na matako

Mabomba yaliyotajwa, yaliyoelezwa katika Sura ya 11, yanafanana sana na soketi, lakini kuna tofauti kubwa katika njia zinazotumiwa.

Mabomba yaliyotajwa yanaweza kuelekezwa kwa ujumbe, ambayo hurahisisha sana programu.

Mabomba yaliyopewa majina yanahitaji matumizi ya vitendakazi vya ReadFile na AndikaFile, huku soketi pia zinaweza kutumia vitendakazi vya kutuma na kurejesha.

Tofauti na mabomba yaliyotajwa, soketi ni rahisi kunyumbulika hivi kwamba huwapa watumiaji uwezo wa kuchagua itifaki ya kutumia na soketi, kama vile TCP au UDP. Kwa kuongeza, mtumiaji ana fursa ya kuchagua itifaki kulingana na hali ya huduma iliyotolewa au mambo mengine.

Soketi zinatokana na kiwango cha sekta, na kuzifanya ziendane na mifumo isiyo ya Windows.

Pia kuna tofauti katika mifano ya programu ya seva na mteja.

Ulinganisho wa bomba iliyopewa jina na seva za tundu

Kuanzisha muunganisho na wateja wengi kwa kutumia soketi kunahitaji simu zinazorudiwa kwa kipengele cha kukubalika. Kila simu inarudisha tundu linalofuata lililounganishwa. Ikilinganishwa na bomba zilizotajwa, tofauti zifuatazo zipo:

Mabomba yaliyopewa jina yanahitaji kwamba kila mfano wa bomba uliopewa jina na mpini wa HANDLE uundwe kwa kutumia kitendakazi cha CreateNamedPipe, ilhali hali za soketi huundwa kwa kutumia kitendakazi cha kukubali.

Idadi ya soketi za mteja zinazoruhusiwa hazina kikomo (kitendo cha kusikiliza huweka kikomo tu idadi ya wateja wanaoweza kupangiliwa), wakati idadi ya matukio ya bomba yaliyotajwa, kulingana na kile kilichobainishwa wakati CreateNamedPipe ilipoitwa mara ya kwanza, inaweza kupunguzwa.

Hakuna vitendakazi vya kisaidizi vya soketi sawa na kitendakazi cha TransactNamedPipe.

Mabomba yaliyotajwa hayana nambari za bandari zilizofafanuliwa kwa uwazi na hutofautishwa na majina.

Kwa upande wa seva ya bomba iliyopewa jina, kupata mpini wa aina ya HANDLE inayoweza kutumika kunahitaji kupiga vitendakazi viwili (CreateNamedPipe na ConnectNamedPipe), wakati seva ya soketi inahitaji kupiga vitendaji vinne (soketi, funga, sikiliza na ukubali).

Ulinganisho wa bomba zilizotajwa na wateja wa tundu

Katika kesi ya mabomba yaliyotajwa, lazima uite kazi za WaitNamedPipe na CreateFile kwa mfuatano. Ikiwa soketi zinatumiwa, utaratibu huu wa simu hubadilishwa, kwa kuwa kazi ya tundu inaweza kufikiriwa kama kuunda tundu na kuzuia kazi ya kuunganisha.

Tofauti ya ziada ni kwamba kazi ya kuunganisha ni kazi ya mteja wa tundu, wakati kazi ya ConnectNamedPipe inatumiwa na seva ya bomba iliyoitwa.

Mfano: kipengele cha kupokea ujumbe katika kesi ya tundu

Mara nyingi ni rahisi kutuma na kupokea ujumbe katika vizuizi moja. Kama tulivyoona katika Sura ya 11, vituo vinakuruhusu kufanya hivyo. Hata hivyo, katika kesi ya soketi, kichwa kilicho na ukubwa wa ujumbe kinahitajika, ikifuatiwa na ujumbe yenyewe. Kazi ya ReceiveMessage, ambayo itatumika katika mifano, imeundwa kupokea ujumbe kama huo. Vile vile vinaweza kusemwa kuhusu kazi ya SendMessage, iliyoundwa kwa ajili ya kutuma ujumbe.

Tafadhali kumbuka kuwa ujumbe umepokelewa katika sehemu mbili: kichwa na maudhui. Hapo chini tunachukulia hivyo aina maalum MESSAGE inalingana na kichwa cha baiti 4. Lakini hata kichwa cha baiti 4 kinahitaji simu zinazorudiwa kwa kitendakazi cha recv ili kuhakikisha kuwa kimesomwa kikamilifu, kwani kitendakazi cha recv si cha atomiki.

Kumbuka maalum kwa Win64

Aina ya vigeu vinavyotumika kuhifadhi saizi ya ujumbe ni aina ya data ya LONG32 iliyosahihishwa, ambayo itatosha kukidhi maadili ya kigezo cha saizi iliyojumuishwa katika ujumbe wakati wa kuingiliana na mifumo mingine isipokuwa Windows, na ambayo inafaa. uwezekano wa urejeshaji unaofuata wa programu kwa matumizi yake kwenye jukwaa la Win64 (tazama Sura ya 16).

DWORD PokeaMessage (UJUMBE *pMsg, SOCKET sd) (
/* Ujumbe una uga wa ukubwa wa ujumbe wa baiti 4 ukifuatwa na maudhui halisi. */
/* Soma ujumbe. */
/* Kichwa kinasomwa kwanza, kisha yaliyomo. */
nRemainRecv = 4; /* Ukubwa wa sehemu ya kichwa. */
pBuffer = (LPBYTE)pMsg; /* recv haiwezi kuhamisha baiti zote zilizoombwa. */
wakati (nRemainRecv > 0 && !Tenganisha) (
/* Soma yaliyomo kwenye ujumbe. */
wakati (nRemainRecv > 0 && !Tenganisha) (
nXfer = recv(sd, pBuffer, nRemainRecv, 0);

Mfano: Mteja wa soketi

Mpango 12.1 ni urekebishaji upya wa programu ya mtejaNP (Programu 11.2) ambayo ilitumika kwa mabomba yaliyopewa jina. Ubadilishaji wa programu unafanywa kwa njia ya moja kwa moja na inahitaji maelezo fulani tu.

Badala ya kugundua seva kwa kutumia visanduku vya barua, mtumiaji huingiza anwani ya IP ya seva kwenye mstari wa amri. Ikiwa hakuna anwani ya IP iliyoelezwa, anwani ya default 127.0.0.1 inatumiwa, inayofanana na mfumo wa ndani.

Ili kutuma na kupokea ujumbe, vitendaji kama vile ReceiveMessage vinatumika, ambavyo havijawasilishwa hapa.

Nambari ya mlango, SERVER_PORT, imefafanuliwa katika faili ya kichwa ClntSrvr.h.

Ingawa msimbo umeandikwa ili kuendeshwa kwenye Windows, utegemezi pekee kwa Windows ni kupitia utumiaji wa simu za kukokotoa zilizoamrishwa na WSA.

Mpango 12.1. clientSK: mteja wa tundu
/* Sura ya 12. clientSK.с */
/* Teja ya mstari wa amri yenye nyuzi moja. */
/* SOKO MADIRISHA MSINGI WA VERSION. */
/* Husoma mlolongo wa amri za kutuma kwa mchakato wa seva*/
/* kupitia muunganisho wa soketi. Inasubiri jibu na kuionyesha. */

#fafanua _NOEXCLUSIONS /* Inahitajika ili kuwezesha ufafanuzi wa soketi. */
#include "ClntSrvr.h" /* Inafafanua miundo ya rekodi za ombi na majibu. */

/* Vitendaji vya ujumbe kwa ajili ya kuhudumia maombi na majibu. */
/* Zaidi ya hayo, ReceiveResponseMessage huonyesha ujumbe uliopokewa. */
tuli DWORD SendRequestMessage(OMBI *, SOCKET);
tuli DWORD ReceiveResponseMessage(RESPONSE *, SOCKET);
tengeneza sockaddr_in ClientSAddr; /* Anwani ya tundu la mteja. */
int _tmain(DWORD argc, LPTSTR argv) (
SOCKET ClientSock = INVALID_SOCKET;
OMBI Ombi; /* Tazama ClntSrvr.h. */
MAJIBU MAJIBU; /* Tazama ClntSrvr.h. */
TCHAR PromptMsg = _T("\nIngiza amri> ");
TCHAR QuitMsg = _T("$Quit");
/* Ombi: kusitisha mteja. */
TCHAR ShutMsg = _T("$ShutDownServer"); /* Komesha nyuzi zote. */
CHAR DefaultIPAddr = "127.0.0.1"; /* Mfumo wa ndani. */
/* Unganisha kwa seva. */
/* Fuata utaratibu wa kawaida wa kuita mfuatano wa vitendaji vya soketi/unganishe na mteja. */
ClientSock = tundu(AF_INET, SOCK_STREAM, 0);
memset(&ClientSAddr, 0, sizeof(ClientSAddr));
ClientSAddr.sin_family = AF_INET;
ikiwa (argc >= 2) ClientSAddr.sin_addr.s_addr = inet_addr(argv);
else ClientSAddr.sin_addr.s_addr = inet_addr(DefaultIPAddr);
ClientSAddr.sin_port = htons(SERVER_PORT);
/* Nambari ya bandari inafafanuliwa kama 1070. */
unganisha(ClientSock, (struct sockaddr *)&ClientSAddr, sizeof(ClientSAddr));
/* Kitanzi kikuu cha kuonyesha kidokezo cha amri, kutuma ombi na kupokea jibu. */
_tprintf(_T("%s"), PromptMsg);
/* Ingizo liko katika umbizo la kadi-mwitu, lakini amri kwa seva lazima iwe katika umbizo la ASCII. */
_fgetts(Req, MAX_RQRS_LEN-1, stdin);
kwa (j = 0; j<= _tcslen(Req) Request.Record[j] = Req[j];
/* Ondoa ishara mstari mpya mwishoni mwa mstari. */
Ombi.Rekodi = "\0";
ikiwa (strcmp(Request.Record, QuitMsg) == 0 || strcmp(Request.Record, ShutMsg) == 0) Acha = TRUE;
SendRequestMessage(&Request, ClientSock);
ReceiveResponseMessage(&Response, ClientSock);
shutdown(ClientSock, 2); /* Kataza kutuma na kupokea ujumbe. */
_tprintf(_T("\n****Ondoka kwenye programu ya mteja\n"));

Mfano: Seva ya Soketi ya Juu

Programu ya sevaSK (mpango 12.2) ni sawa na programu ya sevaNP (mpango wa 11.3), kuwa toleo lake lililorekebishwa na kuboreshwa.

Katika toleo lililoboreshwa la programu, nyuzi za seva zinaundwa juu ya mahitaji(inapohitajika), badala ya kuwa kidimbwi cha nyuzi za ukubwa usiobadilika. Kila wakati seva inakubali ombi la uunganisho la mteja, uzi wa mfanyakazi wa seva huundwa, na mteja anapomaliza, utekelezaji wa thread hukoma.

Seva inaunda tofauti pokea mkondo(kukubali thread), ambayo huruhusu thread kuu kupigia kura bendera ya kimataifa ya kuzimwa huku simu ya kukubali ikiendelea kuzuiwa. Ingawa soketi zinaweza kufafanuliwa kama zisizozuia, mitiririko hutoa suluhisho rahisi la kusudi la jumla. Ikumbukwe kwamba utendakazi mwingi wa hali ya juu wa Winsock umeundwa ili kusaidia shughuli za asynchronous, wakati nyuzi za Windows hutoa uwezo wa kuchukua fursa ya utendakazi rahisi, unaozingatia viwango vya hali ya tundu ya synchronous.

Kwa sababu ya ugumu fulani wa programu, usimamizi wa nyuzi umeboreshwa, ambayo ilifanya iwezekane kutoa msaada kwa majimbo ya kila uzi.

Seva hii pia inasaidia katika mchakato wa seva(seva za mchakato), ambayo hupatikana kwa kupakia DLL wakati wa uanzishaji. Jina la DLL limetajwa kwenye mstari wa amri, na thread ya seva inajaribu kwanza kuamua hatua ya kuingia ya DLL. Ikiwa imefanikiwa, uzi wa seva huita sehemu ya kuingia ya DLL; vinginevyo, seva huunda mchakato kwa njia sawa na ulifanyika katika programu ya sevaNP. Mfano wa DLL umetolewa katika Programu ya 12.3. Kwa sababu kutupa kando na DLL kutaua mchakato mzima wa seva, simu ya kitendakazi ya DLL inalindwa na kidhibiti rahisi cha ubaguzi.

Ikiwa inataka, unaweza kujumuisha seva za mchakato katika programu ya sevaNP. Faida kubwa ya seva zilizo katika mchakato ni kwamba hazihitaji kubadili muktadha wowote kwa michakato mingine, ambayo inaweza kusababisha uboreshaji wa utendakazi unaoonekana.

Kwa kuwa nambari ya seva hutumia huduma maalum za Windows, haswa, uwezo wa kudhibiti nyuzi na zingine zingine, tofauti na nambari ya mteja, inageuka kuwa imefungwa kwa Windows.

Mpango 12.2. serverSK: seva ya soketi iliyo na seva zinazochakatwa
/* Sura ya 12. Mfumo wa seva ya mteja. PROGRAMU YA SEVA. SOCKET BASED VERSION. */
/* Hutekeleza amri iliyoainishwa katika ombi na kurudisha jibu. */
/* Ikiwa sehemu ya kuingia ya maktaba iliyoshirikiwa inaweza kupatikana, amri */
/* hutekelezwa ndani ya mchakato, vinginevyo nje ya mchakato. */
/* KIPENGELE CHA SI LAZIMA: argv inaweza kuwa na jina la maktaba */
/* DLL inayounga mkono seva katika mchakato. */

#include "ClntSrvr.h" /* Inafafanua muundo wa rekodi za ombi na majibu. */

/* Muundo wa anwani wa tundu la seva. */
tengeneza sockaddr_in ConnectSAddr; /* Soketi iliyounganishwa. */
WSADATA WSStartData; /* Muundo wa data wa maktaba ya soketi. */

muundo wa typedef SERVER_ARG_TAG ( /* Hoja za nyuzi za seva. */
/* Maelezo yamo kwenye maoni kwenye uzi mkuu. */
HINSTANCE dlhandle; /* Hushughulikia maktaba iliyoshirikiwa. */

tuli ShutFlag = FALSE;
SOCKET tuli SrvSock, ConnectSock;
int _tmain(DWORD argc, LPCTSTR argv) (
/* Usikilizaji wa seva na soketi zilizounganishwa. */
SERVER_ARG srv_arg;
/* Anzisha maktaba ya WSA; toleo la 2.0 limebainishwa, lakini toleo la 1.1 pia litafanya kazi. */
WSAStartup(MAKEWORD(2, 0), &WSStartData);
/* Fungua maktaba ya amri yenye nguvu ikiwa jina lake limetajwa kwenye mstari wa amri. */
ikiwa (argc> 1) hDll = LoadLibrary(argv);
/* Anzisha safu ya safu ya mazungumzo. */
kwa (ith = 0; ith< MAXCLIENTS; ith++) {
srv_arg.dlhandle = hDll;
/* Fuata utaratibu wa kawaida wa kuita soketi/bind/sikiliza/kubali mfuatano wa utendakazi na mteja. */
SrvSock = tundu(AF_INET, SOCK_STREAM, 0);
SrvSAddr.sin_family = AF_INET;
SrvSAddr.sin_addr.s_addr = htonl(INADDR_ANY);
SrvSAddr.sin_port = htons(SERVER_PORT);
bind(SrvSock, (muundo sockaddr *)&SrvSAddr, sizeof SrvSAddr);
sikiliza(SrvSock, MAX_CLIENTS);

/* Uzi mkuu unakuwa uzi wa kusikiliza/kuunganisha/kudhibiti.*/
/* Tafuta kisanduku tupu katika safu ya safu ya safu ya seva. */
/* parameter ya hali: 0 - kiini ni bure; 1 - mtiririko umesimama; 2 - thread inaendesha; 3 - mfumo mzima umesimamishwa. */
kwa (ith = 0; ith< MAX_CLIENTS && !ShutFlag;) {
ikiwa (srv_arg.status==1 || srv_arg.status==3) ( /* Utekelezaji wa thread umekatishwa kama kawaida au kwa sababu ya ombi la kusitisha. */
WaitForSingleObject(srv_arg.srv_thd INFINITE);
CloseHandle(srv_arg.srv_tnd);
ikiwa (srv_arg.status == 3) ShutFlag = TRUE;
kingine srv_arg.status = 0;
/* Achia kisanduku cha mazungumzo haya. */
ikiwa (srv_arg.status == 0 || ShutFlag) itavunjika;
ith = (ith + 1) % MAXCLIENTS;
/* Kataza mzunguko wa upigaji kura. */
/* Chaguo mbadala: Tumia tukio kutoa ishara inayoonyesha kuwa kisanduku kimetolewa. */
/* Subiri jaribio la muunganisho kwenye soketi hii. */
/* Tenganisha uzi ili kupigia kura ShutFlag. */
hAcceptTh = (HANDLE)_beginthreadex(NULL, 0, AcceptTh, &srv_arg, 0, &ThId);
tstatus = WaitForSingleObject(hAcceptTh, CS_TIMEOUT);
ikiwa (hali == WAIT_OBJECT_0) itavunjika; /* Muunganisho umeanzishwa. */
hAcceptTh = NULL; /* Jitayarishe kwa muunganisho unaofuata. */
_tprintf(_T("Inasimamisha seva. Inasubiri minyororo yote ya seva ikamilike\n"));
/* Sitisha uzi wa kupokea ikiwa bado unaendelea. */
/* Maelezo zaidi juu ya mantiki ya kukamilisha iliyotumiwa */
/* kazi zimeorodheshwa kwenye Tovuti ya kitabu. */
ikiwa (hDll != NULL) FreeLibrary(hDll);
ikiwa (hAcceptTh != NULL) TerminateThread(hAcceptTh, 0);
/* Subiri hadi nyuzi zote zinazotumika za seva zikamilike. */
kwa (ith = 0; ith< MAXCLIENTS; ith++) if (srv_arg .status != 0) {
WaitForSingleObject(srv_arg.srv_thd, INFINITE);
CloseHandle(srv_arg.srv_thd);

tuli DWORD WINAPI AcceptTh(SERVER_ARG * pThArg) (
/* Uzi wa kupokea, unaoruhusu uzi mkuu kupigia kura bendera ya kusitisha. Zaidi ya hayo, thread hii inaunda thread ya seva. */
AddrLen = sizeof(ConnectSAddr);
pThArg->sock = kubali(SrvSock, /* Hii ni simu inayozuia. */
(struct sockaddr *)&ConnectSAddr, &AddrLen);
/* Muunganisho mpya. Unda uzi wa seva. */
pThArg->srv_thd = (HANDLE)_beginthreadex(NULL, 0, Seva, pThArg, 0, &ThId);
kurudi 0; /* Mzunguko wa seva unaendelea kufanya kazi. */

Seva tuli ya DWORD WINAPI(SERVER_ARG * pThArg)
/* Kitendaji cha nyuzi za seva. Mtiririko huundwa kwa mahitaji. */
/* Kila mazungumzo hudumisha ombi lake, majibu, na miundo ya data ya kumbukumbu kwenye rafu. */
/* ... Matangazo ya kawaida kutoka kwa sevaNP yameachwa ... */
int (*dl_addr)(char *, char *);
char *ws = "\0\t\n"; /* Nafasi. */
GetStartupInfo(&StartInfoCh);
/* Unda jina la faili la muda. */
sprintf(TempFile, "%s%d%s", "ServerTemp", pThArg->nambari, ".tmp");
wakati (!Imekamilika && !ShutFlag) ( /* Kitanzi cha amri kuu. */
Tenganisha = ReceiveRequestMessage(&Request, ConnectSock);
Imekamilika = Tenganisha || (strcmp(Request.Record, "$Quit") == 0) || (strcmp(Request.Record, "$ShutDownServer") == 0);
/* Komesha mazungumzo haya inapopokea amri ya "$Quit" au "$ShutDownServer". */
hTrapFile = CreateFile(TempFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &TempSA, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
/* Angalia uwepo wa amri hii kwenye DLL. Ili kurahisisha amri */
/* maktaba zilizoshirikiwa zina zaidi kipaumbele cha juu ikilinganishwa */
/* na amri za mchakato. Kwanza kabisa, unahitaji kutoa jina la amri.*/
i = strcspn(Request.Record, ws); /* Saizi ya ishara. */
memcpy(sys_command, Request.Record, i);
dl_addr = NULL; /* Itawekwa ikiwa kitendakazi cha GetProcAddress kimefaulu. */
if (pThArg->dlhandle != NULL) (/* Inaangalia usaidizi wa seva ya "in-process". */
dl_addr = (int (*)(char *, char *))GetProcAddress(pThArg->dlhandle, sys_command);
/* Linda mchakato wa seva dhidi ya vighairi vilivyotupwa kwenye DLL*/
(*dl_addr)(Request.Record, TempFile);
) __isipokuwa (EXCEPTION_EXECUTE_HANDLER) (
ReportError(_T("Ubaguzi katika DLL"), 0, FALSE);
ikiwa (dl_addr == NULL) ( /* Hakuna usaidizi wa seva katika mchakato. */
/* Unda mchakato wa kutekeleza amri. */
/* ... Sawa na kwenye sevaNP ... */
) /* Mwisho wa kitanzi cha amri kuu. Pata amri inayofuata. */
/* Mwisho wa mzunguko wa amri. Fungua rasilimali; kutoka nje ya mtiririko. */
_tprintf(_T("Kuzima kwa seva# %d\n"), pThArg->nambari);
ikiwa (strcmp(Request.Record, "$ShutDownServer") == 0) (

Vidokezo vya Usalama

Kama inavyoonyeshwa hapa, mfumo huu wa seva ya mteja Sivyo iko salama. Ikiwa mfumo wako unatumia seva na mtu anajua nambari ya bandari unayotumia na jina la kompyuta, basi anaweza kushambulia mfumo wako. Mtumiaji mwingine anayeendesha programu ya mteja kwenye kompyuta yake ataweza kutekeleza amri kwenye mfumo wako ambazo, kwa mfano, zinawaruhusu kufuta au kubadilisha faili.

Mjadala kamili wa mbinu za kujenga mifumo salama uko nje ya upeo wa kitabu hiki. Walakini, Sura ya 15 inaonyesha jinsi ya kupata usalama Windows vitu, na Exercise 12.14 inapendekeza kutumia itifaki ya SSL.

Seva Katika Mchakato

Kama ilivyoonyeshwa hapo awali, uboreshaji kuu wa programu ya sevaSK inahusiana na kujumuishwa kwa seva za mchakato. Mpango wa 12.3 unaonyesha jinsi ya kuandika DLL ambayo hutoa aina hii ya huduma. Programu ina kazi mbili ambazo tayari unajua - kazi ya kuhesabu neno na kazi ya juu.

Kwa mkataba, parameta ya kwanza ni mstari wa amri na ya pili ni jina la faili la pato. Zaidi ya hayo, unapaswa kukumbuka kila wakati kwamba chaguo la kukokotoa litakuwa likifanya kazi kwenye uzi sawa na seva, na hii inahitaji mahitaji madhubuti ya usalama wa nyuzi, pamoja na, lakini sio tu, yafuatayo:

Kazi lazima zisibadilishe mazingira ya mchakato kwa njia yoyote. Kwa mfano, ikiwa moja ya kazi itabadilisha saraka ya kazi, itaathiri mchakato mzima.

Vile vile, chaguo za kukokotoa hazipaswi kuelekeza upya pembejeo na matokeo ya kawaida.

Hitilafu za kupanga kama vile faharasa au kielekezi nje ya mipaka au kufurika kwa rafu kunaweza kuharibu kumbukumbu ya uzi mwingine au mchakato wenyewe.

Uvujaji wa rasilimali, kama vile ule unaotokana na kushindwa kurudisha kumbukumbu iliyoachiliwa mara moja kwenye mfumo au kushindwa kufunga vishikizo, hatimaye utakuwa na athari mbaya katika uendeshaji wa mfumo mzima wa seva.

Mahitaji makali kama haya hayawekwa kwa michakato kwa sababu mchakato mmoja, kama sheria, hauwezi kusababisha uharibifu kwa michakato mingine, na baada ya mchakato huo kukamilisha utekelezaji wake, rasilimali iliyochukua hutolewa kiatomati. Kwa sababu ya hii, huduma kawaida hutengenezwa na kutatuliwa kama uzi, na tu baada ya kujiamini kuwa itafanya kazi kwa uhakika inabadilishwa kuwa DLL.

Mpango wa 12.3 hutoa DLL ndogo ambayo ina kazi mbili.

Mpango 12.3. amri: mfano ndani ya seva za mchakato
/* Sura ya 12. amri.p. */
/* Amri za seva za mchakato wa matumizi ya sevaSK na kadhalika. */
/* Kuna amri kadhaa zinazotekelezwa kama DLL. */
/* Kila kazi ya amri inachukua vigezo viwili na hutoa */
/* utekelezaji salama katika hali ya nyuzi nyingi. Kigezo cha kwanza */
/* ndio kamba: amri arg1 arg2 ... argn */
/* (yaani, mstari wa amri ya kawaida), na ya pili ni jina la faili ya pato. …*/

tuli utupu extract_token(int, char *, char *);

int wcip (char * amri, char * output_file)
/* Kaunta ya maneno; inashughulikiwa. */
/* KUMBUKA: toleo lililorahisishwa; matokeo yanaweza kutofautiana na yale yaliyotolewa na matumizi ya wc. */
wakati ((c = fgetc(fin)) != EOF) (
/* … Nambari ya kawaida - sio muhimu kwa mfano huu ... */
/* Andika matokeo. */
fprintf(fout, " %9d %9d %9d %s\n", nl, nw, nc, input_file);

int topperip (char * amri, char * output_file)
/* Hubadilisha ingizo kuwa herufi kubwa; kutekelezwa ndani ya mchakato. */
/* Ishara ya pili inabainisha faili ya ingizo (ishara ya kwanza ni "toupperip"). */
extract_token(1, amri, faili_ingizo);
fin = fopen(input_file, "r");
fout = fopen(output_file, "w");
wakati ((c = fgetc (fin)) != EOF) (
ikiwa (isalpha(c)) c = juu(c);

tuli utupu extract_token (int it, char * amri, char * ishara) (
/* Hutoa nambari ya ishara "it" kutoka kwa "amri" (idadi ya ishara ya kwanza */
/* ni "0"). Matokeo huingia kwenye "ishara" */
/* Nafasi zinatumika kama vikomo vya tokeni. …*/

Ujumbe wenye mwelekeo wa mstari , sehemu za kusafiri za DLL na TLS

Programu za sevaSK na mtejaSK huwasiliana kwa kubadilishana ujumbe, ambayo kila moja ina kichwa cha baiti 4 kilicho na saizi ya ujumbe na yaliyomo yenyewe. Njia mbadala ya kawaida ya mbinu hii ni kwamba ujumbe hutenganishwa kutoka kwa kila mmoja kwa herufi za mwisho wa mstari (au mlisho wa laini).

Ugumu wa kufanya kazi na ujumbe huo ni kwamba urefu wa ujumbe haujulikani mapema, na kwa hiyo kila tabia inayoingia inapaswa kuchunguzwa. Hata hivyo, kurejesha herufi moja kwa wakati mmoja haifai sana, na kwa hivyo vibambo huhifadhiwa katika bafa ambayo maudhui yake yanaweza kujumuisha herufi moja au zaidi ya mwisho wa mstari na vijenzi vya ujumbe mmoja au zaidi. Katika kesi hii, katika vipindi kati ya simu kwa kazi ya kupokea ujumbe, ni muhimu kudumisha yaliyomo na hali ya buffer bila kubadilika. Katika mazingira ya thread moja, seli za kumbukumbu za tuli zinaweza kutumika kwa kusudi hili, lakini kugawana Nyuzi nyingi kwa kutumia utofauti sawa wa tuli haziwezekani.

Katika uundaji wa jumla zaidi, tunakabiliwa hapa tatizo la kuokoa majimbo ya muda mrefu katika mazingira yenye nyuzi nyingi(tatizo la hali inayoendelea yenye nyuzi nyingi). Tatizo hili hutokea wakati wowote utendakazi wa uzi-salama lazima usaidie kuendelea kwa baadhi ya taarifa kutoka kwa simu moja ya kukokotoa hadi nyingine. Tatizo sawa hutokea wakati wa kufanya kazi na kazi ya strook iliyojumuishwa maktaba ya kawaida C, ambayo imeundwa kuchanganua mfuatano ili kupata matukio mfuatano ya tokeni fulani.

Kutatua tatizo la majimbo ya muda mrefu katika mazingira yenye nyuzi nyingi

Suluhisho linalohitajika linachanganya vipengele kadhaa:

DLL ambayo ina vitendaji vya kutuma na kupokea ujumbe.

Chaguo la kukokotoa ambalo linawakilisha mahali pa kuingilia kwa DLL.

Tiririsha Eneo la Hifadhi ya Ndani (TLS, Sura ya 7). Kuunganisha mchakato kwenye maktaba kunafuatana na kuundwa kwa index ya DLL, na kuiondoa kunafuatana na uharibifu. Thamani ya faharisi huhifadhiwa katika hifadhi tuli inayoweza kufikiwa na minyororo yote.

Muundo ambao buffer na hali yake ya sasa huhifadhiwa. Muundo huo husambazwa wakati wowote nyuzi mpya inapojiunga na maktaba, na anwani yake huhifadhiwa kwenye ingizo la TLS la mazungumzo hayo. Wakati thread imezuiliwa kutoka kwa maktaba, kumbukumbu iliyochukuliwa na muundo wake inafunguliwa.

Kwa hivyo, TLS hufanya kama duka tuli, na kila uzi una nakala yake ya kipekee ya duka hili.

Mfano: DLL salama ya nyuzi kwa utumaji soketi

Programu 12.4 ni DLL iliyo na vitendaji viwili vya kuchakata masharti ya tabia(ambao majina yao katika kesi hii yana "CS", kutoka kamba ya tabia- mfuatano wa herufi), au vipengele vya utiririshaji wa tundu: SendCSMessage na ReceiveCSMessage, pamoja na sehemu ya kuingilia ya DllMain (ona Sura ya 5). Vitendaji hivi viwili vina jukumu sawa na chaguo la kukokotoa la ReceiveMessage, pamoja na vitendakazi vinavyotumika katika Programu za 12.1 na 12.2, na kwa hakika kuzibadilisha.

Chaguo za kukokotoa za DllMain ni kielelezo wakilishi cha kutatua tatizo la hali ya muda mrefu katika mazingira yenye nyuzi nyingi na kuchanganya TLS na DLL.

Kufungua rasilimali wakati nyuzi zimezuiliwa (kesi ya DLL_THREAD_DETACH) ni muhimu sana katika mazingira ya seva; Usipofanya hivi, rasilimali za seva hatimaye zitakwisha, ambayo inaweza kusababisha kuanguka, kuteseka kutokana na utendakazi duni, au zote mbili.

Kumbuka

Baadhi ya dhana zilizoonyeshwa hapa chini hazihusiani moja kwa moja na soketi, lakini hata hivyo zimefunikwa hapa badala ya sura zilizopita kwa sababu mfano unatoa fursa muhimu ya kuonyesha mbinu za kuunda DLL zisizo salama katika mpangilio halisi.

Msimbo wa mteja na seva ambayo hutumia DLL hii, iliyobadilishwa kidogo kutoka kwa programu 12.1 na 12.2, inapatikana kwenye Tovuti ya kitabu.

Mpango 12.4. SendReceiveSKST: Thread Salama DLL
/* SendReceiveSKST.с - DLL ya soketi ya mtiririko yenye nyuzi nyingi. */
/* Vibambo vya kukomesha hutumiwa kama vikomo vya ujumbe */
/* mifuatano ("\0"), kwa hivyo ukubwa wa ujumbe haujulikani mapema. */
/* Data inayoingia imehifadhiwa na kuhifadhiwa kati ya */
/* simu za chaguo. */
/* Sehemu za uhifadhi wa ndani hutumika kwa kusudi hili */
/* (Hifadhi ya Ndani ya Thread, TLS) ikitoa kila moja ya nyuzi */
/* mwenyewe "hifadhi tuli" ya kibinafsi. */

#include "ClntSrvr.h" /* Inafafanua rekodi za ombi na majibu. */

/* "static_buf" ina baiti za "static_buf_len" za data iliyobaki. */
/* Mwisho wa herufi (vibambo tupu) vinaweza kuwepo au visiwepo */
/* na usihudhurie. */
char tuli_buf ;

tuli DWORD TlsIx = 0; /* Kielezo cha TLS - KILA MCHAKATO UNA KIELEKEZO CHAKE.*/
/* Kwa maktaba yenye uzi mmoja ufafanuzi ufuatao utatumika:
char tuli tuli_buf ;
tuli LONG32 tuli_buf_len; */
/* Kazi kuu ya DLL. */

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvImehifadhiwa) (
/* Hakuna muunganisho wa uzi kuu, kwa hivyo shughuli za uunganisho wa nyuzi lazima pia zifanywe wakati mchakato unaunganishwa. */
/* Onyesha kuwa kumbukumbu haijatolewa. */
rudisha KWELI; /* Thamani hii kwa kweli imepuuzwa. */
/* Ondoa uzi mkuu pia. */

BOOL ReceiveCSMessage(REQUEST *pRequest, SOCKET sd) (
/* Thamani ya kurejesha TRUE inaonyesha hitilafu au kukatwa. */
LONG32 nRemainRecv = 0, nXfer, k; /* Lazima iwe nambari kamili. */
CHAR TempBuf;
p = (STATIC_BUF *)TlsGetValue(TlsIx);
ikiwa (p == NULL) ( /* Anzisha kwa simu ya kwanza. */
/* Hifadhi hii itasambazwa tu na nyuzi zinazohitaji */
/* muhimu. Aina zingine za mitiririko zinaweza kutumia TLS kwa madhumuni mengine. */
p = malloc(sizeof(STATIC_BUF));
ikiwa (p == NULL) itarudi TRUE; /* Hitilafu. */
p->static_buf_len = 0; /* Anzisha jimbo. */
/* Hesabu hadi herufi mpya, ukiacha data iliyobaki katika bafa tuli. */
kwa (k = 0; k< p->static_buf_len && p->static_buf[k] != "\0"; k++) (
message[k] = p->static_buf[k];
) /* k - idadi ya wahusika zinazopitishwa. */
ikiwa (k< p->static_buf_len) ( /* Herufi batili iligunduliwa kwenye bafa tuli. */
p->static_buf_len –= (k + 1); /* Rekebisha hali ya bafa tuli. */
memcpy(p->static_buf, &(p->static_buf), p->static_buf_len);
rudisha UONGO; /* Hakuna ingizo la tundu linalohitajika. */

/* Bafa nzima tuli imehamishwa. Kisimamishaji cha laini hakijatambuliwa.*/
nRemainRecv = sizeof(TempBuf) – 1 – p->static_buf_len;
pBuffer = ujumbe + p->static_buf_len;
wakati (nRemainRecv > 0 && !Tenganisha) (
nXfer = recv(sd, TempBuf, nRemainRecv, 0);
/* Sambaza herufi zote hadi kwa herufi batili, ikiwa ipo, kwa ujumbe lengwa. */
kwa (k =0; k< nXfer && TempBuf[k] != "\0"; k++) {
ikiwa (k >= nXfer) ( /*Kisimamishaji cha laini hakijagunduliwa, soma zaidi*/
) kwingine ( /* Kisimamishaji cha laini kimegunduliwa. */
memcpy(p->static_buf, &TempBuf, nXfer – k – 1);
p->static_buf_len = nXfer – k – 1;

BOOL SendCSMessage(RESPONSE *pResponse, SOCKET sd) (
/* Tuma ombi kwa seva kwenye tundu la sd. */
nRemainSend = strlen(pBuffer) + 1;
wakati (nRemainTuma > 0 && !Tenganisha) (
/* Kutuma hakuhakikishi kuwa ujumbe wote utatumwa. */
nXfer = tuma(sd, pBuffer, nRemainSend, 0);
fprintf(stderr, "\nKutenganisha seva kabla ya kutuma ombi la kukamilisha");

Ninapenda sana mfululizo mzima wa makala, pamoja na sikuzote nilitaka kujijaribu kama mtafsiri. Labda nakala hiyo itaonekana wazi sana kwa watengenezaji wenye uzoefu, lakini inaonekana kwangu kuwa itakuwa muhimu kwa hali yoyote.
Makala ya kwanza - http://habrahabr.ru/post/209144/

Mapokezi na usambazaji wa pakiti za data

Utangulizi
Hujambo, jina langu ni Glenn Fiedler na ninakukaribisha kwa makala yangu ya pili katika mfululizo wa "Kupanga Mtandao kwa Wasanidi wa Michezo".

Katika makala iliyotangulia, tulijadili njia mbalimbali za kuhamisha data kati ya kompyuta kwenye mtandao, na mwisho tuliamua kutumia itifaki ya UDP badala ya TCP. Tuliamua kutumia UDP ili kuweza kutuma data bila ucheleweshaji unaohusishwa na kusubiri pakiti kutumwa tena.

Sasa nitakuambia jinsi ya kutumia UDP kutuma na kupokea pakiti kwa vitendo.

Soketi za BSD
Mifumo mingi ya kisasa ya uendeshaji ina aina fulani ya utekelezaji wa tundu kulingana na soketi za BSD (soketi za Berkeley).

Soketi za BSD hufanya kazi kwa vitendaji rahisi kama vile "soketi", "bind", "sendto" na "recvfrom". Bila shaka, unaweza kufikia kazi hizi moja kwa moja, lakini katika kesi hii kanuni yako itakuwa tegemezi ya jukwaa, kwa kuwa utekelezaji wao unaweza kutofautiana kidogo kwenye mifumo tofauti ya uendeshaji.

Kwa hivyo, ingawa nitatoa zaidi mfano rahisi wa kwanza wa mwingiliano na soketi za BSD, kwa kile kinachofuata hatutazitumia moja kwa moja. Badala yake, baada ya kusimamia utendakazi wa kimsingi, tutaandika madarasa kadhaa ambayo yanazingatia kazi yote na soketi, ili katika siku zijazo msimbo wetu utakuwa huru wa jukwaa.

Vipengele vya OS tofauti
Kwanza, wacha tuandike nambari ambayo itaamua OS ya sasa ili tuweze kuzingatia tofauti katika utendakazi wa soketi:

// utambuzi wa jukwaa #fafanua PLATFORM_WINDOWS 1 #fafanua PLATFORM_MAC 2 #fafanua PLATFORM_UNIX 3 #if defined(_WIN32) #define PLATFORM WINDOWS #elif defined(__APPLE__) #define PLATFORMPLATAT _UNIX #endif
Sasa hebu tujumuishe faili za kichwa zinazohitajika kufanya kazi na soketi. Kwa kuwa seti ya faili za kichwa zinazohitajika inategemea OS ya sasa, hapa tunatumia nambari ya #define iliyoandikwa hapo juu ili kuamua ni faili gani zinazohitajika kuingizwa.

#kama PLATFORM == PLATFORM_WINDOWS #pamoja na #elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX #pamoja na #pamoja na #pamoja na #endif
Kwenye mifumo ya UNIX, vitendaji vya tundu vinajumuishwa kama kawaida. maktaba za mfumo, kwa hivyo hatuhitaji maktaba zozote za watu wengine katika kesi hii. Walakini, kwenye Windows, kwa madhumuni haya tunahitaji kujumuisha maktaba ya winsock.

Hapa kuna hila kidogo juu ya jinsi unaweza kufanya hivyo bila kubadilisha mradi au faili:

#kama PLATFORM == PLATFORM_WINDOWS #pragma comment(lib, "wsock32.lib") #endif
Ninapenda ujanja huu kwa sababu mimi ni mvivu. Unaweza, bila shaka, kujumuisha maktaba katika mradi au katika faili ya maandishi.

Kuanzisha Soketi
Kwenye mifumo mingi ya uendeshaji kama unix (pamoja na macosx) hakuna hatua maalum zinazohitajika ili kuanzisha utendakazi wa soketi, lakini kwenye Windows unahitaji kufanya hatua kadhaa kwanza - unahitaji kupiga kazi ya "WSAStartup" kabla ya kutumia kazi zozote za tundu, na baada ya kumaliza kazi - piga "WSACleanup".

Hebu tuongeze vipengele viwili vipya:

Inline bool InitializeSockets() ( #kama PLATFORM == PLATFORM_WINDOWS WSADATA WsaData; rudisha WSAStartup(MAKEWORD(2,2), &WsaData) == NO_ERROR; #else return true; #endif ) inline void ShutdownSockets() (RMFORM=if= PLATFORM=) PLATFORM_WINDOWS WSACleanup(); #endif )
Sasa tuna uanzishaji wa soketi unaojitegemea na msimbo wa kuzima. Kwenye majukwaa ambayo hayahitaji kuanzishwa, nambari hii haifanyi chochote.

Unda tundu
Sasa tunaweza kuunda Soketi ya UDP. Hii inafanywa kama hii:

Int kushughulikia = soketi(AF_INET, SOCK_DGRAM, IPPROTO_UDP); ikiwa (kushughulikia<= 0) { printf("failed to create socket\n"); return false; }
Ifuatayo, lazima tufunge tundu kwa nambari maalum ya bandari (kwa mfano, 30000). Kila tundu lazima iwe na bandari yake ya kipekee, kwa sababu wakati pakiti mpya inakuja, nambari ya bandari huamua ambayo tundu inatumwa. Usitumie nambari za bandari chini ya 1024 - zimehifadhiwa na mfumo.

Ikiwa haujali ni nambari gani ya bandari ya kutumia kwa tundu, unaweza kupitisha "0" kwa chaguo la kukokotoa, na kisha mfumo wenyewe utakutengea bandari isiyo na mtu.

Sockaddr_in anwani; address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons ((fupi isiyo na saini) bandari); ikiwa (bind(hand, (const sockaddr*) &anwani, sizeof(sockaddr_in))< 0) { printf("failed to bind socket\n"); return false; }
Sasa soketi yetu iko tayari kutuma na kupokea pakiti za data.

Lakini kazi hii ya ajabu ya "htons" inaitwaje katika msimbo? Hiki ni kitendakazi kidogo cha msaidizi ambacho hubadilisha mpangilio wa baiti ya nambari kamili ya biti 16 kutoka ya sasa (kidogo- au-endian-kubwa) hadi ya mwisho-kubwa ambayo hutumiwa kwa mawasiliano ya mtandao. Inahitaji kuitwa kila wakati unapotumia nambari kamili wakati wa kufanya kazi na soketi moja kwa moja.

Utaona kazi ya "htons" na mwenzake wa 32-bit "htonl" mara kadhaa zaidi katika makala hii, kwa hiyo makini.

Kubadilisha tundu kwa hali isiyo ya kuzuia
Kwa chaguo-msingi, soketi ziko katika kile kinachoitwa "hali ya kuzuia". Hii ina maana kwamba ukijaribu kusoma data kutoka kwayo kwa kutumia "recvfrom", kazi haitarudi mpaka tundu lipokea pakiti na data ambayo inaweza kusoma. Tabia hii haitufai hata kidogo. Michezo ni programu za wakati halisi zinazotumia fremu 30 hadi 60 kwa sekunde, na mchezo hauwezi tu kusimama na kusubiri pakiti ya data ifike!

Unaweza kutatua tatizo hili kwa kubadili tundu kwenye "mode isiyo ya kuzuia" baada ya kuundwa. Katika hali hii, kazi ya "recvfrom", ikiwa hakuna data ya kusoma kutoka kwenye tundu, mara moja inarudi thamani fulani inayoonyesha kwamba itahitaji kuitwa tena wakati kuna data kwenye tundu.

Unaweza kuweka tundu kwa hali isiyozuia kama ifuatavyo:

#kama PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX int nonBlocking = 1; ikiwa (fcntl(kushughulikia, F_SETFL, O_NONBLOCK, nonBlocking) == -1) ( printf("imeshindwa kuweka tundu lisilozuia\n"); rudisha sivyo; ) #elif PLATFORM == PLATFORM_WINDOWS DWORD nonBlocking = 1; ikiwa (ioctlsocket(handle, FIONBIO, &nonBlocking) != 0) ( printf("imeshindwa kuweka tundu lisilozuia\n"); rudisha sivyo; ) #endif
Kama unavyoona, hakuna kitendakazi cha "fcntl" katika Windows, kwa hivyo tunatumia "ioctlsocket" badala yake.

Kutuma vifurushi
UDP ni itifaki isiyo na muunganisho, kwa hivyo kila wakati tunapotuma pakiti tunahitaji kubainisha anwani ya mpokeaji. Unaweza kutumia tundu sawa la UDP kutuma pakiti kwa anwani tofauti za IP - mwisho mwingine wa tundu sio lazima iwe kompyuta sawa.

Unaweza kusambaza pakiti kwa anwani maalum kama ifuatavyo:

Int sent_bytes = sendto( handle, (const char*)packet_data, packet_size, 0, (sockaddr*)&address, sizeof(sockaddr_in)); ikiwa (sent_bytes != packet_size) ( printf("imeshindwa kutuma pakiti: return value = %d\n", sent_bytes); rudisha sivyo; )
Tafadhali kumbuka kuwa thamani ya kurejesha kutoka kwa chaguo la kukokotoa la "sendto" inaonyesha tu ikiwa pakiti ilitumwa kwa ufanisi kutoka kwa kompyuta ya ndani. Lakini haionyeshi ikiwa pakiti ilipokelewa na marudio! UDP haina njia ya kubainisha kama pakiti imefika kulengwa kwake au la.

Katika msimbo ulio hapo juu, tunapitisha muundo wa "sockaddr_in" kama anwani lengwa. Je, tunapataje muundo huu?

Wacha tuseme tunataka kutuma pakiti kwa 207.45.186.98:30000.

Wacha tuandike anwani katika fomu ifuatayo:

Haijasainiwa int a = 207; unsigned int b = 45; unsigned int c = 186; unsigned int d = 98; bandari fupi isiyosajiliwa = 30000;
Na unahitaji kufanya mabadiliko kadhaa ili kuileta kwa fomu ambayo "sendto" inaelewa:

Int destination_address isiyo na saini = (a<< 24) | (b << 16) | (c << 8) | d; unsigned short destination_port = port; sockaddr_in address; address.sin_family = AF_INET; address.sin_addr.s_addr = htonl(destination_address); address.sin_port = htons(destination_port);
Kama unavyoona, kwanza tunachanganya nambari a, b, c, d (ambazo ziko kwenye safu ) kuwa nambari moja, ambayo kila baiti ni moja ya nambari asili. Kisha tunaanzisha muundo wa "sockaddr_in" kwa anwani yetu na mlango wetu, tukikumbuka kubadilisha mpangilio wa baiti kwa kutumia vitendaji vya "htonl" na "htons".

Inafaa kuangazia kesi hiyo kando wakati unahitaji kujituma pakiti: katika kesi hii, hauitaji kupata anwani ya IP ya mashine ya ndani, lakini unaweza kutumia 127.0.0.1 tu kama anwani (kitanzi cha ndani. anwani), na pakiti itatumwa kwa kompyuta ya ndani.

Kupokea vifurushi
Baada ya kufunga soketi ya UDP kwenye bandari, pakiti zote za UDP zinazofika kwenye anwani ya IP na mlango wa soketi zetu zitawekwa kwenye foleni. Kwa hiyo, ili kupokea pakiti, tunaita tu "recvfrom" katika kitanzi mpaka inatupa hitilafu, ikimaanisha kuwa hakuna pakiti zilizobaki kwenye foleni ya kusoma.

Kwa sababu UDP haina muunganisho, pakiti zinaweza kutoka kwa kompyuta nyingi tofauti kwenye mtandao. Kila wakati tunapopokea pakiti, chaguo za kukokotoa za "recvfrom" hutupatia anwani ya IP na mlango wa mtumaji, ili tujue ni nani aliyetuma pakiti.

Msimbo wa kupokea pakiti kwenye kitanzi:

Wakati (kweli) (data_ya_char pakiti_isiyotiwa saini; int maximum_packet_size = sizeof(packet_data); #kama PLATFORM == PLATFORM_WINDOWS typedef int socklen_t; #endif sockaddr_in from; socklen_t fromLength = saizi ya(kutoka_kwa_kutoka); (chara *)data_ya_pakiti, saizi_ya_pakiti ya upeo, 0, (sockaddr*)&kutoka, &kutokaUrefu); ikiwa (baiti_zilizopokewa<= 0) break; unsigned int from_address = ntohl(from.sin_addr.s_addr); unsigned int from_port = ntohs(from.sin_port); // process received packet }
Pakiti ambazo ni kubwa kuliko saizi ya bafa ya kupokea zitaondolewa tu kimyakimya kutoka kwenye foleni. Kwa hivyo ukitumia bafa ya baiti 256 kama ilivyo kwenye mfano hapo juu, na mtu akakutumia pakiti ya baiti 300, itatupwa. Hutapata tu baiti 256 za kwanza kutoka kwa pakiti.

Lakini kwa kuwa tunaandika itifaki yetu wenyewe, hii haitakuwa shida kwetu. Kuwa mwangalifu kila wakati na uhakikishe kuwa saizi ya bafa ya kupokea ni kubwa ya kutosha kubeba pakiti kubwa zaidi inayoweza kutumwa kwako.

Kufunga tundu
Kwenye mifumo mingi inayofanana na unix, soketi ni vielezi vya faili, kwa hivyo kitendakazi cha kawaida cha "funga" kinaweza kutumika kufunga soketi baada ya matumizi. Walakini, Windows, kama kawaida, inasimama, na ndani yake tunahitaji kutumia "closesocket".

#kama PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX funga(tundu); #elif PLATFORM == PLATFORM_WINDOWS closesocket(tundu); #endif
Endelea hivyo, Windows!

Darasa la soketi
Kwa hiyo, tumegundua shughuli zote za msingi: kuunda tundu, kuifunga kwenye bandari, kubadili mode isiyo ya kuzuia, kutuma na kupokea pakiti, na hatimaye kufunga tundu.

Lakini, kama unaweza kuwa umeona, shughuli hizi zote hutofautiana kidogo kutoka jukwaa hadi jukwaa, na, bila shaka, ni vigumu kukumbuka vipengele vya majukwaa tofauti na kuandika haya yote #ifdefs kila wakati wakati wa kufanya kazi na soketi.

Kwa hivyo, tutafanya darasa la wrapper "Soketi" kwa shughuli hizi zote. Pia tutaunda darasa la "Anwani" ili iwe rahisi kufanya kazi na anwani za IP. Itaturuhusu tusitekeleze ghiliba zote kwa "sockaddr_in" kila wakati tunapotaka kutuma au kupokea pakiti.

Kwa hivyo, darasa letu la Soketi:

Soketi ya Hatari ( hadharani: Soketi(); ~Soketi(); bool Fungua(mlango fupi usio na saini); utupu Funga(); bool IsOpen() const; bool Tuma(const Anwani & lengwa, const void * data, int size); int Pokea(Anwani & mtumaji, batili * data, int size); faragha: int handle; );
Na darasa la Anwani:

Anwani ya Darasa ( hadharani: Anwani(); Anwani(char a ambayo haijatiwa saini, char b ambayo haijatiwa saini, char c ambayo haijatiwa saini, char d isiyotiwa saini, bandari fupi isiyotiwa saini); Anwani(anwani ya int ambayo haijatiwa saini, bandari fupi isiyotiwa saini); int GetAddress() const isiyotiwa saini; chapa ambayo haijatiwa saini GetA() const; chapa ambayo haijatiwa saini GetB() const; chapa ambayo haijatiwa saini GetC() const; chapa ambayo haijatiwa saini GetD() const; fupi isiyotiwa saini GetPort() const; bool operator == (consst Address & other) const; bool operator ! = (Const Address & other) const; faragha: anwani ya int ambayo haijasainiwa; bandari fupi isiyo na saini; );
Unahitaji kuzitumia kwa mapokezi na usambazaji kama ifuatavyo:

// tengeneza tundu const int bandari = 30000; tundu la tundu; ikiwa (!soketi.Open(port)) ( printf("imeshindwa kuunda soketi!\n"); rudisha sivyo; ) // tuma pakiti const char data = "hello world!"; soketi.Tuma(Anwani(127,0,0,1,bandari), data, saizi(data)); // pokea vifurushi wakati (kweli) ( Anwani ya mtumaji; bafa ya char isiyotiwa saini; int bytes_read = socket. Pokea(mtumaji, bafa, saizi ya(bafa)); ikiwa (!baiti_kusoma) itavunjika; // pakiti ya mchakato)
Kama unaweza kuona, hii ni rahisi zaidi kuliko kufanya kazi na soketi za BSD moja kwa moja. Na pia msimbo huu utakuwa sawa kwa OS zote, kwa sababu utendaji wote unaotegemea jukwaa unapatikana ndani ya madarasa ya Soketi na Anwani.

Hitimisho
Sasa tuna zana inayojitegemea ya jukwaa ya kutuma na kupokea pakiti za UDP.

UDP haiungi mkono miunganisho, na nilitaka kutoa mfano ambao ungeonyesha hii wazi. Kwa hiyo niliandika programu ndogo ambayo inasoma orodha ya anwani za IP kutoka kwa faili ya maandishi na kutuma pakiti kwao, moja kwa pili. Kila wakati programu inapokea pakiti, inaonyesha anwani na bandari ya kompyuta inayotuma na ukubwa wa pakiti iliyopokea kwenye console.

Unaweza kusanidi programu kwa urahisi ili hata kwenye mashine ya ndani unaweza kupata nodi kadhaa za kubadilishana pakiti kwa kila mmoja. Ili kufanya hivyo, toa bandari tofauti kwa hali tofauti za programu, kwa mfano:

> Node 30000
>Node 30001
>Node 30002
Na kadhalika…

Kila nodi itasambaza pakiti kwa nodi nyingine zote, na kutengeneza kitu kama mfumo mdogo wa rika-kwa-rika.

Nilitengeneza programu hii kwenye MacOSX, lakini inapaswa kujumuisha kwenye OS yoyote kama unix na kwenye Windows, lakini ikiwa unahitaji kufanya marekebisho yoyote kwa hili, tafadhali nijulishe.

Lebo: Ongeza vitambulisho

VSEVOLOD STAKHOV

Upangaji wa Soketi

Idadi kubwa ya programu za seva za mtandao hupangwa kwa kutumia soketi. Kimsingi, soketi ni sawa na maelezo ya faili na tofauti moja muhimu sana - soketi hutumiwa kwa mawasiliano kati ya programu ama kwenye mtandao au kwenye mashine ya ndani. Kwa hivyo, programu haina shida na uwasilishaji wa data; soketi humfanyia. Unahitaji tu kuhakikisha kuwa vigezo vya tundu vya programu mbili vinafanana.

Kwa hivyo, soketi za mtandao ni miundo iliyounganishwa ambayo imesawazishwa madhubuti na kila mmoja. Ili kuunda soketi, mfumo wowote wa uendeshaji unaowaunga mkono hutumia kazi ya tundu (kwa bahati nzuri, soketi ni za kutosha ambazo zinaweza kutumika kuhamisha data kati ya programu zinazoendesha kwenye majukwaa tofauti). Umbizo la kazi ni:

tundu la int (kikoa cha int, aina ya int, itifaki ya int);

Kigezo cha kikoa kinataja aina ya itifaki ya usafiri, i.e. itifaki ya utoaji wa pakiti za mtandao. Itifaki zifuatazo zinatumika kwa sasa (lakini kumbuka kuwa aina ya muundo wa anwani itakuwa tofauti kwa aina tofauti za itifaki):

  • PF_UNIX au PF_LOCAL- mawasiliano ya ndani kwa UNIX OS (na sawa).
  • PF_INET - IPv4, IP-Itifaki ya mtandao, inayojulikana zaidi sasa (anwani 32-bit).
  • PF_INET6- IPv6, kizazi kijacho cha itifaki ya IP (IPng) - anwani ya 128-bit.
  • PF_IPX - IPX- Itifaki za Novell.

Itifaki zingine zinaungwa mkono, lakini hizi 4 ndizo maarufu zaidi.

Aina ya parameter ina maana ya aina ya tundu, i.e. jinsi data itahamishwa: SOCK_STREAM mara kwa mara hutumiwa; matumizi yake inamaanisha uhamishaji wa data salama katika mtiririko wa pande mbili wenye udhibiti wa makosa. Kwa njia hii ya uhamishaji data, mpangaji programu hana haja ya kuwa na wasiwasi juu ya kushughulikia makosa ya mtandao, ingawa hii hailindi dhidi ya makosa ya kimantiki, ambayo ni muhimu kwa seva ya mtandao.

Kigezo cha itifaki kinabainisha aina mahususi ya itifaki ya kikoa fulani, kwa mfano IPPROTO_TCP au IPPROTO_UDP (kigezo cha aina kinapaswa kuwa SOCK_DGRAM katika kesi hii).

Kazi ya tundu inaunda tu mwisho na inarudi kushughulikia tundu; Mpaka tundu limeunganishwa na anwani ya mbali kwa kutumia kazi ya kuunganisha, data haiwezi kutumwa kwa njia hiyo! Ikiwa pakiti zimepotea kwenye mtandao, k.m. Wakati kushindwa kwa mawasiliano kunatokea, ishara ya Bomba Iliyovunjika - SIGPIPE inatumwa kwa programu iliyounda tundu, kwa hiyo inashauriwa kumpa kidhibiti kwa ishara hii kwa kutumia kazi ya ishara. Baada ya tundu kuunganishwa na mwingine na kazi ya kuunganisha, data inaweza kutumwa juu yake ama kwa kutumia kazi za kawaida za kusoma - kuandika au recv maalum - kutuma kazi. Baada ya kumaliza kazi, tundu lazima limefungwa kwa kutumia kazi ya karibu. Ili kuunda programu ya mteja, unganisha tu tundu la ndani kwenye tundu la mbali (seva) kwa kutumia kazi ya kuunganisha. Muundo wa kipengele hiki ni:

int connect(int sock_fd, const struct *sockaddr serv_addr, socketlen_t addr_len);

Ikiwa kuna hitilafu, kazi inarudi -1; hali ya hitilafu inaweza kupatikana kwa kutumia mfumo wa uendeshaji. Ikiwa imefanikiwa, 0 inarejeshwa. Tundu, mara moja imeunganishwa, mara nyingi haiwezi kuunganishwa tena, kama, kwa mfano, hutokea katika itifaki ya IP. Kigezo cha sock_fd kinabainisha kielezi cha tundu, muundo wa serv_addr unapeana anwani ya mwisho ya mbali, addr_len ina urefu wa serv_addr (aina ya socketlen_t ina asili ya kihistoria, kwa kawaida ni sawa na aina ya int). Kigezo muhimu zaidi katika kazi hii ni anwani ya tundu la mbali. Kwa kawaida, sio sawa kwa itifaki tofauti, kwa hiyo nitaelezea hapa muundo wa anwani tu kwa itifaki ya IP (v4). Kwa hili, muundo maalum wa sockaddr_in hutumiwa (lazima utupwe moja kwa moja kwa aina ya sockaddr wakati wa kupiga simu unganisha). Sehemu za muundo huu zinaonekana kama hii:

tengeneza sockaddr_in(

Familia_ya_siyo_familia; - inafafanua familia ya anwani, lazima iwe AF_INET kila wakati

U_int16_t sin_port; - bandari ya tundu kwa mpangilio wa mtandao

Muundo katika_addr sin_addr; - muundo ulio na anwani ya IP

Muundo unaoelezea anwani ya IP:

muundo katika_addr(

U_int32_t s_addr; - weka anwani ya IP kwa mpangilio wa mtandao

Kumbuka mpangilio maalum wa baiti katika sehemu zote kamili. Ili kubadilisha nambari ya bandari kuwa mpangilio wa baiti ya mtandao, unaweza kutumia htons (bandari fupi isiyo na saini) macro. Ni muhimu sana kutumia aina hii ya nambari kamili - nambari fupi isiyo na saini.

Anwani za IPv4 zimegawanywa katika moja, matangazo (matangazo) na kikundi (multicast). Kila anwani moja inaelekeza kwenye kiolesura kimoja cha seva pangishi, anwani za utangazaji huelekeza kwa wapangishaji wote kwenye mtandao, na anwani za utangazaji anuwai huelekeza kwa wapangishi wote katika kikundi cha utangazaji anuwai. Muundo wa in_addr unaweza kupewa anwani yoyote kati ya hizi. Lakini kwa wateja wa tundu, katika idadi kubwa ya kesi anwani moja hupewa. Isipokuwa ni kesi wakati unahitaji kuchanganua mtandao mzima wa ndani katika kutafuta seva, basi unaweza kutumia anwani ya utangazaji. Kisha, uwezekano mkubwa, seva lazima iripoti anwani yake halisi ya IP na tundu la uhamisho zaidi wa data lazima liunganishwe nayo. Kusambaza data kupitia anwani za utangazaji si wazo zuri, kwa kuwa haijulikani ni seva gani inayoshughulikia ombi. Kwa hiyo, soketi zinazoelekezwa kwa sasa zinaweza kutumia anwani moja tu. Kwa seva za tundu za kusikiliza anwani, hali ni tofauti: hapa inaruhusiwa kutumia anwani za utangazaji ili kujibu mara moja ombi la mteja kwa eneo la seva. Lakini mambo ya kwanza kwanza. Kama ulivyoona, katika muundo wa sockaddr_in sehemu ya anwani ya IP inawakilishwa kama nambari kamili isiyo na saini, na tunatumiwa anwani katika umbizo la x.x.x.x (172.16.163.89) au katika umbizo la herufi (myhost.com). Ili kubadilisha ya kwanza, tumia kitendakazi cha inet_addr (const char *ip_addr), na kwa pili, tumia kitendakazi cha gethostbyname (const char *host). Wacha tuangalie zote mbili:

u_int32_t inet_addr(const char *ip_addr)

– hurejesha nambari kamili ya moja kwa moja inayofaa kutumika katika muundo wa sockaddr_in kwenye anwani ya IP iliyopitishwa kwake katika umbizo la x.x.x.x. Hitilafu ikitokea, INADDR_NONE itarejeshwa.

tengeneza HOSTENT* gethostbyname(const char *host_name)

- inarudisha muundo wa habari kuhusu mwenyeji kulingana na jina lake. Isipofaulu, hurejesha NULL. Jina hutafutwa kwanza kwenye faili ya majeshi na kisha katika DNS. Muundo wa HOSTENT hutoa maelezo kuhusu mwenyeji anayehitajika. Kati ya sehemu zake zote, muhimu zaidi ni sehemu ya char **h_addr_list, ambayo inawakilisha orodha ya anwani za IP kwa seva pangishi fulani. Kwa kawaida h_addr_list hutumiwa, ikiwakilisha anwani ya kwanza ya IP ya seva pangishi, lakini pia unaweza kutumia usemi wa h_addr kwa hili. Baada ya kutekeleza kitendakazi cha gethostbyname, orodha h_addr_list ya muundo wa HOSTENT ina anwani rahisi za ishara za IP, kwa hivyo lazima utumie kitendakazi cha inet_addr kubadilisha hadi umbizo la sockaddr_in.

Kwa hiyo, tumeunganisha tundu la mteja kwenye tundu la seva kwa kutumia kazi ya kuunganisha. Kisha unaweza kutumia vitendaji vya kuhamisha data. Ili kufanya hivyo, unaweza kutumia vitendaji vya kiwango cha chini cha I/O kwa faili, kwani tundu kimsingi ni kielezi cha faili. Kwa bahati mbaya, kazi za kazi za kiwango cha chini na faili zinaweza kutofautiana kwa mifumo tofauti ya uendeshaji, kwa hiyo unahitaji kuangalia mwongozo wa mfumo wako wa uendeshaji. Tafadhali kumbuka kuwa mawasiliano ya mtandao yanaweza kuisha kwa ishara ya SIGPIPE na vitendakazi vya kusoma/kuandika vitaleta hitilafu. Unapaswa kukumbuka daima kuangalia makosa, kwa kuongeza, usipaswi kusahau kwamba uhamisho wa data kwenye mtandao unaweza kuwa polepole sana, na kazi za pembejeo / pato ni sawa, na hii inaweza kusababisha ucheleweshaji mkubwa katika programu.

Ili kuhamisha data kati ya soketi, kuna kazi maalum ambazo ni za kawaida kwa mifumo yote ya uendeshaji - hizi ni kazi za recv na kutuma familia. Muundo wao ni sawa sana:

int send(int sockfd, batili *data, size_t len, int bendera);

- hutuma bafa ya data.

int recv(int sockfd, batili *data, size_t len, bendera za int);

- inakubali bafa ya data.

Hoja ya kwanza ni maelezo ya tundu, ya pili ni kielekezi kwa data inayopaswa kuhamishwa, ya tatu ni urefu wa bafa, na ya nne ni bendera. Ikifaulu, nambari ya baiti zilizohamishwa hurejeshwa; ikiwa haikufaulu, msimbo wa hitilafu hasi hurejeshwa. Bendera hukuruhusu kubadilisha vigezo vya uhamishaji (kwa mfano, wezesha hali ya operesheni ya asynchronous), lakini kwa kazi nyingi inatosha kuacha sifuri kwenye uwanja wa bendera kwa hali ya kawaida ya uhamishaji. Wakati wa kutuma au kupokea data, vitendakazi huzuia utekelezaji wa programu kabla ya bafa nzima kutumwa. Na wakati wa kutumia itifaki ya tcp/ip, jibu lazima litoke kwenye tundu la mbali linaloonyesha kutuma au kupokea data kwa mafanikio, vinginevyo pakiti inatumwa tena. Wakati wa kutuma data, fikiria MTU ya mtandao (kiwango cha juu cha ukubwa wa fremu kinachopitishwa kwa wakati mmoja). Inaweza kuwa tofauti kwa mitandao tofauti, kwa mfano, kwa Mitandao ya Ethernet ni sawa na 1500.

Kwa hivyo, kwa ajili ya ukamilifu, nitatoa mfano rahisi zaidi wa programu ya C inayotekelezea mteja wa tundu:

#pamoja na /* Maktaba za soketi za kawaida za Linux */

#pamoja na /* Kwa Windows OS tumia #include */

#pamoja na

int kuu())(

Int sockfd = -1;

/* Maelezo ya tundu */

buf ya Char;

Char s = "Mteja tayari";

HOSTENT *h = NULL;

Sockaddr_in addr;

Bandari fupi isiyo na saini = 80;

Addr.sin_family = AF_INET;

/* Tengeneza tundu */

Ikiwa(sockfd == -1)

/* Ikiwa soketi imeundwa */

Kurudi -1;

H = gethostbyname("www.myhost.com");

/* Pata anwani ya mwenyeji */

Ikiwa(h == NULL)

/* Je, kuna anwani kama hiyo? */

Kurudi -1;

Addr.sin_addr.s_addr = inet_addr(h->h_addr_list);

/* Badilisha anwani ya IP kuwa nambari */

If(connect(sockfd, (sockaddr*) &addr, sizeof(addr)))

/* Kujaribu kuunganisha kwenye tundu la mbali */

Kurudi -1;

/* Muunganisho ulifanikiwa - endelea */

Ikiwa(tuma(sockfd, s, sizeof), 0)< 0)

Kurudi -1;

Ikiwa(recv(sockfd, buf, sizeof(buf), 0)< 0)

Kurudi -1;

Funga(sockfd);

/* Funga tundu */

/* Kwa Windows, kitendakazi cha closesocket kinatumika */

Rudia 0;

Unaona, kutumia soketi sio ngumu sana. Maombi ya seva hutumia kanuni tofauti kabisa za kufanya kazi na soketi. Kwanza, tundu linaundwa, kisha anwani ya ndani inapewa kwa kutumia kazi ya kumfunga, lakini unaweza kugawa anwani ya utangazaji kwenye tundu. Kisha kazi ya kusikiliza huanza kusikiliza anwani, maombi ya uunganisho yanawekwa kwenye foleni. Hiyo ni, kazi ya kusikiliza inaanzisha tundu la kupokea ujumbe. Baada ya hayo, unahitaji kutumia kazi ya kukubali, ambayo inarudi tundu mpya tayari kuhusishwa na mteja. Ni kawaida kwa seva kukubali miunganisho mingi kwa muda mfupi. Kwa hiyo, unahitaji kuangalia mara kwa mara foleni ya miunganisho inayoingia kwa kutumia kazi ya kukubali. Ili kupanga tabia hii, mara nyingi huamua uwezo wa mfumo wa uendeshaji. Kwa Windows OS, toleo la nyuzi nyingi za operesheni ya seva hutumiwa mara nyingi zaidi; baada ya kukubali unganisho, nyuzi mpya huundwa kwenye programu, ambayo huchakata tundu. Katika mifumo ya *nix, uundaji wa mchakato wa mtoto kwa kitendakazi cha uma hutumiwa mara nyingi zaidi. Wakati huo huo, gharama za juu zinapunguzwa kwa sababu ya ukweli kwamba kuna nakala ya mchakato katika mfumo wa faili wa proc. Katika kesi hii, vigezo vyote vya mchakato wa mtoto ni sawa na mzazi. Na mchakato wa mtoto unaweza kushughulikia mara moja uunganisho unaoingia. Mchakato wa mzazi unaendelea kusikiliza. Tafadhali kumbuka kuwa bandari zilizo na nambari 1 hadi 1024 zina bahati na kuzisikiliza sio rahisi kila wakati. Hoja moja zaidi: huwezi kuwa na soketi mbili tofauti zinazosikiliza bandari moja kwenye anwani moja! Kwanza, hebu tuangalie fomati za kazi zilizo hapo juu za kuunda tundu la seva:

int bind(int sockfd, const struct *sockaddr, socklen_t addr_len);

- inapeana anwani ya eneo kwenye tundu ili kuiruhusu kukubali miunganisho inayoingia. Kwa anwani, unaweza kutumia INADDR_ANY mara kwa mara, ambayo inakuwezesha kukubali miunganisho inayoingia kutoka kwa anwani zote katika subnet fulani. Umbizo la chaguo la kukokotoa ni sawa na kuunganisha. Ikitokea hitilafu, hurejesha thamani hasi.

int kusikiliza (int sockfd, int backlog);

- kazi inaunda foleni ya soketi zinazoingia (idadi ya viunganisho imedhamiriwa na parameter ya backlog, haipaswi kuzidi namba SOMAXCONN, ambayo inategemea OS). Baada ya kuunda foleni, unaweza kusubiri muunganisho kwa kutumia kitendakazi cha kukubali. Soketi kawaida huzuia, kwa hivyo utekelezaji wa programu umesimamishwa hadi uunganisho utakubaliwa. Katika kesi ya makosa, -1 inarudishwa.

kukubali kwa ndani (int sockfd, muundo *sockaddr, socklen_t addr_len)

- kazi inasubiri uunganisho unaoingia (au huiondoa kwenye foleni ya uunganisho) na inarudi tundu mpya tayari inayohusishwa na mteja wa mbali. Katika kesi hii, soketi ya asili ya soketi bado haijabadilika. Muundo wa sockaddr umejaa maadili kutoka kwa soketi ya mbali. Katika kesi ya makosa, -1 inarudishwa.

Kwa hivyo hapa kuna mfano wa seva rahisi ya soketi ambayo hutumia kazi ya uma kuunda mchakato wa mtoto ambao unashughulikia muunganisho:

int kuu())(

Pid_t pid;

/* Kitambulisho cha mchakato wa mtoto */

Int sockfd = -1;

/* Shikilia kwa tundu ili kusikiliza kwenye */

Int s = -1;

/* Pokea maelezo ya tundu */

buf ya Char;

/* Elekeza kwenye bafa ili kupokea */

Char str = "Seva tayari";

/* Kamba ya kutuma kwa seva */

HOSTENT *h = NULL;

/* Muundo wa kupata anwani ya IP */

Sockaddr_in addr;

/* Muundo wa itifaki ya Tcp/ip */

Sockaddr_in raddr;

Bandari fupi isiyo na saini = 80;

/* Jaza sehemu za muundo: */

Addr.sin_family = AF_INET;

Addr.sin_port = htons(bandari);

sockfd = soketi(PF_INET, SOCK_STREAM, IPPROTO_TCP);

/* Tengeneza tundu */

Ikiwa(sockfd == -1)

/* Ikiwa soketi imeundwa */

Kurudi -1;

Addr.sin_addr.s_addr = INADDR_ANY;

/* Sikiliza katika anwani zote */

Ikiwa(bind(sockfd, (sockaddr*) &addr, sizeof(addr)))

/* Agiza anwani ya karibu kwenye tundu */

Kurudi -1;

Kama(sikiliza(sockfd, 1))

/* Anza kusikiliza */

Kurudi -1;

S = kubali(sockfd, (sockaddr *) &raddr, sizeof(raddr));

/* Kubali muunganisho */

Pid = uma ();

/* kuzaa mchakato wa mtoto */

Ikiwa(pid == 0)(

/* Huu ni mchakato wa mtoto */

Ikiwa(recv, buf, sizeof(buf), 0)< 0)

/* Tuma kamba s kwenye tundu la mbali */

Kurudi -1;

Ikiwa(tuma, str, saizi ya(str), 0)< 0)

/* Pokea jibu kutoka kwa seva ya mbali */

Kurudi -1;

Printf("Kamba iliyopokelewa ilikuwa: %s", buf);

/* Chapisha bafa kwa pato la kawaida */

Funga;

/* Funga tundu */

Rudia 0;

/* Toka katika mchakato wa mtoto */

Funga(sockfd);

/* Funga soketi ya kusikiliza */

Rudia 0;

Wakati wa kuunda thread ili kuchakata tundu, angalia mwongozo wa OS, kwani wito wa kazi ya kuunda thread inaweza kutofautiana kwa kiasi kikubwa kwenye mifumo tofauti. Lakini kanuni za usindikaji kwa thread zinabaki sawa. Kitendakazi cha kuchakata kinahitaji tu kupitisha kielekezi cha tundu kama hoja (kawaida aina yoyote ya data katika umbizo batili * inaweza kupitishwa kwa kipengele cha kukokotoa cha mtiririko, ambacho kinahitaji matumizi ya aina ya kutupwa).

Kumbuka muhimu kwa mifumo ya Windows. Niligundua kuwa mfumo wa tundu haufanyi kazi bila kutumia kazi ya WSAStartup kuanzisha maktaba ya tundu. Programu ya soketi ya Windows inapaswa kuanza kama hii:

WSADATA wsaData;

WSAStartup(0x0101, &wsaData);

Na unapotoka kwenye programu, andika yafuatayo:

WSACleanup ();

Kwa kuwa shughuli nyingi za tundu zinazuia, utekelezaji wa kazi lazima uingizwe mara kwa mara kwa kusubiri maingiliano. Kwa hiyo, mifumo ya *nix-kama mara nyingi huepuka kuzuia console kwa kuunda aina maalum ya programu - daemon. Daemoni si mali ya kiweko pepe na hutokea wakati mchakato wa mtoto unapoita uma, na mchakato wa mzazi huisha kabla ya mtoto wa 2 (na hii hutokea kila mara kwa njia hii). Baada ya hayo, mchakato wa mtoto wa 2 unakuwa mchakato kuu na hauzuii console. Hapa kuna mfano wa tabia ya programu hii:

pid = uma ();

/* Unda mchakato wa mtoto wa kwanza */

ikiwa (pid<0){

/* Hitilafu katika kupiga uma */

Printf("Kosa la Kuiga:) ");

Toka(-1);

)mwingine ikiwa (pid !=0)(

/* Huyu ndiye mzazi wa kwanza! */

Printf("Huyu ni Baba 1");

)mwingine(

Pid = uma ();

/* Kazi ya mzazi wa kwanza inaisha */

/* Na tunaita mchakato mwingine wa mtoto */

Ikiwa (pid<0){

Printf("Kosa la kughushi:) ");

Toka(-1);

)mwingine ikiwa (pid !=0)(

/* Huyu ni mzazi wa pili */

Printf("Huyu ni baba 2");

)mwingine(

/* Na huu ni mchakato uleule wa mtoto wa 2 */

/* Badilisha hadi hali ya "kiwango" ya daemon */

Setid();

/* Endesha daemon katika hali ya mtumiaji mkuu */

Umask(0); /* Mask ya kawaida ya faili */

Chdir("/"); /* Nenda kwenye saraka ya mizizi */

Daemoncode(); /* Kwa kweli msimbo wa daemon yenyewe */

/* Unapopiga daemon, daemon ya mtoto inaonekana */

Ni hayo tu. Nadhani hii inatosha kuunda seva rahisi ya tundu.