Beej Vadovas Tinklo Programavimo Naudojant Interneto Lizdai

Vadovas išversta su http://beej.us/guide/bgnet/

1. Įvadas

Ei! Lizdas programavimo got you down? Yra ši medžiaga, tik šiek tiek per sunku suprasti, iš vyro puslapius? Norite padaryti nemokama Interneto programavimo, bet neturite laiko bristi per gob, structs bando išsiaiškinti, ar jūs turite skambinti bind(), prieš jums connect() ir pan., pan.

Na, atspėti, ką! Aš jau padaryta tai bjaurus verslo, ir aš miršta pasidalinti informacija su visais! Jūs atėjote į tinkamą vietą. Šiame dokumente turėtų būti vidutinė kompetentingos C programuotojas krašto, jis turi gauti rankena dėl šio tinklo triukšmo.

Ir check it out: aš pagaliau pasivijo ateityje (tik nick laiko, per daug!) ir buvo atnaujintas Vadovas IPv6! Mėgautis!

1.1. Auditorija
Šis dokumentas buvo parašyta kaip vadovėlis, ne visą nuorodą. Tai turbūt geriausias kada perskaityti asmenys, kurie tik pradeda su lizdu programavimas ir ieškote įsitvirtinti. Tai tikrai nėra pilnas ir visiškas vadovas lizdai programavimo, bet kokiu būdu.

Tikimės, kad, nors jis bus tik pakankamai tiems, vyras puslapių pradėti priėmimo prasme… 🙂

1.2. Platforma ir Sudarytojas
Kodas esantys šiame dokumente buvo įrašyta į Linux PC naudojant Gnu gcc kompiliatorių. Tačiau jis turėtų remtis tik apie bet kokią platformą, kuri naudoja gcc. Žinoma, tai nėra taikoma, jei esate programavimo Windows—žr. skyrių apie Windows programavimo, žemiau.

1.3. Oficialus Puslapis ir Knygos a Pardavimui
Šis oficialus vieta šiame dokumente yra http://beej.us/guide/bgnet/. Ten taip pat rasite kodo pavyzdys ir vertimus vadovas į įvairias kalbas.

Pirkti gražiai jungiasi spausdinti kopijas (kai kurie vadina juos “knygos”), apsilankymas http://beej.us/guide/url/bgbuy. Aš vertinu pirkti, nes jis padeda išlaikyti mano dokumentas-raštu gyvenimo būdas!

1.4. Pastaba Solaris/SunOS Programuotojai
Kai kompiliavimas Solaris arba SunOS, jums reikia nurodyti kai kurių papildomų komandų eilutės perjungtuvai, skirti sujungti tinkamai bibliotekos. Siekiant tai padaryti, tiesiog pridėkite “-lnsl -lsocket -lresolv” pabaigoje surinkti komandą, kaip taip:

$ cc -o server server.c -lnsl -lsocket -lresolv

Jei jūs vis dar gaunate klaidos, galite pabandyti dar pridėti “-lxnet” pabaigoje, kad komandą. Aš nežinau, kas tai daro, tiksliai, tačiau kai kurie žmonės, atrodo, reikia.

Kita vieta, kad jūs galite rasti problemų yra kvietimas setsockopt(). Prototipas skiriasi nuo mano Linux box, todėl vietoj:

int yes=1;

įrašykite tai:

char yes='1';

Kaip aš neturiu Saulė lauke, aš ne testuoti, bet pirmiau nurodyta informacija—tai, ką žmonės man pasakė, el. paštu.

1.5. Pastaba dėl Windows Programuotojai
Šiuo vadovas, istoriškai susiklostė, kad aš padariau šiek tiek maišo Windows, tiesiog dėl to, kad man nepatinka, tai labai daug. Bet aš tikrai turėtų būti sąžiningas ir pasakyti, kad Windows turi didžiulį įdiegti bazę ir yra suprantama, tai yra puikiai baudą operacinė sistema.

Jie sako, nebuvimas daro širdies augti fonder, ir šiuo atveju, manau, tai tiesa. (Arba gal tai amžiaus.) Bet ką galiu pasakyti, yra tai, kad po dešimtmečio-plius ne naudojant Microsoft OSes mano asmeninio darbo, esu daug laimingesnis! Kaip, pavyzdžiui, aš galiu sėdėti ir drąsiai pasakyti, “Žinoma, nedvejodami naudoti Windows!” …Gerai taip, ji man žvyro, mano dantys pasakyti, kad.

Taigi, aš vis dar siūlome jums išbandyti Linux, BSD, arba kai kurių skonis iš Unix, o ne.

Bet žmonėms patinka tai, kas jiems patinka, ir jūs Windows liaudies bus malonu žinoti, kad ši informacija yra visuotinai taikomas ir jums, vaikinai, keletą nedidelių pakeitimų, jei jų yra.

Vienas kietas dalykas, jūs galite padaryti, tai įdiegti Cygwin, kuris yra kolekciją Unix įrankiai Windows. Aš girdėjau apie gandus, kad tai leidžia visus šių programų kompiliavimo nemodifikuotas.

Bet kai kurie iš jūsų gali norėti daryti tai, ko Grynas Windows Būdas. Tai labai gutsy iš jūsų, ir tai, ką jūs turite padaryti: ištekėti ir gauti Unix iš karto! Ne, ne—juokauju. Aš esu turėjo būti Windows draugiškas šių dienų…

Tai yra tai, ką jūs turite daryti (nebent turite įdiegti Cygwin!): pirma, ignoruoti beveik visi sistemos header files aš paminėti čia. Viskas, ką jums reikia įtraukti, yra:

#include <winsock.h>

Palaukite! Jūs taip pat turite paskambinti WSAStartup() prieš tai dar ką nors su lizdai biblioteka. Kodas daryti, kad atrodo kažkas panašaus į tai:

#include <winsock.h>

{
    WSADATA wsaData;   // if this doesn't work
    //WSAData wsaData; // then try this instead

    // MAKEWORD(1,1) for Winsock 1.1, MAKEWORD(2,0) for Winsock 2.0:

    if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) {
        fprintf(stderr, "WSAStartup failed.\n");
        exit(1);
    }

Jūs taip pat turite pasakyti savo kompiliatorius į nuorodą Winsock biblioteka, paprastai vadinamas wsock32.lib arba winsock32.lib, ar ws2_32.lib už Winsock 2.0. Pagal VC++, tai gali būti padaryta per Projektas meniu, pagal Parametrus…. Spustelėkite Saitą, tab, ir ieškokite langelio pavadinimu “Objektas/library moduliai”. Pridėti “wsock32.lib” (arba priklausomai nuo to, lib yra jūsų pageidavimą) į tą sąrašą.

Ar taip, girdžiu.

Galiausiai, jūs turite skambinti WSACleanup() kai jūs visi per lizdams biblioteka. Pamatyti jūsų interneto žinyne.

Kai tik tai atliksite, poilsio pavyzdžių tai pamoka apskritai turėtų būti taikomas, su keliomis išimtimis. Vienas dalykas, jūs negalite naudoti uždaryti() uždaryti lizdas—jums reikia naudoti closesocket(), o ne. Taip pat, select() veikia tik su lizdu aprašai, ne failą aprašus (pvz., 0 stdin).

Taip pat yra lizdas klasės, kad jūs galite naudoti, CSocket. Patikrinkite savo kompiliatoriai pagalbos puslapius, daugiau informacijos.

Norite gauti daugiau informacijos apie Winsock, skaityti Winsock DUK ir eiti iš ten.

Galiausiai, aš girdėjau, kad Windows yra ne fork() sistema, skambinkite, kuris yra, deja, naudojami kai mano pavyzdžiai. Gal turite nuorodą POSIX biblioteka ar kažką, kad gauti jį dirbti, arba galite naudoti CreateProcess() vietoj. fork() nesiima jokių argumentų, ir CreateProcess() trunka apie 48 mlrd. argumentus. Jei jūs būsite ne iki to, CreateThread() yra šiek tiek lengviau virškinamas…deja diskusija apie multiįvėrime peržengia šį dokumentą. Galiu tik kalbėti apie tiek daug, jūs žinote!

1.6. El. Paštas Politikos
Aš paprastai galima padėti su elektroninio pašto klausimus taip drąsiai rašyti, bet aš negaliu garantuoti atsakymo. Aš švino gana užimtas gyvenimo ir yra kartų, kai aš tiesiog negaliu atsakyti į klausimą, ką turite. Jei taip yra šiuo atveju, aš paprastai tiesiog ištrinti pranešimą. Tai nieko asmeninių; aš tiesiog nebus kada turi laiko pateikti išsamų atsakymą jums reikės.

Kaip taisyklė, daugiau sudėtingas klausimas, mažiau tikėtina, esu reaguoti. Jei galite susiaurinti klausimą prieš siuntimas jį ir įsitikinkite, kad įtraukti bet kokią su tuo susijusią informaciją (pvz., platforma, kompiliatorius, klaidų pranešimus jūs gaunate, ir viską, kas, jūsų manymu, galėtų padėti man išspręsti), jūs daug labiau tikėtina, kad gauti atsakymą. Daugiau patarimų, skaityti ESR dokumentų, Kaip Užduoti Klausimus Protingas Būdas.

Jei jums nereikia gauti atsakymą, hack apie jį šiek tiek daugiau, pabandykite rasti atsakymą, o jei jis dar silpnas, tada parašykite man vėl su informacija, kurią radote, ir, tikiuosi, jis bus pakankamai man padėti.

Dabar, kad aš badgered jums apie tai, kaip rašyti, o ne rašyti man, tiesiog norėčiau, kad jums žinoti, kad visiškai suprantu, visi girti vadovas gavo per metus. Tai tikra nuotaika padidinti, ir ji gladdens man girdėti, kad ji yra naudojama geras! 🙂 Ačiū!

1.7. Dubliavimas
Jūs esate daugiau nei kviečiame į veidrodį šioje svetainėje, viešai ar privačiai. Jei jūs viešai veidrodis svetainę ir nori man nuorodą į jį iš pagrindinio puslapio, lašas man linija beej@beej.us.

1.8. Pastaba Vertėjai
Jeigu norite išversti vadovas į kitą kalbą, parašykite man ne beej@beej.us ir aš nuorodą į savo vertimo iš pagrindinio puslapio. Nesivaržykite pridėti savo vardą ir kontaktinę informaciją vertimo.

Prašome atkreipti dėmesį, kad licencijos apribojimai Autorių teisių ir Paskirstymo skyriuje, toliau.

Jei norite man priimančiosios vertimo, tiesiog paprašykite. Aš taip pat nuorodą, jei norite priimančiosios; bet kuriuo atveju yra gerai.

1.9. Autorių teisių ir Paskirstymo
Beej Vadovas Tinklo Programavimo Autorinės teisės © 2015 Brian “Beej Jorgensen” Salėje.

Su konkrečių išimčių, šaltinio kodą ir vertimai, žemiau, šiame darbe yra licencijuotas pagal Creative Commons Attribution – Nekomercinis – Jokių Išvestinių Darbų, 3.0 Licencija. Norėdami peržiūrėti kopiją šią licenciją, apsilankymas http://creativecommons.org/licenses/by-nc-nd/3.0 (arba siųsti laišką į Creative Commons, 171 second Street, Suite 300, San Franciskas, Kalifornija, 94105, JAV.

Vienas konkrečias išimtis, “Jokių Išvestinių Darbų” dalis licenciją yra taip: šis vadovas gali būti laisvai išverstas į bet kokią kalbą, jei vertimas yra tikslus, ir vadovas yra perspausdintas visas. Tą pačią licenciją taikomi apribojimai vertimo, kaip originalius vadovas. Vertimo, taip pat gali būti, pavadinimas ir kontaktinė informacija vertėjas.

C kodo, pateikti šiame dokumente, yra suteikta visuomenės domeno, ir yra visiškai be jokios licencijos apribojimas.

Pedagogai yra laisvai skatinami rekomenduojame ar tiekimo kopijas vadovas studentams.

Kontaktai beej@beej.us daugiau informacijos.

2. Kas yra lizdas?

Išgirsite kalbėti apie “lizdai” visą laiką, ir galbūt jums įdomu tai, ką jie tiksliai. Na, jie tai būdas kalbėti į kitas programas, naudojant standartinę Unix failų deskriptorių.

Ką?

Gerai—turbūt girdėjote, kai Unix hakeris narė, “Pas, viskas Unix failų!” Tai, ką tas asmuo gali turėti buvo kalbama apie tai, kad kai Unix programos ar bet kokios rūšies (I/O), tai nereikia skaityti ar rašyti į failą aprašų. Failo deskriptorius yra tiesiog sveikasis skaičius, susijęs su atvira failą. Tačiau (ir štai laimikis), kad failas gali būti tinklo ryšį, FIFO, vamzdis, terminalo, nekilnojamojo on-the-disk file, arba tiesiog apie ką nors kita. Viskas Unix failų! Taigi, kai norite bendrauti su kita programa, Internetu, you ‘ re gonna do it per failo deskriptorius, geriau patikėti.

“Kur aš galiu gauti šio failo deskriptorius, tinklo ryšių, Ponas Eugenijus-Kelnes?” tikriausiai paskutinis klausimas, dėl savo proto dabar, bet aš ruošiuosi jį atsakyti vistiek: Jūs paskambinti į lizdą() sistema rutinos. Jis grįžta į lizdą aprašų, ir jums bendrauti per jį naudojant specializuotą send() ir recv() (vyras siųsti, vyras recv) socket skambučius.

“Bet, hey!”, jums gali būti exclaiming teisus dabar. “Jei tai failo deskriptorius, kodėl pavadinimas Neptūnas negaliu tiesiog naudokite įprastą read() ir write() ragina jungtis per lizdą?” Trumpas atsakymas yra “Tu gali!” Ilgesnis atsakymas “Tu gali, bet send() ir recv() siūlome daug daugiau galimybių kontroliuoti savo duomenų perdavimui.”

Kas toliau? Kaip apie tai: ten yra visų rūšių lizdus. Yra DARPA Interneto adresai (Interneto Sockets), kelias pavardžių vietos mazgas (Unix Sockets), CCITT X. 25 adresai (X 25 Socketsc, kad galite nekreipti dėmesio), ir turbūt daugelis kitų, priklausomai nuo to, kurioje Unix skonio paleisti. Šiame dokumente aptariamos tik pirmas: Interneto Lizdai.

2.1. Dviejų Tipų Interneto Lizdai
Kas tai? Yra dviejų tipų Interneto lizdai? Taip. Na, ne. Aš gulėti. Yra daugiau, bet aš nenorėjau, kad panika jums. Aš tik kalbėti apie dviejų rūšių čia. Išskyrus šį sakinį, kur aš ruošiuosi pasakyti, kad “Raw Lizdai”, taip pat yra labai galingas ir reikia žiūrėti juos.

Viskas gerai, jau. Kokie yra dviejų rūšių? Vienas yra “Stream Sockets”; kitas – “duomenų paketų kelio schemos Lizdai”, kuri gali toliau būti vadinama “SOCK_STREAM” ir “SOCK_DGRAM”, atitinkamai. Duomenų paketų kelio schemos lizdai yra kartais vadinamas “connectionless lizdai”. (Nors jie gali būti connect(), jei jūs tikrai norite. Matyti connect(), toliau.)

Stream lizdai yra patikimas dvipusis prijungtas komunikacijos srautai. Jei jums produkcija du daiktų į lizdo tvarka “1, 2”, jie atvyks tvarka “1, 2″ į priešingą pabaigoje. Jie taip pat bus klaidų. Aš tikiu, kad tam tikri, iš tiesų, jie bus klaidų, kad aš tik ketina įdėti savo pirštus į mano ausis ir giesmė la la la la jei kas nors bando teigia kitaip.

Tai, ką naudoja stream kištukiniai lizdai? Na, jūs galbūt girdėjote apie telnet programą, taip? Jis naudoja stream ” lizdai. Visi simboliai, tu tipo reikia atvykti pat, kad jūs tipo juos, tiesa? Taip pat, žiniatinklio naršyklės naudoja HTTP protokolą, kuris naudoja stream lizdai gauti puslapių. Iš tiesų, jei telnet interneto svetainę apie 80 prievadas, ir tipo “GET / HTTP/1.0” ir paspauskite RETURN du kartus, ji bus iškelties HTML atgal į tave!

Kaip padaryti stream lizdai tai pasiekti aukštą duomenų perdavimo kokybę? Jie naudoja protokolą, vadinamą “Perdavimo Kontrolės Protokolas”, kitaip žinomas kaip “TCP” (žr. RFC 793 už labai išsamią info TCP.) TCP užtikrina, kad jūsų duomenys atvyksta iš eilės ir klaidų. Jūs galbūt girdėjote, “TCP” prieš kaip geriau pusė “TCP/IP”, kur “IP” reiškia “Interneto Protokolą” (žr. RFC 791.) IP pirmiausia kalbama apie Interneto kanalų ir apskritai nėra atsakingas už duomenų vientisumą.

Kietas. Ką apie duomenų paketų kelio schemos kištukiniai lizdai? Kodėl jie vadinami connectionless? Kas yra sandoris, čia, bet kokiu atveju? Kodėl jie nepatikimi? Na, čia yra keletas faktų: jei siunčiate duomenų paketų kelio schemos, ji gali atvykti. Ji gali atvykti. Jei jis atvyksta, esančius duomenis paketinių bus klaidų.

Duomenų paketų kelio schemos lizdai taip pat naudoja IP maršrutizavimą, bet jie don ‘ t naudoti TCP; jie naudoja “Vartotojų duomenų paketų kelio schemos Protokolo”, arba “UDP” (žr. RFC 768.)

Kodėl jie connectionless? Na, iš esmės, tai, nes jūs neturite palaiko atvirą ryšį, kaip jūs darote su stream lizdai. Jūs tiesiog sukurti paketinių, slap IP antraštės ant jo paskirties informaciją ir siųsti ją. Nėra ryšio reikia. Jie paprastai yra naudojama tada, kai TCP kamino nėra, kai nedaug sumažėjo paketus ir čia neturiu omenyje pabaigoje Visata. Imties programos: tftp (trivial file transfer protocol, šiek tiek brolis FTP), dhcpcd (DHCP klientas), multiplayer žaidimus, transliacijos garso, vaizdo konferencijos ir kt.

“Palauk! tftp ir dhcpcd yra naudojama perduoti dvejetainis programos nuo vieno šeimininko prie kito! Duomenys negali būti prarastas, jei tikitės, kad programa į darbą, kai jis atvyksta! Kokia tamsioji magija yra?”

Na, mano žmogaus draugas, tftp ir panašias programas turi savo protokolas ant UDP. Pavyzdžiui, tftp protokolas sako, kad kiekvieno paketo, kad gauna išsiųstus, gavėjas turi išsiųsti atgal paketinių, kad sako, “I got it!” (“ACK” paketo.) Jei siuntėjas originalo paketinių gauna jokio atsakymo, tarkim, penkių sekundžių, jis bus vėl perduoti paketą, tol, kol jis pagaliau gauna ACK. Šio pripažinimo procedūra, yra labai svarbus įgyvendinant patikimas SOCK_DGRAM programas.

Už nepatikimas programas, pavyzdžiui, žaidimai, audio, ar video, galite tiesiog ignoruoti išmestus paketus, o gal pabandykite gudriai kompensuoti jų. (Quake žaidėjai žinos, apraiška šis poveikis techninis terminas: prakeiktas vvg. Žodis “prakeiktas”, šiuo atveju, kelia bet labai nešvankaus pasisakymas.)

Kodėl jums naudoti nepatikima pagrindinių protokolo? Dvi priežastys: greitis ir greitis. Tai būdu greičiau į ugnį-ir-pamiršta, kaip ji yra sekti, kas atvyko saugiai ir įsitikinkite, kad jis, kad ir visi, kad. Jeigu siunčiate pokalbių pranešimai, TCP yra didelis, jei jūs siunčiate 40 pozicinį atnaujinimai per sekundę žaidėjų pasaulyje, gal tai nesvarbu, tiek daug, jei viena ar dvi gauti sumažėjo, ir UDP yra geras pasirinkimas.

2.2. Žemo lygio Nesąmonė ir Tinklų Teorija
Nes aš tik paminėjau, sluoksniavimasis protokolus, atėjo laikas kalbėti apie tai, kaip tinklai iš tiesų veikia, ir pateikti keletą pavyzdžių, kaip SOCK_DGRAM paketai yra pastatytas. Praktiškai, jūs tikriausiai galite praleisti šį skyrių. Tai gerai, fono, tačiau.

 

[Encapsulated Protocols Diagram]

Duomenų Inkapsuliacija.

Ei, vaikai, tai laikas sužinoti apie Duomenų Inkapsuliacija! Tai yra labai labai svarbu. Tai labai svarbu, kad jūs tiesiog gali sužinoti apie tai, jei jums imtis tinklų žinoma, čia ne Chico Valstybės ;-). Iš esmės, tai sako, kad tai: paketinių yra gimęs, paketinių yra įvyniojami (“aplieti”) antraštė (ir retai paraštę) iki pirmojo protokolo (tarkime, TFTP protokolas), tada visą dalykas (TFTP antraštė komplekte) yra aplieti vėl kitą protokolą (tarkim, UDP), tada vėl kitas (IP), tada vėl baigiamojo protokolo dėl techninės (fizinės) sluoksnį (tarkime, Ethernet).

Kai kitas kompiuteris gauna paketo, apkaustų juosteles Ethernet antraštės, branduolio juostelėmis IP ir UDP antraštes, TFTP programa juostelėmis TFTP antraštės, ir ji pagaliau turi duomenų.

Dabar aš pagaliau gali kalbėti apie liūdnai Sluoksniuotos Tinklo Modelį, (dar žinomas kaip “ISO/OSI”). Šio Tinklo Modelį aprašoma sistema, tinklo funkcionalumą, kuris turi daug privalumų, palyginti su kitais modeliais. Pavyzdžiui, jums gali rašyti lizdai programas, kurios yra lygiai tas pats, be rūpestinga, kaip duomenys yra fiziškai perduodamas (serijos, plonas Ethernet, AUI, kas), nes programos dėl žemesnio lygio spręsti už jį. Tikrasis tinklo aparatūros ir topologijos yra skaidri, į lizdą programuotojas.

Be tolesnio ceremonija, aš pristatysiu sluoksniai pilnos modelį. Atminkite, kad tai, tinklo klasės egzaminus:

  • Prašymas
  • Pristatymas
  • Sesijos metu
  • Transportas
  • Tinklas
  • Duomenų Nuorodą
  • Fizinio

Fizinio Sluoksnio aparatūros (serijos, Ethernet ir kt.). Taikymo Sluoksnis yra tik apie tai, kiek iš fizinio lygio, kaip jūs galite įsivaizduoti—tai vieta, kur naudotojai sąveikauja su tinklu.

Dabar, šis modelis yra tokia bendra jūs turbūt galėtų naudotis juo kaip automobilių remonto vadove, jei jūs tikrai norėjo. Sluoksniuotos modelis labiau atitinka Unix gali būti:

  • Taikymo Sluoksnis (telnet, ftp ir kt.)
  • Host-į-Host Transporto Sluoksnį (TCP, UDP)
  • Interneto Sluoksnis (IP ir pervežimas)
  • Prieigą Prie Sluoksnio (Ethernet, wi-fi, ar pan.)

Šiuo metu, jūs tikriausiai galite pamatyti, kaip šie sluoksniai atitinka hermetizuoti pradiniai duomenys.

Pamatyti, kiek daug darbo yra pastato paprastą paketą? Pas! Ir jūs turite tipas paketo antraštės save naudojant “katė”! Juokauju. Viskas, ką jums reikia padaryti stream lizdų send() duomenis. Viskas, ką jums reikia padaryti, duomenų paketų kelio schemos lizdai yra sutelkta pakelio metodas jūsų pasirinkimas ir sendto(). Branduolio stato Transporto Sluoksnį ir Interneto Sluoksniu, jums ir aparatūros ar Prieigos prie Tinklo Sluoksniu. Ah, šiuolaikinės technologijos.

Taigi baigiasi mūsų trumpą įsiveržimas į tinklo teorija. O taip, aš pamiršau, kad pasakys viską, ką aš norėjau pasakyti apie maršrutą: nieko! That ‘ s right, aš neketinu apie tai kalbėti. Maršrutizatorius juosteles paketo IP antraštės, konsultuoja savo maršrutų lentelės, blah blah blah. Patikrinti IP RFC jei jums tikrai rūpi. Jei jūs niekada sužinoti apie tai, taip pat, gyvensite.

3. IP Adresai, structs ir Duomenų Munging

Čia yra žaidimo dalis, kurioje mes galime kalbėti kodas keisti.

Bet pirma, aptarti daugiau ne kodą! Yay! Pirmiausia noriu kalbėti apie IP adresus ir prievadus tik tad taip, mes turime tai sutvarkyti. Tada mes kalbame apie tai, kaip lizdai API parduotuvių ir manipuliuoja IP adresus ir kitus duomenis.

3.1. IP Adresai, versijos 4 ir 6
Ir senas geras dienas atgal, kai Ben Kenobi, vis dar buvo vadinamas Obi Wan Kenobi, ten buvo nuostabus tinklo maršrutizavimo sistema, vadinama Interneto Protokolo 4 Versija, dar vadinama IPv4. Jis turėjo adresus sudaro keturi baitai (A.K.A. keturi “baitai”), ir buvo plačiai parašyta “taškus ir skaičių” forma, kaip kad: 192.0.2.111.

Jūs tikriausiai matė ją aplink.

Iš tiesų, kaip šio raštu, beveik kiekvieną svetainės Internete naudoja IPv4.

Kiekvienas, įskaitant Obi Wan, buvo laiminga. Viskas buvo puiku, kol kai naysayer pavadinimas Vint Cerf- įspėjo visus, kad mes buvome apie paleisti iš IPv4 adresus!

(Be to, įspėjimas visiems Ateinančių IPv4 Apokalipsės Bausmė Ir Drūmums, Vint Cerf yra taip pat gerai žinomas už tai, Kad Tėvas Internetu. Taip, aš tikrai esu ne pozicija-antros atspėti savo sprendimą.)

Paleisti iš adresus? Kaip tai galėtų būti? Aš turiu galvoje, yra, pavyzdžiui, milijardus IP adresų 32 bitų, IPv4 adresą. Ar tikrai mes turime milijonus kompiuterių, iš ten?

Taip.

Taip pat, pradžioje, kai buvo tik keli kompiuterių ir visi manė, milijardo, buvo neįmanomai daug, kai kurios didelės organizacijos buvo dosniai skyrė milijonus IP adresų savo reikmėms. (Pavyzdžiui, Xerox, MIT, Ford, HP, IBM, GE, AT&T, ir, kai mažai kompanija Apple, pavadinimas kelias.)

Tiesą sakant, jei ji nebuvo kelių last resort priemonių, mes turėtų vykdyti jau seniai.

Bet dabar mes gyvename epochoje, kai mes kalbame apie kiekvieno žmogaus kurių IP adresas, į kiekvieną kompiuterį, kiekvieną skaičiuoklė, kiekvienas telefonas, kiekvienas stovėjimo skaitiklis, ir (kodėl gi ne) kiekvienas šuniukas šuo, taip pat.

Ir taip, IPv6 gimė. Nuo Vint Cerf tikriausiai yra nemirtingas (net jei jo fizinė forma turėtų praeiti apie, neduok dieve, jis, tikriausiai, jau kaip kažkoks hyper-pažangi ELIZA programą gylyje Internet2), ne vienas nori, kad jį išgirsti dar kartą pasakyti: “aš juk tau sakiau” jei mes neturime pakankamai adresai kitą versiją Interneto Protokolo.

Ką tai rodo?

Kad mes turime daug daugiau adresai. Kad mes reikia ne tik dvigubai daugiau adresus, ne milijardą kartų, kiek daug, ne tūkstantis trilijonų kartų, kiek daug, tačiau 79 MLN MILIJARDŲ TRILIJONŲ kartų, kiek įmanoma adresus! Kad parodysime ’em!

Jūs sakote “Beej, ar tai tiesa? Turiu visas priežastis, kad netiki, dideliais kiekiais.” Na, skirtumas tarp 32 bitų ir 128 bitų gali ne atrodo daug, tai tik 96 daugiau bitų, tiesa? Tačiau nepamirškite, kad mes kalbame įgaliojimus čia: 32 bitai sudaro apie 4 mlrd numeriai (232), o 128 bitai sudaro apie 340 trilijonų trilijonų trilijonų numeriai (nekilnojamojo, 2128). Tai tarsi mln. IPv4 Internetas už kiekvieną žvaigždžių Visatoje.

Pamiršti šį taškai-ir-skaičiai atrodo IPv4, taip pat, dabar mes turime šešioliktaine sistema, su kiekvienu dviejų baitų riekė atskirti dvitaškis, kaip šis: 2001:0db8:c9d2:aee5:73e3:934a:a5ae:9551.

Tai dar ne viskas! Daug kartų, jūs turėsite IP adresą, su daug nulių, ir jūs galite juos suspausti tarp dviejų colons. Ir jūs galite palikti off nuliais už kiekvienas baitas poros. Pavyzdžiui, kiekvienas iš šių porų adresai yra lygiaverčiai:

2001:0db8:c9d2:0012:0000:0000:0000:0051
2001:db8:c9d2:12::51

2001:0db8:ab00:0000:0000:0000:0000:0000
2001:db8:ab00::

0000:0000:0000:0000:0000:0000:0000:0001
::1

Adresas ::1 grįžtamojo adresu. Jis visada reiškia “ši mašina aš bėgu dabar”. IPv4″, grįžtamojo adresas 127.0.0.1.

Galiausiai, yra IPv4-suderinamumo režimu su IPv6 adresus, kad jums susidurti. Jei norite, kad, pavyzdžiui, atstovauti IPv4 adresas 192.0.2.33 kaip IPv6 adresą, jums naudoti šį žymėjimą: “::ffff:192.0.2.33”.

Mes kalbame rimtai įdomus.

Tiesą sakant, tai tokia sunki, smagu, kad Kūrėjai IPv6 turi gana cavalierly lopped off milijonai ir milijonai adresų rezervuota naudoti, tačiau mes turime tiek daug, tiesą sakant, kas net skaičiuoti nebėra? Yra daug likę už kiekvienas vyras, moteris, vaikas, mažylis ir automobilių stovėjimo skaitiklis apie kiekvieną planetą galaktikoje. Ir patikėkite manimi, kiekvieną planetą galaktikoje yra automobilių stovėjimo aikštelė metrų. Jūs žinote, tai tiesa.

3.1.1. Potinkliai
Dėl organizacinių priežasčių, tai kartais patogiau pareikšti, kad “tai pirmoji dalis iš šio IP adreso per tai tiek, yra tinklo dalis, IP adresą, o likusią kompiuterio dalį.

Pavyzdžiui, su IPv4, jums gali tekti 192.0.2.12, ir mes galime pasakyti, kad pirmieji trys baitai tinklo ir paskutinis baitas buvo priimančiosios. Arba, kitaip tariant, mes kalbame apie priimančiosios 12 tinklas 192.0.2.0 (pamatyti, kaip mes nulis iš baitų, kad buvo šeimininką.)

Ir dabar daugiau pasenusi informacija! Pasiruošę? Senovėje, buvo “klases” potinkliai, kai pirmasis vienos, dviejų arba trijų baitų adresas buvo tinklo dalis. Jei jums pasisekė, kad turime vienas baitas tinklo ir trijų kompiuterio, jūs galite turėti 24 bitai-verta šeimininkų tinklo (16 milijonų, arba tiek). Tai buvo “A Klasės” tinkle. Į priešingą pabaigoje buvo “C Klasė”, su trijų baitų, tinklo, ir vienas baitas priimančiosios (256 šeimininkai, minus pora, kad buvo rezervuota.)

Taigi, kaip matote, ten buvo tik keletas Klasė, didžiulė krūva Klasės Cs, ir kai kurios Klasės Bs viduryje.

Tinklo dalį IP adresas yra aprašyta kažką vadinami tinklo kaukę, kuri jums bitwise-IR su IP adresą norėdami gauti tinklo numeris iš jo. Į netmask paprastai atrodo kažkas panašaus 255.255.255.0. (E. g. su netmask, jei jūsų IP yra 192.0.2.12, jūsų tinklas yra 192.0.2.12 IR 255.255.255.0, kuri suteikia 192.0.2.0.)

Deja, paaiškėjo, kad tai buvo ne smulkiagrūdės pakankamai galiausiai poreikius Internete, mes buvo bėga C Klasės tinklai, gana greitai, ir mes tikrai iš Klasės, todėl nereikia net vargintis paklausti. Siekiant tai ištaisyti, Įgaliojimus, Kad Būtų leidžiama netmask būti savavališkai bitų, o ne tik 8, 16 arba 24. Taip, jums gali turėti netmask, tarkime 255.255.255.252, kuris yra 30 bitų tinklo, ir 2 bitai priimančiosios leidžia keturios šeimininkų tinklo. (Atkreipkite dėmesį, kad tinklo kaukė yra krūva 1-bitų sekė krūva 0-bitai.)

Bet tai šiek tiek sudėtingiau naudoti didelį skaičių eilutė kaip 255.192.0.0 kaip netmask. Visų pirma, žmonės neturi intuityvus idėja, kaip daugelis bitai, kad yra, ir, antra, tai tikrai nebus kompaktiškas. Taigi, Naujas Stilius, atėjo kartu, ir tai daug gražiau. Jūs tiesiog padėkite brūkšnį po IP adresą, tada vykdykite, kad numerį tinklo bitai ir dešimtainės trupmenos. Kaip tai: 192.0.2.12/30.

Arba, IPv6, kažką panašaus į tai: 2001:db8::/32 ar 2001:db8:5413:4028::9db9/64.

3.1.2. Prievado Numerius
Jei jums maloniai prisiminti, aš pateikti jums anksčiau su Sluoksniuotos Tinklo Modelis, kuris turėjo Interneto Sluoksnis (IP) dal nuo Šeimininko-to-Host Transporto Sluoksnį (TCP ir UDP). Gauti iki greičio, kad prieš kitą pastraipą.

Pasirodo, kad, be to, IP adresas (naudojamas IP sluoksnis), yra dar vienas adresas, kuris naudojamas TCP (stream lizdus) ir, atsitiktinai, UDP (duomenų paketų kelio schemos lizdus). Tai prievado numerį. Tai 16-bitų skaičius, kad kaip vietos adresą ryšį.

Manau, kad IP adresą, kaip namų adresą viešbučio, ir prievado numerį, kaip kambario numeris. Kad padoraus analogiją; galbūt vėliau aš ateiti iki kai kalbama automobilių pramonei.

Tarkime, jūs norite turėti kompiuterį, kuri tvarko gaunamus laiškus IR interneto paslaugų—kaip tu gali atskirti tarp dviejų kompiuteryje, su vienu IP adresu?

Na, įvairias paslaugas Internete, turime įvairių gerai žinomų prievado numerius. Jūs galite pamatyti juos visus, ir Big IANA Uosto Sąrašą arba, jei naudojate Unix lauke savo /etc/paslaugos failą. HTTP (web) yra 80 prievadas, o telnet yra uostas 23, SMTP yra 25 prievadas, žaidimas DOOM, naudojamos uosto 666 ir kt. ir t.t. Uostų pagal 1024 dažnai laikoma ypatinga, ir paprastai reikalauja specialių OS privilegijų naudoti.

Ir štai apie tai!

3.2. Baitų Eilės
Įsakymu Karalystė! Yra dviejų baitų orderings, toliau, kad būtų žinoma, kaip Nevykęs ir Didinga!

Aš juokais, bet vienas tikrai yra geriau nei kiti. 🙂

Ten tikrai nėra lengva taip pasakyti, tai, kad aš tiesiog blurt ji: jūsų kompiuterio galėjo saugoti baitų atvirkštine tvarka, už nugaros. Aš žinau! Niekas nenorėjo turiu pasakyti jums.

Dalykas yra tas, kad kiekvienas Interneto pasaulyje yra visuotinai sutarta, kad jei norite atstovauja du-baitas hex numerį, sako, b34f, jūs laikykite jį dvi eilės baitų b3 po 4f. Prasminga, ir, kaip Wilford Brimley būtų pasakyti, tai, kad Teisė, ką reikia Padaryti. Šis skaičius, saugomi su dideliu tikslu pirmiausia, yra vadinamas Dideli-Endian.

Deja, keletą kompiuterių, išsibarsčiusios čia ir ten, ir visame pasaulyje, t. y. ką nors su Intel arba Intel procesorius, saugoti baitų pasikeitė, todėl b34f būtų saugomas atmintyje, kaip eilės baitų 4f po b3. Šis saugojimo būdas yra vadinamas Mažoji-Endian.

Bet palaukite, aš ne padaryti terminija dar! Daugiau-sveikas Big-Endian taip pat yra vadinami Tinklo Baitų Tvarka, nes tai kad mums tinklo tipai panašūs.

Kompiuterio parduotuvių numerius Priimančiosios Baitų Tvarka. Jei tai Intel 80×86, Priimančiosios Baitas Kad yra Mažai-Endian. Jei tai Motorola 68k, Priimančiosios Baitas Kad yra Big-Endian. Jei tai PowerPC, Host Baitas Kad yra… na, tai priklauso!

Daug kartų, kai jūs statote paketus arba užpildyti duomenų struktūras, jums reikia įsitikinti, kad jūsų dviejų ir keturių baitų numeriai yra Tinklo Baitų Tvarka. Bet kaip jūs galite padaryti tai, jei jūs nežinote, gimtoji Priimančiosios Baitų eilės?

Geros naujienos! Jūs tiesiog gaunate manyti, Priimančiosios Baitas Kad ne teisę, ir jūs visada paleisti vertė per funkcija nustatykite jį į Tinklo Baitų Tvarka. Funkcija bus padaryti magija konversijos, jei jį turi, ir tokiu būdu jūsų kodas yra nešiojamas mašinos skirtingų endianness.

Visi righty. Yra dviejų tipų numeriai, kuriuos galima konvertuoti: trumpi (dviejų baitų) ir ilgas (keturi baitai). Šios funkcijos darbo nepasirašytas variantų, taip pat. Tarkime, jūs norite konvertuoti trumpas iš Priimančiosios Baitų, Kad Tinklo Baitų Tvarka. Pradėti su “h”, “host”, po jo su “to”, tada “n”, “network”, ir “s”, “short”: h-n-s, ar htons() (skaityti: “Host Tinklo Trumpas”).

Tai beveik per lengva…

Jūs galite naudoti kiekvieną kombinaciją “n”, “h”, “s” ir “l” jūs norite, neskaitant tikrai kvaili tie. Pavyzdžiui, NĖRA stolh() (“Trumpas, kad Ilgai Host”) funkcija—ne šios šalies, bet kokiu atveju. Tačiau yra:

htons() host to network short
htonl() host to network long
ntohs() network to host short
ntohl() network to host long

Iš esmės, jūs norite konvertuoti numerius Tinklo Baitų eilės, kol jie eiti į laidas, ir konvertuoti juos į Priimančiosios Baitų Tvarka, kaip jie ateina ne iš vielos.

Aš nežinau, 64-bitų versija, atsiprašau. Ir jei norite padaryti slankiojo kablelio, patikrinti skyriuje Serialization, kiek žemiau.

Tarkime, numerius, šiame dokumente yra Priimančiosios Baitų eilės, nebent aš sakau kitaip.

3.3. konstrukto
Na, mes pagaliau čia. Atėjo laikas kalbėti apie programavimą. Šiame straipsnyje aš padengti įvairių duomenų tipai naudojami lizdai sąsaja, nes kai kurie iš jų yra tikras lokys išsiaiškinti.

Pirmas lengva: socket aprašų. Kištukinis lizdas aprašų yra šių tipų:

int

Tiesiog reguliariai int.

Ko gauti keistai iš čia, kad tik perskaityti ir būti su manimi.

Mano Pirmasis StructTM—struct addrinfo. Ši struktūra yra naujausių išradimų, ir yra naudojama ruošimo lizdų adresas struktūrų vėlesniam naudojimui. Jis taip pat naudojamas pavadinimą paieška, ir paslaugos pavadinimas paieška. Kad jums padaryti daugiau prasmės vėliau, kai mes gauname faktinio naudojimo, bet tiesiog žinau, kad dabar jis yra vienas iš pirmųjų dalykų, jums paskambinti, kai ryšio.

struct addrinfo {
    int              ai_flags;     // AI_PASSIVE, AI_CANONNAME, etc.
    int              ai_family;    // AF_INET, AF_INET6, AF_UNSPEC
    int              ai_socktype;  // SOCK_STREAM, SOCK_DGRAM
    int              ai_protocol;  // use 0 for "any"
    size_t           ai_addrlen;   // size of ai_addr in bytes
    struct sockaddr *ai_addr;      // struct sockaddr_in or _in6
    char            *ai_canonname; // full canonical hostname

    struct addrinfo *ai_next;      // linked list, next node
};

Jums įkelti šį struct šiek tiek, ir tada skambinti getaddrinfo(). Jis bus grąžinti žymiklį į naujas susiję sąrašą, šių statinių užpildyti su visomis goodies jums reikia.

Jūs galite priversti jį naudoti IPv4 arba IPv6 į ai_family srityje, arba palikti kaip AF_UNSPEC naudoti, nepriklausomai. Tai yra cool, nes jūsų kodas gali būti IP versija-agnostikas.

Atkreipkite dėmesį, kad tai yra susiję sąrašas: ai_next taškų, kitas aspektas—gali būti, kad kelis rezultatus galite pasirinkti iš. Norėčiau naudoti, pirmasis rezultatas, kad dirbo, bet jums gali turėti skirtingus verslo poreikius; aš nežinau, viskas, vyras!

Pamatysite, kad ai_addr srityje struct addrinfo yra rodyklė į struct sockaddr. Tai yra, kai mes pradėti gauti į nitty-gritty informacijos apie tai, kas viduje IP adreso struktūra.

Jums gali ne visada reikia rašyti, kad šios struktūros; dažnai, ryšį su getaddrinfo() užpildyti jūsų struct addrinfo jums, viskas, ką jums reikia. Jūs, tačiau, turi tarpusavio viduje šie structs gauti vertybes, taip, aš esu pristatyti juos čia.

(Taip pat, visi kodas parašyta prieš struct addrinfo buvo išrastas mes supakuoti visą šią medžiagą ranka, todėl jūs pamatysite daug IPv4 kodą į laukinių kad būtent tai ir daro. Žinai, senas versijas, vadovas ir t.t.)

Kai kurie structs yra IPv4, kai yra IPv6, ir kai yra abu. Aš, kad pastabos yra ką.

Bet kokiu atveju, struct sockaddr turi lizdą adreso informacija dėl daugelio tipų lizdai.

struct sockaddr {
    unsigned short    sa_family;    // address family, AF_xxx
    char              sa_data[14];  // 14 bytes of protocol address
};

sa_family gali būti įvairių dalykų, bet tai bus AF_INET (IPv4) arba AF_INET6 (IPv6) už viską, ką mes darome šiame dokumente. sa_data yra paskirties adresą ir prievado numerį lizdą. Tai yra gana sudėtingas, nes jūs nenorite, kad nemalonu pack adresą sa_data ranka.

Kovoti su struct sockaddr, programuotojai sukūrė paralelę struktūrą: struct sockaddr_in (“in” dėl “Internet”) turi būti naudojami su IPv4.

Ir tai yra svarbu tiek: rodyklė į struct sockaddr_in gali būti įmestam į žymiklį į struct sockaddr ir atvirkščiai. Taigi, nors connect() nori struct sockaddr*, galite naudoti struct sockaddr_in ir mesti jį paskutinę minutę!

// (IPv4 only--see struct sockaddr_in6 for IPv6)

struct sockaddr_in {
    short int          sin_family;  // Address family, AF_INET
    unsigned short int sin_port;    // Port number
    struct in_addr     sin_addr;    // Internet address
    unsigned char      sin_zero[8]; // Same size as struct sockaddr
};

Ši struktūra leidžia lengvai nuoroda elementų lizdo adresą. Atkreipkite dėmesį, kad sin_zero (kuris yra įtrauktas į trinkelėmis struktūra ilgis struct sockaddr) turėtų būti nustatyti visi nuliai su funkcija memset(). Taip pat pastebėsite, kad sin_family atitinka sa_family į struct sockaddr ir turėtų būti “AF_INET”. Galiausiai, sin_port turi būti Tinklo Baitų eilės (naudojant htons()!)

Tegul kasti giliau! Matote sin_addr srityje yra struct in_addr. Kas yra tas dalykas? Na, negali būti pernelyg dramatiška, tačiau tai vienas scariest sąjungas visą laiką:

// (IPv4 only--see struct in6_addr for IPv6)

// Internet address (a structure for historical reasons)
struct in_addr {
    uint32_t s_addr; // that's a 32-bit int (4 bytes)
};

Oho! Na, jis naudojamas sąjungos, bet dabar tie laikai, atrodo, dingo. Good riddance. Todėl, jei jūs turite paskelbė ina būti tipo struct sockaddr_in, tada ina.sin_addr.s_addr nuorodos 4 baitų IP adresas (tinklo baitų eilės). Atkreipkite dėmesį, kad net jei jūsų sistema vis dar naudoja Dievo-baisu sąjungos struct in_addr, vis tiek galite pateikti nuorodą į 4-baitas IP adresas, lygiai taip pat, kaip aš pirmiau (tai dėl #defines.)

Ką apie IPv6? Panašių structs egzistuoja, taip pat:

// (IPv6 only--see struct sockaddr_in and struct in_addr for IPv4)

struct sockaddr_in6 {
    u_int16_t       sin6_family;   // address family, AF_INET6
    u_int16_t       sin6_port;     // port number, Network Byte Order
    u_int32_t       sin6_flowinfo; // IPv6 flow information
    struct in6_addr sin6_addr;     // IPv6 address
    u_int32_t       sin6_scope_id; // Scope ID
};

struct in6_addr {
    unsigned char   s6_addr[16];   // IPv6 address
};

Atkreipkite dėmesį, kad IPv6 turi IPv6 adresą ir prievado numerį, kaip ir IPv4 turi IPv4 adresą ir prievado numerį.

Taip pat atkreipkite dėmesį, kad aš ne kalbėti apie tai, kad IPv6 srauto informacijos ar Scope ID laukų metu… tai tik starteris vadovas. 🙂

Paskutinis, bet ne mažiau kaip, čia yra kita paprasta konstrukcija, struct sockaddr_storage, kuris yra suprojektuotas būti pakankamai didelis, kad turėti tiek IPv4, ir IPv6 struktūras. (Žr., kai skambučiai, kartais jums nereikia žinoti iš anksto, jei tai bus užpildyti savo struct sockaddr su IPv4, arba IPv6 adresą. Taigi, jums pereiti į šį lygiagrečios struktūra labai panašus į struct sockaddr išskyrus didesnis, ir tada mesti jį tipą, jums reikia:

struct sockaddr_storage {
    sa_family_t  ss_family;     // address family

    // all this is padding, implementation specific, ignore it:
    char      __ss_pad1[_SS_PAD1SIZE];
    int64_t   __ss_align;
    char      __ss_pad2[_SS_PAD2SIZE];
};

Tai, kas svarbu, yra tai, kad jūs galite pamatyti adresą, šeimos ss_family srityje—tai patikrinti, norėdami pamatyti, jei tai AF_INET ar AF_INET6 (IPv4 arba IPv6). Tada jūs galite mesti jį į struct sockaddr_in ar struct sockaddr_in6, jei norite.

3.4. IP Adresai, Dalis Deux
Laimei jums, yra krūva funkcijų, kurios leidžia manipuliuoti, IP adresai. Nereikia išsiaiškinti juos rankomis ir sudėti juos ilgai su << operatorius.

Pirma, tarkime, kad jūs turite struct sockaddr_in ina, ir jums turėti IP adresą “10.12.110.57” arba “2001:db8:63b3:1::3490”, kad jūs norite įrašyti į jį. Funkciją, kurią norite naudoti, inet_pton(), paverčia IP adresų skaičius-ir-taškų kursu į struct in_addr arba struct in6_addr priklausomai nuo to, ar jūs nurodote AF_INET ar AF_INET6. (“pton” reiškia “pristatymą tinklo”—galite jį vadina “spausdinimui tinklo” jei tai būtų lengviau prisiminti.) Konversija gali būti apskaičiuojama taip:

struct sockaddr_in sa; // IPv4
struct sockaddr_in6 sa6; // IPv6

inet_pton(AF_INET, "10.12.110.57", &(sa.sin_addr)); // IPv4
inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr)); // IPv6

(Trumpa pastaba: senas būdas, kaip daryti dalykus naudojama funkcija vadinama inet_addr() arba kitos funkcijos vadinamas inet_aton(); dabar jie yra pasenę ir nedirbkite su IPv6.)

Dabar, aukščiau kodo fragmentą, nėra labai patikimas, nes nėra klaidų tikrinimas. Žr., inet_pton() grąžina -1 klaidų, arba 0, jei adresas yra messed up. Taigi, patikrinkite, ar rezultatas yra didesnis nei 0 prieš naudojant!

Viskas gerai, dabar galite konvertuoti į eilutę IP adresus, kad jų dvejetainiai atstovybės. Ką apie kitus, o ne atvirkščiai? Ką daryti, jei jūs turite struct in_addr ir norite spausdinti numerius-ir-taškų kursu? (Arba struct in6_addr, kad norite, uh, “hex-ir-colons” kursu.) Šiuo atveju, jūs norite naudoti funkciją inet_ntop() (“ntop” – “tinklas pateikimas”—galite jį vadina “tinklo spausdinimui” jei tai lengviau atsiminti), kaip ir šios:

// IPv4:

char ip4[INET_ADDRSTRLEN];  // space to hold the IPv4 string
struct sockaddr_in sa;      // pretend this is loaded with something

inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);

printf("The IPv4 address is: %s\n", ip4);


// IPv6:

char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
struct sockaddr_in6 sa6;    // pretend this is loaded with something

inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);

printf("The address is: %s\n", ip6);

Kai tu jį vadini, jums pereiti adresas tipo (IPv4 arba IPv6), adresas, rodyklė į eilutę laikyti rezultatas, ir ilgis, kad stygų. (Du makrokomandas patogiai laikyti dydžio eilutę, jūs turėsite turėti didžiausią IPv4 arba IPv6 adresas: INET_ADDRSTRLEN ir INET6_ADDRSTRLEN.)

(Kitas greitas pastaba paminėti dar kartą senas būdas, kaip daryti dalykus: istoriniai funkcija, ar šis perskaičiavimas buvo vadinamas inet_ntoa(). Tai taip pat yra pasenę ir negali dirbti su IPv6.)

Galiausiai, šios funkcijos veikia tik su skaitinius IP adresus, jie negali padaryti bet nameserver DNS lookup pagrindinio kompiuterio pavadinimą, pvz., “www.example.com”. Galite naudoti getaddrinfo() padaryti, kad, kaip pamatysite vėliau.

3.4.1. Privačių (Arba Atjungtas) Tinklų
Daug vietų, turi ugniasienę, kuri slepia tinklo, nuo likusio pasaulio dėl savo apsaugos. Ir dažnai užkardos verčia “vidaus” IP adresus, kad “išorės” (kad visi kiti pasaulyje žino) IP adresus, naudojant procesą, vadinamą Tinklo Adresų Vertimas, arba NAT.

Ar jūs vis nervų dar? “Jeigu jis vyksta su visa tai keistai dalykų?”

Na, atsipalaiduoti ir nusipirkti sau nealkoholiniai (ar alkoholio) gerti, nes kaip pradedantysis, jums net nereikės nerimauti apie tai, NAT, nes tai padaryti už jus skaidriai. Bet aš norėjau kalbėti apie tinklą, už užkardos tuo atveju, jei jums pradėjo gauti supainioti, tinklo numerių, kuriuos mes matome.

Pavyzdžiui, aš turiu užkardos namuose. Turiu du statiniai IPv4 adresus, skirta mane DSL bendrovė, ir dar turiu septynis tinklo kompiuterių. Kaip tai įmanoma? Du kompiuteriai negali dalintis tuo pačiu IP adresas ar kiti duomenys nežino, kuris iš jų eiti!

Atsakymas: jie ne tą pačią, IP adresai. Jie yra privataus tinklo su 24 mln. IP adresai jam skirtos. Jie visi yra tik man. Na, visi mano, kaip toli, kaip niekam kitam. Štai kas vyksta:

Jei aš prisijungti prie nuotolinio kompiuterio, ji man sako, aš prisijungęs iš 192.0.2.33, kurie yra viešasis IP adresas mano ISP suteikė man. Bet, jei galiu paklausti, mano vietos kompiuterio, kas jo IP adresas, jis sako, 10.0.0.5. Kas yra vertimas IP adresą, iš vienos į kitą? Tai tiesa, užkardos! Tai daro NAT!

10.x.x.x yra viena iš nedaugelio rezervuota tinklus, kurie yra naudojamas tik arba visiškai atjungti tinklus ar tinklų, kurie yra už ugniasienės. Informaciją, kuri privataus tinklo numeriai yra prieinami jums naudotis yra aprašyta RFC 1918 m., tačiau kai kurios paplitusios pamatysite 10.x.x.x ir 192.168.x.x, kur x yra 0-255, paprastai. Mažiau paplitęs yra 172.y.x.x, kur y eina tarp 16 ir 31.

Tinklų už Baigiamojo užkardos nereikia būti dėl vienos iš šių rezervuota tinklus, tačiau jie paprastai yra.

(Įdomus faktas! Mano išorinis IP adresas, tikrai nėra 192.0.2.33. Į 192.0.2.x tinklas yra skirta apsimetėliai “real” IP adresus, kad galėtų būti naudojami dokumentai, kaip šis vadovas! Wowzers!)

IPv6 turi asmeninių tinklų, taip pat, tam tikra prasme. Jie bus pradėti su fdxx: (arba gal ateityje fcXX:), kaip per RFC 4193. NAT ir IPv6 don ‘ t paprastai sumaišoma, tačiau (išskyrus atvejus, kai darai IPv6, kad IPv4 vartai dalykas, kuris peržengia šį dokumentą)—teoriškai jūs turite tiek daug adresai savo žinioje, kad jums nereikės naudoti NAT ilgiau. Tačiau jei norite skirti adresus sau ant tinklo, kad nebus trasos ribų, tai yra, kaip tai padaryti.

4. Šokinėja nuo IPv4 prie IPv6

Bet aš tiesiog noriu žinoti, ką pakeisti savo kodą, kad gauti jį vyksta su IPv6! Pasakyk man dabar!

Gerai! Gerai!

Beveik viskas čia yra tai, ką aš dingo daugiau, aukščiau, bet tai trumpa versija už nekantrus. (Žinoma, yra daugiau nei tai, tačiau tai, kas taikoma vadovas.)

1. Visų pirma, pabandykite naudoti getaddrinfo() gauti visus struct sockaddr info, vietoj to, pakavimo struktūrų ranką. Tai padės išlaikyti jūsų IP versija-agnostikas, ir leis pašalinti daugelį vėlesnių veiksmų.

2. Bet kurioje vietoje, kad jums rasti, jūs sunkiai kodavimo viskas, kas susiję su IP versija, pabandyti susivynioti į pagalbininkas funkcija.

3. Pakeisti AF_INET, kad AF_INET6.

4. Pakeisti PF_INET į PF_INET6.

5. Pakeisti INADDR_ANY darbų in6addr_any užduotys, kurios šiek tiek skiriasi:

struct sockaddr_in sa;
struct sockaddr_in6 sa6;

sa.sin_addr.s_addr = INADDR_ANY;  // use my IPv4 address
sa6.sin6_addr = in6addr_any; // use my IPv6 address

Taip pat, vertė IN6ADDR_ANY_INIT gali būti naudojamas kaip initializer kai struct in6_addr deklaruojama, kaip taip:

6. Vietoj struct sockaddr_in naudoti struct sockaddr_in6, nepamiršdami pridėti, “6” srityse, kaip reikia (žr. structs, pirmiau). Nėra sin6_zero srityje.

7. Vietoj struct in_addr naudoti struct in6_addr, nepamiršdami pridėti, “6” srityse, kaip reikia (žr. structs, pirmiau).

8. Vietoj inet_aton() arba inet_addr(), naudoti inet_pton().

9. Vietoj inet_ntoa(), naudoti inet_ntop().

10. Vietoj gethostbyname(), naudokite aukščiausios getaddrinfo().

11. Vietoj gethostbyaddr(), naudokite aukščiausios getnameinfo() (nors gethostbyaddr(), vis dar galite dirbti su IPv6).

12. INADDR_BROADCAST nebeveikia. Naudoti IPv6 multicast vietoj.

Et voila!

5. Sistemos Skambučių ar Krūtinė

Tai yra skyrių, kur mes į sistemą reikia (ir kitų bibliotekų ryšiai), kuri leidžia jums prieigą prie tinklo funkcionalumą, “Unix dėžę arba bet langelis, kuris palaiko lizdai API šiuo klausimu (BSD, Windows, Linux, Mac, kas yra jums.) Kai skambinate vieną iš šių funkcijų, branduolio perima ir atlieka visą darbą už jus greitai automatiškai.

Vieta ir dauguma žmonių, trypčioti aplink čia yra tai, ką būtų skambinti šių dalykų. Tam, kad žmogus puslapiai yra ne naudoti, kaip jūs tikriausiai atrado. Na, padėti, kad baisi situacija, bandžiau išdėstyti sistema skambučius sekančiuose tiksliai (maždaug) ta pačia tvarka, kad jums reikia jiems skambinti savo programas.

Kad, kartu su keletą gabaliukų kodo pavyzdžiai čia ir ten, kai kurių pieno ir slapukus (kuri, aš bijau, jūs turėsite pateikti save), ir kai kurių žaliavų drąsos ir drąsos, ir jums bus spindi duomenų apie Interneto, kaip Son a Jon Postel!

(Atkreipkite dėmesį, kad dėl glaustumo, daug kodo fragmentus žemiau neapima būtina tikrinti, ar nėra klaidų. Ir jie labai dažnai manyti, kad rezultatas nuo skambučius getaddrinfo() sėkmės ir grįžti galiojantis įrašas susijęs sąrašą. Abu šie atvejai yra tinkamai atsižvelgta į atskiras programas, nors, todėl naudoti juos kaip pavyzdį.)

5.1. getaddrinfo()—Pasirengti pradėti!
Tai yra realus šaltiniu iš funkcija su daug galimybių, tačiau naudojimas yra iš tikrųjų gana paprasta. Ji padeda nustatyti structs jums reikia vėliau.

Maža tiek istorijos: ji naudojama taip, kad jums būtų naudoti funkcija vadinama gethostbyname() daryti DNS paieška. Tada jums reikia įkelti, kad informaciją ranka į struct sockaddr_in, ir naudoti, kad jūsų skambučių.

Tai nėra būtina, laimei. (Nėra pageidautina, jei norite parašyti kodą, kad dirba ir IPv4, ir IPv6!) Šiais laikais, dabar jūs turite funkcija getaddrinfo (), kad nėra visokių gerų dalykų už jus, įskaitant DNS ir paslaugų vardo paieška, ir užpildo structs jums reikia, be to!

Paimkime!

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *node,     // e.g. "www.example.com" or IP
                const char *service,  // e.g. "http" or port number
                const struct addrinfo *hints,
                struct addrinfo **res);

Jums pateikti šią funkciją, tris įvesties parametrai, ir ji suteikia jums žymiklį į susijęs sąrašą, res, rezultatus.

Mazgas parametras yra priimančiosios pavadinimas prisijungti, arba IP adresą.

Šalia yra parametro paslauga, kuri gali būti ir prievado numerį, pavyzdžiui, “80”, vardas arba konkreti paslauga (rasti IANA Uosto Sąrašą arba /etc/services failą į savo Unix mašina), kaip “http” ir “ftp” arba “telnet” arba “smtp” ar panašiai.

Galiausiai, užuominų parametras nurodo struct addrinfo, kad jūs jau užpildyti reikiamą informaciją.

Štai pavyzdys skambinti, jei esate serverio, kuris nori klausytis savo kompiuterio IP adresą, uosto 3490. Atkreipkite dėmesį, kad tai iš tikrųjų nėra, tai padaryti bet klausymas ar tinklo sąranką; jis tik nustato statinių mes jums naudoti vėliau:

 

Pastebėsite, kad aš ai_family į AF_UNSPEC, taip sakant, kad I don ‘ t care, jei mes naudojame IPv4 arba IPv6. Galite nustatyti, kad jis AF_INET ar AF_INET6 jei norite, kad vienas ar kitas specialiai.

Taip pat, pamatysite AI_PASSIVE vėliava egzistuoja, tai sako getaddrinfo() priskirti adresas mano vietos priimančiosios į lizdą struktūras. Tai gražus, nes tada jūs neturite hardcode. (Ar galite pateikti konkretų adresą, kaip pirmasis parametras getaddrinfo() ten, kur aš šiuo metu yra NULL, iki ten.)

Tada mes skambinti. Jei ten klaida (getaddrinfo() grąžina ne nulis), mes galime atspausdinti jį naudojant funkciją gai_strerror(), kaip matote. Jei viskas veikia tinkamai, nors, servinfo bus nurodyta, susijusios sąrašą struct addrinfos, kurių kiekviena yra struct sockaddr kai natūra, kad mes galime naudoti vėliau! Madingas!

Galiausiai, kai mes galų gale visa tai daroma su susietų sąrašo, kuris getaddrinfo (), kad mielai skirta mums, mes gali (ir turėtų) nemokamai, tai visi su kvietimu į freeaddrinfo().

Štai pavyzdys skambinti, jei esate klientas, kuris nori prisijungti prie konkretaus serverio, tarkim “www.example.net” uosto 3490. Vėlgi, tai iš tikrųjų nėra, prisijunkite, bet jame struktūras mes jums naudoti vėliau:

int status;
struct addrinfo hints;
struct addrinfo *servinfo;  // will point to the results

memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC;     // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets

// get ready to connect
status = getaddrinfo("www.example.net", "3490", &hints, &servinfo);

// servinfo now points to a linked list of 1 or more struct addrinfos

// etc.

Nuolat sakau, kad servinfo yra susijęs sąrašą su visų rūšių adresas informacijos. Parašykite greitai demo programa, parodyti šią informaciją. Ši trumpa programa bus spausdinimo IP adresus kokia priimančiosios galite nurodyti komandinėje eilutėje:

/*
** showip.c -- show IP addresses for a host given on the command line
*/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
    struct addrinfo hints, *res, *p;
    int status;
    char ipstr[INET6_ADDRSTRLEN];

    if (argc != 2) {
        fprintf(stderr,"usage: showip hostname\n");
        return 1;
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
    hints.ai_socktype = SOCK_STREAM;

    if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
        return 2;
    }

    printf("IP addresses for %s:\n\n", argv[1]);

    for(p = res;p != NULL; p = p->ai_next) {
        void *addr;
        char *ipver;

        // get the pointer to the address itself,
        // different fields in IPv4 and IPv6:
        if (p->ai_family == AF_INET) { // IPv4
            struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
            addr = &(ipv4->sin_addr);
            ipver = "IPv4";
        } else { // IPv6
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
            addr = &(ipv6->sin6_addr);
            ipver = "IPv6";
        }

        // convert the IP to a string and print it:
        inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
        printf("  %s: %s\n", ipver, ipstr);
    }

    freeaddrinfo(res); // free the linked list

    return 0;
}

Kaip matote, kodas ragina getaddrinfo() ant kokio jums perduoti komandinės eilutės, kad užpildo susijusį sąrašą nurodė, kad atsinaujinančiųjų išteklių energija, ir tada mes galime pakartoti per sąrašą ir spausdinimo medžiagą ar padaryti viską.

(Yra šiek tiek ugliness ten, kur mes turime kasti į skirtingų tipų struct sockaddrs, priklausomai nuo to, IP versija. Atsiprašome! Aš nesu tikras, kad geresnis būdas aplink jį.)

Imties paleisti! Kiekvienas myli screenshots:

$ showip www.example.net
IP addresses for www.example.net:

  IPv4: 192.0.2.88

$ showip ipv6.example.com
IP addresses for ipv6.example.com:

  IPv4: 192.0.2.101
  IPv6: 2001:db8:8c00:22::171

Dabar, kad mes turime, kad kontroliuojamos, mes naudosime rezultatus mes gauti iš getaddrinfo() perduoti kitiems lizdo funkcijas ir, pagaliau, gauti mūsų tinklo ryšį įsteigta! Laikyti svarstymas!

5.2. socket()—Gauti Failo Deskriptorius!
Manau, aš galiu įdėti jį išjungti nebėra—aš jau kalbėti apie socket() sistema skambina. Štai suskirstymas:

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

Bet kas yra tie argumentai? Jie leidžia jums pasakyti, kokios lizdas norite (IPv4 arba IPv6, upelio ar duomenų paketų kelio schemos, ir TCP arba UDP).

Ji naudojama taip, kad žmonės būtų hardcode šias vertybes, ir jūs galite visiškai vis dar tai daro. (domenas yra PF_INET ar PF_INET6, tipo, SOCK_STREAM ar SOCK_DGRAM, ir protokolo gali būti nustatyta į 0, pasirinkti tinkamą protokolo dėl tikro tipo. Arba galite skambinti getprotobyname() ieškoti protokolo norite, “tcp” arba “udp”.)

(Tai PF_INET dalykas yra artimas giminaitis AF_INET, kad jūs galite naudoti, kai inicijuojama sin_family srityje jūsų struct sockaddr_in. Iš tikrųjų, jie taip glaudžiai susiję, kad jie iš tikrųjų turi tą pačią vertę, ir daugelis programuotojų paskambins lizdas() ir perduoti AF_INET, kaip pirmasis argumentas, o ne PF_INET. Dabar, gauti kai kurių pieno ir slapukus, nes tai laikas, istorija. Kažkada, labai seniai, tai buvo mintis, kad gal adresą šeimos ( kas “AF” ir “AF_INET” stovi) gali palaikyti keletą protokolų, kurie buvo nurodyti savo protokolą šeimos (, kas “PF” ir “PF_INET” stovi). Taip neatsitiko. Ir jie visi gyveno ilgai ir laimingai, Pabaigos. Todėl labiausiai tinkamas dalykas, kurį reikia padaryti yra naudoti AF_INET jūsų struct sockaddr_in ir PF_INET jūsų skambučio į socket()..)

Bet kokiu atveju, pakaks. Tai, ką jūs tikrai norite padaryti, tai naudokite reikšmes iš rezultatų, skambinkite į getaddrinfo(), ir įtraukti jas į socket() tiesiogiai, kaip tai:

int s;
struct addrinfo hints, *res;

// do the lookup
// [pretend we already filled out the "hints" struct]
getaddrinfo("www.example.com", "http", &hints, &res);

// [again, you should do error-checking on getaddrinfo(), and walk
// the "res" linked list looking for valid entries instead of just
// assuming the first one is good (like many of these examples do.)
// See the section on client/server for real examples.]

s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

kištukinis socket() tiesiog grįžta į jums lizdas aprašų, kuriuos galite naudoti vėliau sistema skambučius, arba -1 klaidų. Pasaulio kintamasis errno yra nustatyti klaidos reikšmę (žr. errno vyras puslapyje norėdami gauti daugiau informacijos, ir trumpa pastaba naudojant errno, wielowątkowego programas.)

Baudos, baudos, baudos, bet ko gero, tai yra lizdas? Atsakymas yra, kad tai tikrai nieko gero, pats, ir jūs turite perskaityti ir padaryti daugiau sistemą, reikia jį kokią nors prasmę.

5.3. bind()—Ką uosto aš esu?
Kai jūs turite lizdą, jums gali tekti susieti, kad kištukinis lizdas su uosto į jūsų vietinio kompiuterio. (Tai paprastai daroma, jei jūs ketinate klausytis() įeinančių skambučių konkretaus uosto—multiplayer tinklo žaidimų tai padaryti, kai jie pasakys, kad “prisijungti prie 192.168.5.10 uosto 3490”.) Prievado numeris yra naudojamas branduolio rungtynės priimamojo paketinio tam tikro proceso lizdas aprašų. Jei jūs ketinate būti tik daro connect() (dėl to, kad esate klientas, o ne serverio), tai tikriausiai nebūtina. Skaityti vistiek, tik prasideda.

Čia yra trumpa už bind() sistema skambinkite:

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr, int addrlen);

sockfd yra lizdas failo deskriptorius grąžinti socket(). my_addr yra rodyklė į struct sockaddr, kuriame yra informacija apie jūsų nurodytu adresu, t. y., uosto ir IP adresą. addrlen ilgis baitais, kad adresas.

Oho. “Tai šiek tiek sugeria viena riekė. Tegul pavyzdį, kuris jungiasi lizdo šeimininko programa veikia, uosto 3490:

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

getaddrinfo(NULL, "3490", &hints, &res);

// make a socket:

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// bind it to the port we passed in to getaddrinfo():

bind(sockfd, res->ai_addr, res->ai_addrlen);

Naudojant AI_PASSIVE vėliavos, aš sakau, kad programa jungiasi prie IP priimančiosios tai veikia. Jei norite jungiasi prie konkrečių vietos IP adresą, lašas AI_PASSIVE ir įdėti IP adresą į pirmą argumentą getaddrinfo().

bind() taip pat grąžina -1 klaidų ir rinkiniai, errno klaidą vertės.

Daug senų kodą rankiniu būdu pakuočių struct sockaddr_in prieš skambinant bind(). Akivaizdu, kad tai yra IPv4-specifinis, bet ten tikrai nieko sustabdyti jus nuo daro tą patį su IPv6, išskyrus tai, kad naudojant getaddrinfo() bus lengviau, paprastai. Bet kokiu atveju, senas kodas atrodo kažkas panašaus į tai:

// !!! THIS IS THE OLD WAY !!!

int sockfd;
struct sockaddr_in my_addr;

sockfd = socket(PF_INET, SOCK_STREAM, 0);

my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT);     // short, network byte order
my_addr.sin_addr.s_addr = inet_addr("10.12.110.57");
memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);

bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr);

Pirmiau kodą, taip pat galite priskirkite INADDR_ANY į s_addr lauke, jei jūs norėjo jungiasi prie jūsų vietos IP adresą (pavyzdžiui, AI_PASSIVE vėliavos, virš.) IPv6 versija INADDR_ANY yra globalus kintamasis in6addr_any, kad yra paskirtas į sin6_addr srityje savo struct sockaddr_in6. (Taip pat makro IN6ADDR_ANY_INIT, kad galite naudoti kintamojo initializer.)

Kitas dalykas, kad saugotis kai skambina bind(): nereikia eiti underboard su savo prievado numerius. Visi uostai, žemiau 1024 yra REZERVUOTA (nebent esate administratoriaus)! Jūs galite turėti bet prievado numerį aukščiau, kad, teisę iki 65535 (jeigu jos nėra jau naudoja kita programa.)

Kartais galite pastebėti, pamėginkite pakartoti iš naujo serverio ir bind() nepavyksta, teigdamas “Adresas jau naudojamas.” Ką tai reiškia? Na, šiek tiek lizdą, kuris buvo prijungta yra dar kabo aplink branduolį, ir jis žuvo uosto. Galite palaukti, kol jis aiškiai (per minutę arba tiek), arba pridėti kodą į savo programa leidžia panaudoti uosto, kaip tai:

int yes=1;
//char yes='1'; // Solaris people use this

// lose the pesky "Address already in use" error message
if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof yes) == -1) {
    perror("setsockopt");
    exit(1);
}

Vienas mažas papildomų galutinis pastaba apie bind(): yra kartų, kai jums nereikės visiškai turi vadina. Jei esate connect(), nuotolinio mašina ir jums nerūpi, ką jūsų vietinis prievadas yra (kaip yra su telnet, jei jums rūpi tik nuotolinio uosto), galite tiesiog paskambinti connect(), ji bus patikrinkite, norėdami pamatyti, jei lizde yra neribojama, ir bus bind() tai nepanaudotos vietos uoste, jeigu būtina.

5.4. connect()—Ei, jūs!
Tegul tiesiog apsimesti, keleto minučių, kad jūs esate telnet programą. Jūsų vartotojo komandas, kurias jūs (kaip ir filmo TRON), siekiant gauti lizdas failų deskriptorių. Jūs laikytis ir skambinti socket(). Pirmyn, vartotojo pasakoja, jūs turite prisijungti prie “10.12.110.57” uosto “23” (standartinis telnet uosto.) Yow! Ką daryti dabar?

Pasisekė jums, programos, jūs dabar perusing skyriuje connect()—kaip prisijungti prie nuotolinio kompiuterio. Taigi, skaityti įnirtingai tolyn! Nebėra kada!

connect() kvietimas yra taip:

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

sockfd yra draugiškas mūsų kaimynystėje lizdas failo deskriptorius, kaip grąžinti socket() skambinkite, serv_addr yra struct sockaddr, kuriame paskirties uostą ir IP adresą, ir addrlen ilgis baitais serverio adresas struktūra.

Visą šią informaciją galima nustatyti iš rezultatų getaddrinfo() skambučio, kuris uolų.

Tai pradeda daugiau prasmės? Aš negaliu girdėti iš čia, todėl aš tiesiog tikiuosi, kad jis yra. Tegul pavyzdį, kai mes lizdo jungtis “www.example.com” uosto 3490:

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

getaddrinfo("www.example.com", "3490", &hints, &res);

// make a socket:

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// connect!

connect(sockfd, res->ai_addr, res->ai_addrlen);

Vėl, senojo mokyklos programas užpildyti savo struct sockaddr_ins perduoti prisijungti(). Jūs galite padaryti, jei norite. Matyti panašų užrašą bind() skyriuje, viršuje.

Būtinai patikrinkite, ar grįžti vertė nuo connect()—jis bus grįžti -1 klaidų ir nustatyti kintamojo errno.

Taip pat pastebėsite, kad mes ne skambinti bind(). Iš esmės, mes apie mūsų vietinio prievado numerį; mes tik rūpintis, kur mes einame (nuotolinio uostas). Branduolio rinksis uosto vietos mums, ir svetainėje, mes prisijungti prie automatiškai gauti šią informaciją iš jav. Jokių rūpesčių.

5.5. listen()—Bus kažkas, prašome kreiptis į mane?
Gerai, laikas keisti tempą. Ką daryti, jei jūs nenorite prisijungti prie nuotolinio kompiuterio. Pasakyti, tik prasideda, kad jūs norite laukti, įeinančių skambučių bei tvarkyti juos tam tikru būdu. Procesas yra dviejų etapų: pirmiausia jums listen(), tada tu accept() (žr. toliau.)

Į klausyti ryšys yra gana paprasta, tačiau reikalauja šiek tiek paaiškinimas:

int listen(int sockfd, int backlog);

sockfd yra įprasta lizdas failo deskriptorius iš socket() sistema skambina. atsilikimas yra ryšių skaičių leido gaunamus eilę. Ką tai reiškia? Na, gaunamus ryšius ketinate laukti šioje eilėje, kol tu accept() (žr. žemiau) ir tai yra riba, kiek galima eilėje. Dauguma sistemų tyliai ribą, šis skaičius apie 20; jūs tikriausiai galite išeiti su nustatę, kad 5 ar 10.

Vėl, kaip už įprastą, listen() grąžina -1 ir rinkiniai, errno klaidų.

Na, taip, jūs tikriausiai galite įsivaizduoti, mes turime skambinti bind(), prieš mes vadiname listen() taip, kad serveris yra paleistas ant konkretaus uosto. (Jūs turite būti suteikta galimybė pasakyti savo bičiuliais, kurie uosto prisijungti!) Taigi, jei jūs ketinate klausytis įeinančių skambučių, seka, sistema skambina jums padaryti yra:

getaddrinfo();
socket();
bind();
listen();
/* accept() goes here */

Aš tiesiog palikti, kad vieta mėginio kodą, nes tai gana savaime suprantama. (Kodas accept() skyriuje, žemiau, yra išsamesnė.) Tikrai keblus dalis tai sha-bang yra kvietimas accept().

5.6. accept()—”Ačiū, kad skambinate uosto 3490.”
Ruoškitės—accept() kvietimas yra ganėtinai keista! Kas nutiks, yra šios: nors toli toli bandys connect(), kad jūsų mašina uosto, kad jūs listen(). Jų ryšys bus eilę iki laukia, kol bus accept(). Galite skambinti, accept() ir jums pasako, kad gauti kol ryšį. Jis bus grąžinti jums naują lizdą failo deskriptorius šią vienos jungties! Tai tiesa, ir staiga jūs turite dvi lizdas failų deskriptoriai už vieno kainą! Originalus tai vis viena yra klausytis daugiau naujų ryšių, ir naujai sukurtas, vienas iš jų yra pagaliau paruošta send() ir recv(). Mes ten!

Skambinimas taip:

#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd yra klausytis()ing lizdas aprašų. Pakankamai lengva. adr paprastai bus rodyklė į vietos struct sockaddr_storage. Tai kur informacija apie gaunamus ryšys bus eiti (ir su juo galima nustatyti, kuris priimančiosios jums skambina iš kurio uosto). addrlen yra vietos integer, kintamasis, kad turėtų būti nustatytas sizeof(struct sockaddr_storage) prieš jo adresas yra perduota accept(). accept() nekils daugiau, nei kad daugelis baitai į adr. Jei tai kelia mažiau, ji keičiasi vertė addrlen atspindi, kad.

Atspėti, ką? accept() grąžina -1 ir rinkiniai, errno, jei klaida įvyksta. Betcha nežinojau, kad paveikslas.

Kaip ir anksčiau, tai yra krūva įsisavinti viena riekė, todėl čia pavyzdys kodo fragmentas jūsų perusal:

#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define MYPORT "3490"  // the port users will be connecting to
#define BACKLOG 10     // how many pending connections queue will hold

int main(void)
{
    struct sockaddr_storage their_addr;
    socklen_t addr_size;
    struct addrinfo hints, *res;
    int sockfd, new_fd;

    // !! don't forget your error checking for these calls !!

    // first, load up address structs with getaddrinfo():

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

    getaddrinfo(NULL, MYPORT, &hints, &res);

    // make a socket, bind it, and listen on it:

    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    bind(sockfd, res->ai_addr, res->ai_addrlen);
    listen(sockfd, BACKLOG);

    // now accept an incoming connection:

    addr_size = sizeof their_addr;
    new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);

    // ready to communicate on socket descriptor new_fd!
    .
    .
    .

Vėl atkreipkite dėmesį, kad mes bus naudoti lizdas aprašų new_fd visiems send() ir recv() ryšiai. Jei jūs tik gauti vieną ryšys kada nors, galite close() klausymosi sockfd, siekiant išvengti daugiau įeinančių skambučių pačiame uoste, jeigu tu to nori.

5.7. send() ir recv()—man kalbėti, kūdikių!
Šios dvi funkcijos yra bendravimui per stream lizdai arba prijungtas duomenų paketų kelio schemos lizdai. Jei norite naudoti reguliariai, nesusijusios duomenų paketų kelio schemos lizdai, jums reikia žr. skyrių dėl sendto() ir recvfrom(), toliau.

send() skambinkite:

int send(int sockfd, const void *msg, int len, int flags);

sockfd yra socket aprašų norite siųsti duomenis (nesvarbu, ar tai vienas grąžinti socket() arba vienas, kad jūs turite sutikti accept().) msg yra žymiklį į duomenų, kuriuos norite siųsti, ir len ilgis duomenų baitai. Tiesiog nustatyti vėliavas 0. (Žr. send() vyras puslapį ir gauti daugiau informacijos apie vėliavas.)

Kai kodo pavyzdžiai gali būti:

char *msg = "Beej was here!";
int len, bytes_sent;
.
.
.
len = strlen(msg);
bytes_sent = send(sockfd, msg, len, 0);
.
.
.

send() grąžina baitų skaičius, iš tikrųjų išsiuntė—tai gali būti mažiau nei jūs jį papasakojo siųsti! Matyti, kartais jums pasakyti, kad siųsti visas gob duomenų ir jis tiesiog negali dirbti. Tai bus ugnies ne tiek, kiek duomenų, kaip jis gali, ir pasitikėjimą, galite siųsti poilsio vėliau. Atminkite, jei vertė grąžinti send() nesutampa vertė len, tai iki jums siųsti poilsio vieta. Geros naujienos yra tai: jei paketas yra mažas (mažesnis nei 1K ar taip), tai tikriausiai valdyti siųsti visa tai visus vienu ypu. Vėl, -1 grąžinama klaidos, ir errno yra nustatyta, kad klaidų skaičius.

Į recv() kvietimas yra panaši daugeliu požiūrių:

int recv(int sockfd, void *buf, int len, int flags);

sockfd yra lizdas aprašų skaityti iš, buf, yra rezervo, perskaityti informaciją, len yra didžiausias ilgis buferis, ir vėliavėlės vėl gali būti nustatytas į 0. (Žr. recv() vyras puslapyje vėliavos informaciją.)

recv() grąžina baitų skaičius, iš tikrųjų skaityti į rezervą, arba -1 klaidų (errno nustatyti, atitinkamai.)

Palaukite! recv() galite grąžinti 0. Tai gali reikšti tik vieną dalyką: nuotolinio pusėje yra nutraukė ryšį jums! Grįžti vertė lygi 0 recv() būdu, leidžia jums žinoti, kad taip įvyko.

Ten, kad buvo lengva, ar nebuvo? Dabar galite perduoti duomenis ir atgal apie stream lizdai! Tais atvejais, kai! Jūs esate Unix Tinklo Programuotoju!

5.8. sendto() ir recvfrom()—man kalbėti, DGRAM-stilius
“Tai visos baudos ir dandy,” aš išgirsti jūsų pasakyti, “bet jei tai atostogos man, nesusijusios su duomenų paketų kelio schemos kištukiniai lizdai?” No problemo, amigo. Mes tik dalykas.

Kadangi duomenų paketų kelio schemos lizdai nėra prijungtas prie nuotolinio kompiuterio, atspėti, kokią informaciją reikia pateikti, kol mes išsiųsti paketą? Tai yra teisinga! Paskirties adresas! Čia yra samtelis:

int sendto(int sockfd, const void *msg, int len, unsigned int flags,
           const struct sockaddr *to, socklen_t tolen);

Kaip matote, šis skambutis yra iš esmės tas pats, kaip skambinti, send() be to, dviejų kita informacija. yra rodyklė į struct sockaddr (kuris tikriausiai bus dar struct sockaddr_in ar struct sockaddr_in6 ar struct sockaddr_storage, kad jūs mesti paskutinę minutę), kuriame yra paskirties IP adresą ir prievadą. tolen, int giliai žemyn, tiesiog gali būti nustatytas sizeof *arba sizeof(struct sockaddr_storage).

Gauti savo rankas ant paskirties adresą, struktūrą, jūs tikriausiai arba gauti jį iš getaddrinfo(), arba iš recvfrom(), toliau, ar galėsite užpildyti jį ranka.

Kaip su send(), sendto() grąžina baitų skaičius išsiųsti (kuris, vėlgi, gali būti mažesnis nei baitų skaičius, jūs jį papasakojo siųsti!), arba -1 klaidų.

Lygiai taip pat panašios yra recv() ir recvfrom(). Santrauką, recvfrom() yra:

int recvfrom(int sockfd, void *buf, int len, unsigned int flags,
             struct sockaddr *from, int *fromlen);

Vėlgi, tai tik kaip recv() be to, pora srityse. yra rodyklė į vietos struct sockaddr_storage, kad bus kupinas IP adresą ir prievado kilmės mašina. fromlen yra rodyklė į vietos int, kad turėtų būti parengti sizeof *ar sizeof(struct sockaddr_storage). Kai funkcija grąžina, fromlen bus pateikiama ilgis adresas faktiškai saugoma.

recvfrom() grąžina baitų skaičių gavo, arba -1 klaidų (errno nustatyti atitinkamai.)

Taigi, čia yra klausimas: kodėl mes naudojame struct sockaddr_storage kaip lizdo tipas? Kodėl gi ne struct sockaddr_in? Nes, matote, mes norime, kad nėra susieti save žemyn IPv4 arba IPv6. Todėl mes, naudojama bendra struct sockaddr_storage mes žinome, bus pakankamai didelis, kad.

(Taip… štai dar vienas klausimas: kodėl nėra struct sockaddr pati pakankamai didelis, kad bet koks adresas? Mes netgi mesti bendrosios paskirties struct sockaddr_storage į bendrosios paskirties struct sockaddr! Atrodo svetimas ir nereikalingas, ar ne. Atsakymas yra, jis tiesiog nėra pakankamai didelis, ir aš manau, kad jį pakeisti, šiuo metu būtų Sudėtinga. Taip jie padarė naują.)

Atminkite, jei galite prisijungti() duomenų paketų kelio schemos lizdo, tada galite tiesiog naudoti send() ir recv (), visus savo sandorius. Lizdą pati yra dar datagram lizdą ir pakelių vis dar naudoja UDP, bet lizdą sąsaja bus automatiškai įtraukti paskirties ir šaltinio informacija jums.

5.9. close() ir shutdown()—Get outta my face!
Oho! Jūs buvote send() ir recv() duomenimis, visą dieną ilgai, ir jūs turėjote. Esate pasirengęs nutraukti ryšį į savo lizdą aprašų. Tai labai lengva. Jūs galite tiesiog naudoti reguliariai Unix failo deskriptorius close() funkcija:

int shutdown(int sockfd, int how);

sockfd yra lizdas failo deskriptorius, kurį norite išjungti, o kaip vieną iš šių veiksmų:

 

0 Toliau gauna yra nemokama
1 Toliau siunčia yra nemokama
2 Toliau siunčia ir gauna yra nemokama (kaip ir close())

shutdown() grąžina 0 sėkmės, ir -1 klaidų (errno nustatyti atitinkamai.)

Jei jūs deign naudoti shutdown() nesusijusios duomenų paketų kelio schemos lizdai, tai bus tiesiog padaryti lizdą negalima toliau send() ir recv() skambučius (atminkite, kad jūs galite naudoti šiuos, jei jums connect() jūsų duomenų paketų kelio schemos lizdas.)

Svarbu pažymėti, kad shutdown() iš tikrųjų nėra, uždaryti failo deskriptorius—ji tiesiog keičia savo praktiškumo. Į laisvą lizdą aprašų, jums reikia naudoti close().

Nieko.

(Išskyrus prisiminti, kad, jei naudojate “Windows” ir “Winsock” kad jūs turėtumėte paskambinti closesocket() o ne close().)

5.10. getpeername()—Kas esate?
Ši funkcija yra labai lengva.

Tai taip paprasta, aš beveik nežinojau, suteikti jai savo skyriuje. Bet čia ji yra bet kokiu atveju.

Funkcija getpeername() pasakysiu, kas yra kitame gale prie upelio lizdą. Santrauką:

#include <sys/socket.h>

int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);

sockfd yra aprašyme prijungtas stream lizdas, adr yra rodyklė į struct sockaddr (arba struct sockaddr_in), kurie bus paspaudę informacijos apie kitos pusės ryšį, o addrlen yra rodyklė į int, kad turėtų būti parengti sizeof *adr arba sizeof(struct sockaddr).

Funkcija grąžina -1 klaidų ir rinkiniai, errno atitinkamai.

Kai jūs turite savo adresą, galite naudoti inet_ntop(), getnameinfo(), arba gethostbyaddr() spausdinti arba gauti daugiau informacijos. Ne, jūs negalite gauti savo prisijungimo vardą. (Gerai, gerai. Jei kitas kompiuteris veikia ident demonas, tai yra įmanoma. Tačiau tai peržengia šį dokumentą. Check out RFC 1413 daugiau info.)

5.11. gethostname()—Kas aš esu?
Net lengviau, nei getpeername() funkcija gethostname(). Jis grįžta pavadinimas kompiuterį, kad jūsų programa veikia. Pavadinimas gali būti naudojamas gethostbyname(), žemiau, nustatyti IP adresą jūsų vietinio kompiuterio.

Kas gali būti smagiau? Galiu galvoti apie keletą dalykų, bet jie nėra susiję su lizdu programavimo. Bet kokiu atveju, čia suskirstymas:

#include <unistd.h>

int gethostname(char *hostname, size_t size);

Argumentai paprasti: hostname yra rodykle masyvo simbolių, kurie bus pateikiama hostname nuo funkcija grįžti, ir dydis, ilgis baitais hostname masyvas.

Funkcija grąžina 0 apie sėkmingą ir -1 klaidų, kuriame errno, kaip įprasta.

6. Kliento-Serverio Fone

Tai yra kliento-serverio pasaulyje, kūdikis. Tiesiog apie viską, kas ant tinklas susijęs su kliento procesus kalbėti serverio procesus, ir atvirkščiai. Imtis telnet, pavyzdžiui. Kai jungiatės prie nuotolinio kompiuterio prievadu 23 su telnet (klientas), programą, kad priimančiosios (vadinamas telnetd, serverio) atgyja. Jis tvarko gaunamus telnet ryšys, rinkiniai jus su prisijungimo eilutę, ir kt.

Keistis informacija tarp kliento ir serverio yra apibendrinti aukščiau esančioje schemoje.

Atkreipkite dėmesį, kad kliento-serverio pora gali kalbėti, SOCK_STREAM, SOCK_DGRAM, ar dar ką nors (kaip ilgai, kaip jie kalba tą patį.) Gerų pavyzdžių kliento-serverio poros telnet/telnetd, ftp/ftpd, arba Firefox/Apache. Kiekvieną kartą, kai jūs naudojate ftp, ten remote programa, ftpd, kad tarnauja jums.

Dažnai, ten bus tik viena serverio mašina ir kad serveris bus apdoroti daug klientų, naudojant fork(). Pagrindinio rutina yra: serveris bus laukti ryšį, accept(), ir fork() vaikas procesą, ją apdoroti. Tai yra tai, kas mūsų pavyzdys serveryje, ar kitame skyriuje.

6.1. Paprasta Stream Serverio
Visas šis serveris tikrai yra siųsti eilutę “Hello, world!” per stream ryšį. Viskas, ką jums reikia padaryti, tai patikrinti serverio paleisti ją į vieną langą, ir telnet iš kitos su:

$ telnet remotehostname 3490

kai remotehostname pavadinimas mašina jūs naudojate jį.

Serverio kodas:

/*
** server.c -- a stream socket server demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#define PORT "3490"  // the port users will be connecting to

#define BACKLOG 10     // how many pending connections queue will hold

void sigchld_handler(int s)
{
    // waitpid() might overwrite errno, so we save and restore it:
    int saved_errno = errno;

    while(waitpid(-1, NULL, WNOHANG) > 0);

    errno = saved_errno;
}


// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
    int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_storage their_addr; // connector's address information
    socklen_t sin_size;
    struct sigaction sa;
    int yes=1;
    char s[INET6_ADDRSTRLEN];
    int rv;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE; // use my IP

    if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and bind to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("server: socket");
            continue;
        }

        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
                sizeof(int)) == -1) {
            perror("setsockopt");
            exit(1);
        }

        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("server: bind");
            continue;
        }

        break;
    }

    freeaddrinfo(servinfo); // all done with this structure

    if (p == NULL)  {
        fprintf(stderr, "server: failed to bind\n");
        exit(1);
    }

    if (listen(sockfd, BACKLOG) == -1) {
        perror("listen");
        exit(1);
    }

    sa.sa_handler = sigchld_handler; // reap all dead processes
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    printf("server: waiting for connections...\n");

    while(1) {  // main accept() loop
        sin_size = sizeof their_addr;
        new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
        if (new_fd == -1) {
            perror("accept");
            continue;
        }

        inet_ntop(their_addr.ss_family,
            get_in_addr((struct sockaddr *)&their_addr),
            s, sizeof s);
        printf("server: got connection from %s\n", s);

        if (!fork()) { // this is the child process
            close(sockfd); // child doesn't need the listener
            if (send(new_fd, "Hello, world!", 13, 0) == -1)
                perror("send");
            close(new_fd);
            exit(0);
        }
        close(new_fd);  // parent doesn't need this
    }

    return 0;
}

Tuo atveju, jei įdomu, turiu kodą į vieną didelį main() funkciją (jaučiu) sintaksines aiškumo. Jauskitės laisvai padalinti jį į mažesnius funkcijas, jei ji leidžia jaustis geriau.

(Taip pat, tai visa sigaction() dalykas gali būti naujus—tai yra gerai. Tą kodą, kuris yra atsakingas už javų zombie procesus, kurie atrodo kaip fork()ed vaiko procesų išeiti. Jei jūs darote daug zombių, ir nemanau, kad pasinaudoti jais, sistemos administratorius galės tapti susijaudinęs.)

Jūs galite gauti duomenis iš šio serverio naudojant kliento išvardytų kitame skyriuje.

6.2. Paprasta Stream Klientas
Šis vaikinas, net lengviau, nei serverio. Visa tai klientui ar yra prijungti prie kompiuterio galite nurodyti komandinėje eilutėje, uosto 3490. Jis gauna string, kad serveris siunčia.

Kliento šaltinis:

/*
** client.c -- a stream socket client demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include <arpa/inet.h>

#define PORT "3490" // the port client will be connecting to 

#define MAXDATASIZE 100 // max number of bytes we can get at once 

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(int argc, char *argv[])
{
    int sockfd, numbytes;  
    char buf[MAXDATASIZE];
    struct addrinfo hints, *servinfo, *p;
    int rv;
    char s[INET6_ADDRSTRLEN];

    if (argc != 2) {
        fprintf(stderr,"usage: client hostname\n");
        exit(1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and connect to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("client: socket");
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("client: connect");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "client: failed to connect\n");
        return 2;
    }

    inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
            s, sizeof s);
    printf("client: connecting to %s\n", s);

    freeaddrinfo(servinfo); // all done with this structure

    if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
        perror("recv");
        exit(1);
    }

    buf[numbytes] = '\0';

    printf("client: received '%s'\n",buf);

    close(sockfd);

    return 0;
}

Atkreipkite dėmesį, kad jei jūs neturite paleisti serverio, prieš jums paleisti į klientą, connect() grąžina “Ryšio atsisakė”. Labai naudinga.

6.3. Duomenų Schemos Lizdai
Mes jau apėmė pagrindai UDP paketų kelio schemos lizdai su mūsų diskusija apie sendto() ir recvfrom(), aukščiau, todėl aš tiesiog pateikti keletą pavyzdžių programas: talker.c ir listener.c.

klausytojas sėdi į mašiną laukia priimamojo paketinio uosto 4950. šnekorius siunčia paketo, kad uoste, nurodytą mašina, kad yra koks vartotojo patenka į komandų eilutę.

Čia yra šaltinis listener.c:

/*
** listener.c -- a datagram sockets "server" demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define MYPORT "4950"    // the port users will be connecting to

#define MAXBUFLEN 100

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
    int sockfd;
    struct addrinfo hints, *servinfo, *p;
    int rv;
    int numbytes;
    struct sockaddr_storage their_addr;
    char buf[MAXBUFLEN];
    socklen_t addr_len;
    char s[INET6_ADDRSTRLEN];

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_PASSIVE; // use my IP

    if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and bind to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("listener: socket");
            continue;
        }

        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("listener: bind");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "listener: failed to bind socket\n");
        return 2;
    }

    freeaddrinfo(servinfo);

    printf("listener: waiting to recvfrom...\n");

    addr_len = sizeof their_addr;
    if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN-1 , 0,
        (struct sockaddr *)&their_addr, &addr_len)) == -1) {
        perror("recvfrom");
        exit(1);
    }

    printf("listener: got packet from %s\n",
        inet_ntop(their_addr.ss_family,
            get_in_addr((struct sockaddr *)&their_addr),
            s, sizeof s));
    printf("listener: packet is %d bytes long\n", numbytes);
    buf[numbytes] = '\0';
    printf("listener: packet contains \"%s\"\n", buf);

    close(sockfd);

    return 0;
}

Pranešimą, kad į mūsų kvietimą į getaddrinfo() mes pagaliau naudojant SOCK_DGRAM. Taip pat, atkreipkite dėmesį, kad nereikia listen() arba accept(). Tai yra vienas iš privilegijos naudojant nesusijusios duomenų paketų kelio schemos lizdai!

Kitas ateina šaltinis talker.c:

/*
** talker.c -- a datagram "client" demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define SERVERPORT "4950"    // the port users will be connecting to

int main(int argc, char *argv[])
{
    int sockfd;
    struct addrinfo hints, *servinfo, *p;
    int rv;
    int numbytes;

    if (argc != 3) {
        fprintf(stderr,"usage: talker hostname message\n");
        exit(1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;

    if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and make a socket
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("talker: socket");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "talker: failed to create socket\n");
        return 2;
    }

    if ((numbytes = sendto(sockfd, argv[2], strlen(argv[2]), 0,
             p->ai_addr, p->ai_addrlen)) == -1) {
        perror("talker: sendto");
        exit(1);
    }

    freeaddrinfo(servinfo);

    printf("talker: sent %d bytes to %s\n", numbytes, argv[1]);
    close(sockfd);

    return 0;
}

Ir tai viskas yra viskas! Paleisti klausytojas, kai mašina, tada paleisti paplepėti apie kitą. Žiūrėti jais bendrauti! Įdomus G-vardinė įspūdžių visam branduolinio šeimai!

Jums net nereikia paleisti serverio šį kartą! Jūs galite paleisti paplepėti pati, ir ji tiesiog laimingai gaisrų paketus ne į eterį, kai jie išnyksta, jei ne vienas yra pasirengusi su recvfrom(), iš kitos pusės. Atminkite: duomenys siunčiami naudojant UDP paketų kelio schemos lizdai nėra garantuojamas atvykti!

Išskyrus viena mažytė detalė, kad aš jau minėta daug kartų praeityje: prijungtas duomenų paketų kelio schemos lizdai. Man reikia apie tai kalbėti, kadangi čia mes esame duomenų paketų kelio schemos dokumento skyrius. Tarkime, kad paplepėti ragina connect() ir nurodoma kito asmens adresą. Nuo to momento, paplepėti gali tik siųsti ir priimti iš nurodytu adresu connect(). Dėl šios priežasties, jūs neturite naudoti sendto() ir recvfrom(); jūs galite tiesiog naudoti send() ir recv().

7. Šiek Tiek Advanced Technika

Tai nėra labai geras, tačiau jie vis iš daugiau pagrindinių lygių mes jau taikoma. Iš tikrųjų, jei jūs dotarłeś taip toli, jums turėtų apsvarstyti sau sąžiningai atlikti pagrindai Unix tinklo programavimo! Sveikiname!

Taigi, čia mes einame į drąsų naują pasaulį, kai daugiau ezoterinių dalykų, galbūt norėsite sužinoti apie lizdai. Turėti ne ji!

7.1. Blokuoti
Blokavimas. Jūs girdėjote apie tai—dabar, kas gi tai? Trumpai tariant, “blokuoti” yra techniškai žargono “miego”. Jūs tikriausiai pastebėjote, kad kai jūs paleisti klausytojas, aukščiau, jis tiesiog sėdi ten, kol paketinių atvyksta. Kas atsitiko, kad jis vadinamas recvfrom(), nebuvo jokių duomenų ir, kad recvfrom() sako, kad “blokuoti” (tai yra, miegoti ten), kol kai kurie duomenys atvyksta.

Daug funkcijų blokas. accept() blokus. Visi recv() funkcijos blokas. Priežastis, kodėl jie gali tai padaryti, nes jie leidžiami. Kai pirmą kartą sukurti lizdas aprašų su socket(), branduolį, nustato jį blokuoja. Jei nenorite, kištukinis lizdas turi būti blokavimas, jūs turite skambinti fcntl():

#include <unistd.h>
#include <fcntl.h>
.
.
.
sockfd = socket(PF_INET, SOCK_STREAM, 0);
fcntl(sockfd, F_SETFL, O_NONBLOCK);
.
.
.

Nustatant lizdas ne antiblokavimo, galite efektyviai “apklausa” lizdą informacijos. Jei jūs pabandykite perskaityti iš ne antiblokavimo lizdą ir nėra duomenų nėra, tai nėra leidžiama blokuoti—jis grįš -1 ir errno bus EAGAIN ar EWOULDBLOCK.

(Palaukti—jis gali grįžti EAGAIN ar EWOULDBLOCK? Kuris jums patikrinti? Specifikacijos iš tikrųjų nėra, nurodyti, kuriame jūsų sistema bus grąžinti, todėl nešioti, patikrinti juos abu.)

Tačiau, apskritai kalbant, šio tipo apklausa yra bloga idėja. Jei jūs įtraukėte savo programą užimtas-laukti, ieško duomenų apie lizdas, jums įsiteikti CPU laiko, kaip tai buvo einate stiliaus. Daugiau elegantiškas sprendimas, skirtas patikrinti, norėdami pamatyti, jei yra duomenų, laukia, kad būti perskaityti ateina kitą skyriuje select().

7.2. select()—Sinchroninio I/O Multiplexing
Ši funkcija yra šiek tiek keista, bet tai labai naudinga. Imtis šių situaciją: jūs esate serverio, ir jūs norite klausytis įeinančių skambučių, taip pat išlaikyti svarstymą iš jungtys, jūs jau turite.

Ne problema, galite pasakyti, tik accept() ir pora recv(). Ne taip greitai, buster! Ką daryti, jei jūs esate blokuoti dėl accept() kvietimas? Kaip ketinate recv() duomenis tuo pačiu metu? “Naudokite ne antiblokavimo lizdai!” Jokiu būdu! Jūs nenorite būti CPU šernas. Ką tada?

select() suteikia jums galimybę stebėti keletą lizdų tuo pačiu metu. Jis jums pasakys, kuris iš jų yra paruoštas skaitymo, kuris yra paruoštas rašyti, ir kuris lizdai iškėlė išimtys, jei jūs tikrai norite žinoti, kad.

Tai, kas pasakyta, šiais laikais select(), nors labai nešiojamų, yra vienas iš lėčiausiai stebėjimo metodai lizdai. Viena iš galimų alternatyvų yra libevent, ar kažkas panašaus, kuris apjungia visus sistemos priklauso nuo dalykų, susijusių su gauti lizdas pranešimų.

Be tolesnio ceremonija, aš pasiūlyti apžvelgia select():

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int numfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

Funkcija stebi “rinkiniai” failo deskriptorius; ypač readfds, writefds, ir exceptfds. Jei norite pamatyti, jei jūs galite skaityti iš standartinio įvesties ir kai lizdas, aprašų, sockfd, tereikia pridėti failo deskriptorių 0 ir sockfd rinkinį, readfds. Parametras numfds turėtų būti nustatytas vertės aukščiausios failo deskriptorius plius vienas. Šiame pavyzdyje, ji turėtų būti nustatytas sockfd+1, nes tai buvo tikrai didesnis nei standartinės įvesties (0).

Kai select() grąžina, readfds bus pakeisti, siekiant atspindėti kuris failo deskriptorius, jūsų pasirinktą, kuris yra pasiruošęs už skaitymą. Galite išbandyti juos su makro FD_ISSET(), toliau.

Prieš pereinant daug toliau, aš kalbėti apie tai, kaip manipuliuoti šių rinkinių. Kiekviename rinkinyje yra tipo fd_set. Taip makrokomandas veikia šis modelis:

FD_SET(int fd, fd_set *set); Pridėti fd prie set.
FD_CLR(int fd, fd_set *set); Pašalinti fd  iš set.
FD_ISSET(int fd, fd_set *set); Grįžti tiesa jei fd yra set.
FD_ZERO(fd_set *set); Aišku, visi įrašai iš set.

Pagaliau, kas tai yra weirded iš struct timeval? Na, kartais nenorite laukti amžinai kas nors atsiųsti jums kai kuriuos duomenis. Gal kas 96 sekundžių norite spausdinti “Dar Vyksta…” į terminalą, nors nieko neįvyko. Šį kartą struktūra leidžia jums nustatyti skirtąjį laiką. Jei laikas yra ilgesnis ir select() vis dar nebuvo nustatyta, bet pasiruošę failo deskriptorius, jis bus grįžti, todėl jūs galite ir toliau perdirbti.

Į struct timeval turi sekti srityse:

struct timeval {
    int tv_sec;     // seconds
    int tv_usec;    // microseconds
};

Tiesiog nustatyti tv_sec į keletą sekundžių laukti, ir nustatyti tv_usec skaičius mikrosekundėmis laukti. Taip, tai mikrosekundėmis, ne milisekundes. Yra 1,000 mikrosekundėmis per milisekundės, o 1000 milisekundžių antra. Taigi, yra 1,000,000 mikrosekundėmis antra. Kodėl tai yra “usec”? “U” manoma, kad atrodo kaip graikų raidė μ (Mu), kad mes naudoti “micro”. Taip pat, kai funkcija grąžina, išlaikymas gali būti atnaujintas, siekiant parodyti metu dar likę. Tai priklauso nuo to, kokio skonio Unix naudojate.

Yay! Mes turime mikrosekundę rezoliucija laikmatis! Na, ne tikėtis jį. Jūs tikriausiai turite laukti, kai dalis jūsų standartinių Unix timeslice nesvarbu, kaip mažas, galite nustatyti, kad jūsų struct timeval.

Kitų dalykų palūkanų: Jei nustatysite, kad laukuose struct timeval 0, pasirinkite() bus timeout nedelsiant, efektyviai surenka visų failų deskriptoriai savo rinkinių. Jei nustatyta parametro timeout kaip NULL, jis niekada timeout, ir bus palaukti, kol pirmasis failo deskriptorius yra pasirengęs. Galiausiai, jei jums nereikia rūpintis, laukia tam tikrų nustatytų, jūs galite tiesiog nustatyti, kad jis NULL į skambutį, select().

Taip kodo fragmentą laukia 2,5 sekundės kažką ant standartas įėjimas:

/*
** select.c -- a select() demo
*/

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#define STDIN 0  // file descriptor for standard input

int main(void)
{
    struct timeval tv;
    fd_set readfds;

    tv.tv_sec = 2;
    tv.tv_usec = 500000;

    FD_ZERO(&readfds);
    FD_SET(STDIN, &readfds);

    // don't care about writefds and exceptfds:
    select(STDIN+1, &readfds, NULL, NULL, &tv);

    if (FD_ISSET(STDIN, &readfds))
        printf("A key was pressed!\n");
    else
        printf("Timed out.\n");

    return 0;
}

Jei jūs dėl linija buferiniame terminalą, raktas, paspauskite, turėtų būti, arba ji bus laiko vistiek.

Dabar, kai jūs manote, tai yra puikus būdas laukti, duomenų paketų kelio schemos lizdas—ir tu teisus: tai galėtų būti. Kai kurie Unices gali naudoti pasirinkite tokiu būdu, ir kai kurios negali. Jūs turėtumėte pamatyti, kas jūsų vietos žmogus puslapis sako, kad dėl šio klausimo jei jūs norite bandyti jį.

Kai kurie Unices atnaujinti kartą jūsų struct timeval atspindi, kiek laiko dar prieš pauzės. Bet kiti-ne. Don ‘ t remtis, kad įvykusiu, jei norite būti nešiojami. (Naudoti gettimeofday() jei jums reikia sekti, kiek laiko praėjo. Tai nusivylimas, aš žinau, bet, kad taip, kaip yra.)

Kas atsitiks, jei lizdo skaityti nustatyti nutraukia ryšį? Na, tokiu atveju, select() grąžina, kad lizdas aprašų rinkinys, kaip “pasiruošęs skaityti”. Kai jūs iš tikrųjų recv(), iš jo recv() grąžins 0. Tai, kaip jūs žinote, klientas turi nutraukė ryšį.

Dar viena pastaba palūkanų maždaug select(): jei turite lizdą, kuris yra klausytis()ing, galite patikrinti, norėdami pamatyti, jei yra naujas ryšys, įsidėjęs, kad lizdas failo deskriptorius readfds nustatyti.

Ir, kad mano draugai, trumpa apžvalga visagalio select() funkcija.

Tačiau, pagal paklausą, čia yra išsamus pavyzdys. Deja, skirtumas tarp purvo-paprastas pavyzdys, kas išdėstyta, ir tai čia yra didelė. Bet pažvelkite, tada skaityti aprašymą, kad taip.

Ši programa veikia kaip paprastas multi-user pokalbių serverio. Pradėti jis veikia vienas langas, tada telnet it (“telnet hostname 9034”), iš daugelio kitų windows. Kai rašote ką nors vieną telnet seansą, ji turėtų būti ir visi kiti.

/*
** selectserver.c -- a cheezy multiperson chat server
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define PORT "9034"   // port we're listening on

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
    fd_set master;    // master file descriptor list
    fd_set read_fds;  // temp file descriptor list for select()
    int fdmax;        // maximum file descriptor number

    int listener;     // listening socket descriptor
    int newfd;        // newly accept()ed socket descriptor
    struct sockaddr_storage remoteaddr; // client address
    socklen_t addrlen;

    char buf[256];    // buffer for client data
    int nbytes;

    char remoteIP[INET6_ADDRSTRLEN];

    int yes=1;        // for setsockopt() SO_REUSEADDR, below
    int i, j, rv;

    struct addrinfo hints, *ai, *p;

    FD_ZERO(&master);    // clear the master and temp sets
    FD_ZERO(&read_fds);

    // get us a socket and bind it
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
        fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
        exit(1);
    }
    
    for(p = ai; p != NULL; p = p->ai_next) {
        listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (listener < 0) { 
            continue;
        }
        
        // lose the pesky "address already in use" error message
        setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

        if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
            close(listener);
            continue;
        }

        break;
    }

    // if we got here, it means we didn't get bound
    if (p == NULL) {
        fprintf(stderr, "selectserver: failed to bind\n");
        exit(2);
    }

    freeaddrinfo(ai); // all done with this

    // listen
    if (listen(listener, 10) == -1) {
        perror("listen");
        exit(3);
    }

    // add the listener to the master set
    FD_SET(listener, &master);

    // keep track of the biggest file descriptor
    fdmax = listener; // so far, it's this one

    // main loop
    for(;;) {
        read_fds = master; // copy it
        if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
            perror("select");
            exit(4);
        }

        // run through the existing connections looking for data to read
        for(i = 0; i <= fdmax; i++) {
            if (FD_ISSET(i, &read_fds)) { // we got one!!
                if (i == listener) {
                    // handle new connections
                    addrlen = sizeof remoteaddr;
                    newfd = accept(listener,
                        (struct sockaddr *)&remoteaddr,
                        &addrlen);

                    if (newfd == -1) {
                        perror("accept");
                    } else {
                        FD_SET(newfd, &master); // add to master set
                        if (newfd > fdmax) {    // keep track of the max
                            fdmax = newfd;
                        }
                        printf("selectserver: new connection from %s on "
                            "socket %d\n",
                            inet_ntop(remoteaddr.ss_family,
                                get_in_addr((struct sockaddr*)&remoteaddr),
                                remoteIP, INET6_ADDRSTRLEN),
                            newfd);
                    }
                } else {
                    // handle data from a client
                    if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
                        // got error or connection closed by client
                        if (nbytes == 0) {
                            // connection closed
                            printf("selectserver: socket %d hung up\n", i);
                        } else {
                            perror("recv");
                        }
                        close(i); // bye!
                        FD_CLR(i, &master); // remove from master set
                    } else {
                        // we got some data from a client
                        for(j = 0; j <= fdmax; j++) {
                            // send to everyone!
                            if (FD_ISSET(j, &master)) {
                                // except the listener and ourselves
                                if (j != listener && j != i) {
                                    if (send(j, buf, nbytes, 0) == -1) {
                                        perror("send");
                                    }
                                }
                            }
                        }
                    }
                } // END handle data from client
            } // END got new incoming connection
        } // END looping through file descriptors
    } // END for(;;)--and you thought it would never end!
    
    return 0;
}

Pranešimo turiu du failo deskriptorius rinkinių kodas: meistras ir read_fds. Pirma, kapitonas, turinti visas lizdas aprašai, kurie yra prisijungę, taip pat lizdą aprašų, kad yra klausymas dėl naujos jungtys.

Priežastis, dėl kurios aš jau kapitonas nustatyti, kad select() iš tikrųjų keičia nustatyti jums perduoti į ją atspindėti, kurios lizdai yra pasirengusi skaityti. Kadangi aš turiu sekti jungtys vieną kvietimą, select() į kitą, aš privalo saugoti šiuos saugiai toli kažkur. Ne paskutinę minutę, aš nukopijuoti meistras į read_fds, ir tada skambinti, select().

Bet ne, tai reiškia, kad kiekvieną kartą, kai gaunu naują ryšį, turiu pridėti jį kapitonas komplektą? Yup! Ir kiekvieną kartą, kai ryšys užsidaro, turiu jį pašalinti iš kapitonas komplektą? Taip, ji.

Pranešimo galėčiau patikrinti, norėdami pamatyti, kai klausytojas lizdas yra pasirengusi skaityti. Kai jis yra, tai reiškia, kad turiu naują ryšį laukiama, ir aš sutinku), ir įtraukti jį į pagrindinį rinkinį. Panašiai, kai klientas sujungimas yra paruoštas ir skaityti, ir recv() grąžina 0, žinau, kad klientas buvo uždarytas į ryšį, ir aš turiu jį pašalinti iš master rinkinį.

Jei klientas recv() grąžina ne nulis, nors, žinau, kai kurie duomenys buvo gauti. Taip man ji, ir tada eiti per pagrindinį sąrašą ir siųsti duomenis visi kiti susijusių klientų.

Ir kad, mano draugai, yra mažiau nei paprasta apžvalga visagalis select() funkcija.

Be to, čia yra premijos mąstymas: yra dar viena funkcija vadinama poll(), kurie elgiasi panašiai select() nėra, bet su skirtinga valdymo sistema failo deskriptorius rinkinių. Check it out!

7.3. Tvarkymo Dalinis send()
Prisimenu tą skyrių apie send(), anksčiau, kai pasakiau, kad send() gali siųsti visi baitai jūs paprašė ją? Tai yra, jūs norite jį siųsti 512 baitų, o ji grįžta 412. Kas atsitiko, kad likusius 100 baitų?

Na, jie dar šiek tiek rezervo, kurie turi būti išsiųsti. Dėl aplinkybių, nepriklausančių aplinkybių, branduolio nusprendė nesiųsti visų duomenų viena riekė, ir dabar, mano draugas, tai iki jums gauti duomenų iš ten.

Jūs galite rašyti funkciją, kaip tai padaryti, taip pat:

#include <sys/types.h>
#include <sys/socket.h>

int sendall(int s, char *buf, int *len)
{
    int total = 0;        // how many bytes we've sent
    int bytesleft = *len; // how many we have left to send
    int n;

    while(total < *len) {
        n = send(s, buf+total, bytesleft, 0);
        if (n == -1) { break; }
        total += n;
        bytesleft -= n;
    }

    *len = total; // return number actually sent here

    return n==-1?-1:0; // return -1 on failure, 0 on success
}

Šiame pavyzdyje, s lizdą, į kurį norite siųsti duomenis, buf, yra rezervo, kuriuose yra duomenų, ir len yra žymiklį į int, kuriuose baitų skaičių rezervo.

Funkcija grąžina -1 klaidų (ir errno vis dar yra nustatytas iš skambinti, send().) Taip pat, baitų skaičius išsiųsti grąžinama len. Tai bus pats baitų skaičius, jūs paprašė ją siųsti, nebent ten buvo klaida. sendall() bus padaryti, tai geriausia, huffing ir skleisdamas, siųsti duomenis, bet jei ten klaida, jis gauna su jumis iš karto.

Dėl išsamumo, čia pavyzdys skambutis funkcija:

char buf[10] = "Beej!";
int len;

len = strlen(buf);
if (sendall(s, buf, &len) == -1) {
    perror("sendall");
    printf("We only sent %d bytes because of the error!\n", len);
}

Kas atsitinka, gavėjo pabaigoje, kai dalis paketinių atvyksta? Jei paketai yra kintamo ilgio, kaip imtuvas, žinoti, kada vienas pakelis baigiasi, o kitas prasideda? Taip, realaus pasaulio scenarijus, royal skausmas asilai. Jūs tikriausiai turite viską (atminkite, kad iš duomenų inkapsuliacija skyriuje kelią atgal ten pradžioje?) Skaitykite informacija!

Trumpa pastaba, kad visi jūs Linux ventiliatoriai ten: kartais, retais atvejais, Linux select() gali grįžti “ready-to-read” ir tada ne iš tikrųjų būti pasiruošę skaityti! Tai reiškia, kad ji bus blokuoti read(), po to select() sako, kad ji nebus! Kodėl jūs mažai! Bet kokiu atveju, problemos sprendimas yra nustatyti O_NONBLOCK vėliavos gavimo lizdą, todėl klaidų, su EWOULDBLOCK (kuriuos jūs galite tiesiog nekreipti dėmesio, jei tai įvyksta). Matyti fcntl() nuoroda puslapio, daugiau info apie nustatymas lizdas ne antiblokavimo.

7.4. Serialization—Kaip Pakuotės Duomenys
Tai gana paprasta, siųsti tekstinius duomenis tinkle, jūs rasti, bet kas atsitiks, jei norite siųsti kai kuriuos “dvejetainis” duomenų, pavyzdžiui, ints ar plūdes? Pasirodo, jūs turite keletą galimybių.

  1. Konvertuoja skaičių į tekstą su funkcija, kaip sprintf(), tada siųsti tekstą. Imtuvas bus apdoroti tekstą, grįžti į numerį naudojant funkciją kaip strtol().
  2. Tiesiog atsiųskite duomenų raw, artimųjų žymiklį į duomenų send().
  3. Koduoti skaičių į nešiojamąjį dvejetainis forma. Imtuvas bus iššifruoti jį.

Sneak preview! Šį vakarą tik!

[Uždanga kyla]

Beej sako: “aš norėčiau Metodas Tris, aukščiau!”

[PABAIGA]

(Kol aš pradėti šiame skyriuje rimtai, norėčiau pasakyti, kad yra bibliotekos, iš ten, tai daryti, ir sukti savo ir likusių nešiojamų ir klaidų, yra gana sudėtingas uždavinys. Taigi, medžioti aplink ir daryti savo namų darbus prieš priimant sprendimą įgyvendinti šią medžiagą sau. Pateikiu informacijos čia tiems, smalsu, kaip dalykai patinka šis darbas.)

Iš tikrųjų visi metodai, virš jų turi savo trūkumų ir privalumų, bet, kaip ir sakiau, apskritai, aš norėčiau trečias būdas. Pirma, nors, apie kai kurių trūkumų, ir privalumų, kiti du.

Pirmasis metodas, kodavimas numerius, prieš išsiųsdami pranešimą, turi tą privalumą, kad jūs galite lengvai spausdinti ir nuskaityti duomenis, tai ateina per laidą. Kartais žmonėms suprantama protokolas yra puikus naudoti ne pralaidumo intensyviai situaciją, kaip, pavyzdžiui, Internet Relay Chat (IRC). Tačiau ji turi trūkumą, kad jis yra lėtas konvertuoti, o rezultatai beveik visada užima daugiau vietos, nei originalo numeris!

Metodas du: artimosios pirminiai duomenys. Tai yra gana lengvai (bet ir pavojinga!): tik žymiklį į duomenų siųsti, ir skambinti, siųsti su juo.

double d = 3490.15926535;

send(s, &d, sizeof d, 0);  /* DANGER--non-portable! */

Gavėjas gauna tai, kaip šis:

double d;

recv(s, &d, sizeof d, 0);  /* DANGER--non-portable! */

Greita, paprasta—kas nepatinka? Na, pasirodo, kad ne visi architektūros, sudaro dvigubą (ar int šiuo klausimu) su tą patį bitų atstovavimu ar net pačiu baitų seka! Kodas yra neabejotinai ne nešiojamas. (Ei—galbūt jums nereikia nešioti, tokiu atveju yra gražus ir greitas.)

Kai pakavimo integer rūšių, mes jau matėme, kaip htons()-klasės funkcijas, gali padėti išlaikyti dalykų, nešiojamų keičiant numerius į Tinklo Baitų Tvarka, ir kaip tai teisinga. Deja, nėra jokių panašių funkcijų plaukti rūšių. Yra visas viltis prarado?

Nebijok! (Buvote bijo ten, už antrą? Ne? Net ne šiek tiek?) Ten yra kažkas, ką galime padaryti, mes galime įpakuoti (arba “marshal”, arba “serialize”, arba vienas tūkstantis milijonų, kiti pavadinimai) duomenis į žinomą formatą, kad imtuvas gali išskleisti nuotolinio pusėje.

Ką turiu galvoje “žinoma, dvejetainis formatas”? Na, mes jau matėme, kad htons() pavyzdys, tiesa? Ji kinta (arba “užkoduoja”, jei nori galvoti apie tai, kad taip) numeris iš bet kokio šeimininko formatas yra į Tinklo Baitų Tvarka. Atvirkštinis (unencode) skaičius, imtuvas ragina ntohs().

Bet ne, aš tiesiog gauti baigė sakydamas, nebuvo jokių tokių funkcijų dėl kitų ne sveikasis skaičius, tipai? Taip. Aš. Ir kadangi nėra standartinis būdas C padaryti, tai šiek tiek marinatas (kad neatlygintinai kalambūras ten jums Python gerbėjai).

Ką reikia padaryti yra pakuotėje duomenis į žinomą formatą ir siųsti, kad per laidą dekodavimo. Pavyzdžiui, pakuotėje plūdes, čia kažkas, greitai ir purvinas, su daug galimybių tobulinti:

#include <stdint.h>

uint32_t htonf(float f)
{
    uint32_t p;
    uint32_t sign;

    if (f < 0) { sign = 1; f = -f; }
    else { sign = 0; }
        
    p = ((((uint32_t)f)&0x7fff)<<16) | (sign<<31); // whole part and sign
    p |= (uint32_t)(((f - (int)f) * 65536.0f))&0xffff; // fraction

    return p;
}

float ntohf(uint32_t p)
{
    float f = ((p>>16)&0x7fff); // whole part
    f += (p&0xffff) / 65536.0f; // fraction

    if (((p>>31)&0x1) == 0x1) { f = -f; } // sign bit set

    return f;
}

Aukščiau pateiktas kodas yra tarsi naivus įgyvendinimo, kad parduotuvėse plaukti 32-bitų skaičius. Aukštos bitų (31) yra naudojami saugoti ženklas, skaičių (“1” – neigiamos), o kitą septyni bitai (30-16) yra naudojami saugoti visą numeris dalis plūdės. Galiausiai, likusi bitų (15-0) yra naudojami saugoti frakcinės dalį, skaičių.

Naudojimas yra gana paprasta:

#include <stdio.h>

int main(void)
{
    float f = 3.1415926, f2;
    uint32_t netf;

    netf = htonf(f);  // convert to "network" form
    f2 = ntohf(netf); // convert back to test

    printf("Original: %f\n", f);        // 3.141593
    printf(" Network: 0x%08X\n", netf); // 0x0003243F
    printf("Unpacked: %f\n", f2);       // 3.141586

    return 0;
}

Apie plius pusėje, tai mažas, paprastas ir greitas. Dėl minuso pusę, tai nėra efektyviai išnaudoti erdvę, ir diapazonas yra griežtai ribojamas—pabandykite saugoti daug didesnis nei 32767 ten ir ji negali būti labai laimingas! Taip pat galite pamatyti pavyzdyje aukščiau, kad pastaruosius porą dešimtųjų tikslumu nėra tinkamai konservuoti.

Ką mes galime daryti vietoj to? Na, Standartinės saugoti slankiojo kablelio skaičiais yra žinomas kaip IEEE-754. Dauguma kompiuterių naudokite šį formatą, viduje daro slankiojo kablelio matematika, todėl tais atvejais, griežtai kalbant, konvertavimo negi reikia nuveikti. Bet jei jūs norite, kad jūsų kodo būti nešiojami, tai prielaida jūs negalite būtinai padaryti. (Kita vertus, jei norite, kad viskas būtų greitai, jums reikia optimizuoti tai iš platformų, kad nereikia daryti! Tai, ką htons() ir jo ilk daryti.)

Štai tam tikrą kodą, kuris užkoduoja plūdes ir vaikinai, į IEEE-754 formatu. (Dažniausiai—ji nėra koduoti NaN arba Infinity, tačiau ji gali būti modifikuota, kad padaryti, kad.)

#define pack754_32(f) (pack754((f), 32, 8))
#define pack754_64(f) (pack754((f), 64, 11))
#define unpack754_32(i) (unpack754((i), 32, 8))
#define unpack754_64(i) (unpack754((i), 64, 11))

uint64_t pack754(long double f, unsigned bits, unsigned expbits)
{
    long double fnorm;
    int shift;
    long long sign, exp, significand;
    unsigned significandbits = bits - expbits - 1; // -1 for sign bit

    if (f == 0.0) return 0; // get this special case out of the way

    // check sign and begin normalization
    if (f < 0) { sign = 1; fnorm = -f; }
    else { sign = 0; fnorm = f; }

    // get the normalized form of f and track the exponent
    shift = 0;
    while(fnorm >= 2.0) { fnorm /= 2.0; shift++; }
    while(fnorm < 1.0) { fnorm *= 2.0; shift--; }
    fnorm = fnorm - 1.0;

    // calculate the binary form (non-float) of the significand data
    significand = fnorm * ((1LL<<significandbits) + 0.5f);

    // get the biased exponent
    exp = shift + ((1<<(expbits-1)) - 1); // shift + bias

    // return the final answer
    return (sign<<(bits-1)) | (exp<<(bits-expbits-1)) | significand;
}

long double unpack754(uint64_t i, unsigned bits, unsigned expbits)
{
    long double result;
    long long shift;
    unsigned bias;
    unsigned significandbits = bits - expbits - 1; // -1 for sign bit

    if (i == 0) return 0.0;

    // pull the significand
    result = (i&((1LL<<significandbits)-1)); // mask
    result /= (1LL<<significandbits); // convert back to float
    result += 1.0f; // add the one back on

    // deal with the exponent
    bias = (1<<(expbits-1)) - 1;
    shift = ((i>>significandbits)&((1LL<<expbits)-1)) - bias;
    while(shift > 0) { result *= 2.0; shift--; }
    while(shift < 0) { result /= 2.0; shift++; }

    // sign it
    result *= (i>>(bits-1))&1? -1.0: 1.0;

    return result;
}

Aš įdėti keletą naudingų makrokomandas ten viršuje pakavimo ir išpakavimo 32-bit (tikriausiai plaukti) ir 64-bitų (tikriausiai du kartus) numeriai, bet pack754() funkcija gali būti vadinama tiesiogiai ir papasakojo, kad koduoti bitų verta duomenų (expbits kurios yra rezervuotos normalizuotą skaičius eksponentė.)

Čia pavyzdys naudojimo:

#include <stdio.h>
#include <stdint.h> // defines uintN_t types
#include <inttypes.h> // defines PRIx macros

int main(void)
{
    float f = 3.1415926, f2;
    double d = 3.14159265358979323, d2;
    uint32_t fi;
    uint64_t di;

    fi = pack754_32(f);
    f2 = unpack754_32(fi);

    di = pack754_64(d);
    d2 = unpack754_64(di);

    printf("float before : %.7f\n", f);
    printf("float encoded: 0x%08" PRIx32 "\n", fi);
    printf("float after  : %.7f\n\n", f2);

    printf("double before : %.20lf\n", d);
    printf("double encoded: 0x%016" PRIx64 "\n", di);
    printf("double after  : %.20lf\n", d2);

    return 0;
}

Aukščiau kodas gamina šią produkciją:

float before : 3.1415925
float encoded: 0x40490FDA
float after  : 3.1415925

double before : 3.14159265358979311600
double encoded: 0x400921FB54442D18
double after  : 3.14159265358979311600

Kitas klausimas, jums gali turėti, kaip jūs pack structs? Deja, kompiliatorius yra nemokamas įdėti padding visos vietos į struct, ir tai reiškia, kad jūs negalite portably siųsti visa tai per vielos viena riekė. (Jūs nesate vis serga klausos “negali to padaryti”, “negaliu to padaryti”? Atsiprašome! Citata draugas”, Jei nieko goes wrong, aš visada kaltina “Microsoft”.” Tai vienas gali būti ne Microsoft kaltė, tiesa, bet mano draugo teiginys yra visiškai teisingas.)

Atgal į jį: geriausias būdas siųsti struct per viela yra pakuotėje kiekviename lauke atskirai ir tada išpakuokite juos į struct, kai jie atvyksta iš kitos pusės.

Tai daug darbo, yra tai, ką jūs galvojate. Taip, ji yra. Vienas dalykas, jūs galite padaryti, tai parašyti pagalbininkas funkcija, siekiant padėti pack duomenis jūs. Jis bus įdomus! Tikrai!

Knygoje “Praktika Programavimo” Kernighan ir Lydekos, jie įgyvendinti printf()-kaip ir funkcijas, vadinamas pack() ir unpack(), kad daryti būtent tai. Aš nuorodą į juos, bet, matyt, kad šios funkcijos nėra internete su poilsio šaltinis iš knygos.

(Praktika Programavimas yra puikus skaityti. Dzeusas taupo kačiukas kiekvieną kartą, aš rekomenduoju jį.)

Šiuo metu, aš ruošiuosi mesti žymiklį į BSD licencijos, Įvedamas Parametras Kalba C API, kurios aš niekada nesinaudojo, bet atrodo visiškai garbingas. Python ir Perl programuotojai nori patikrinti savo kalbos pack() ir unpack() funkcijoms atlikti tą patį. Ir Java yra ir didelis-olis Serializable sąsają, kuri gali būti naudojama panašiai.

Tačiau, jei norite parašyti pakavimo naudingumo C, K&P triukas yra naudojamas kintamas argumentas sąrašus, kad printf()-patinka funkcijas statyti pakelių. Štai versiją aš virti ant savo grindžiamas tuo, kuris, kaip tikimasi, bus pakankamai, kad suteikti jums idėja kaip toks dalykas gali dirbti.

(Šis kodas nuorodos pack754() funkcijas, viršuje. Į packi*() funkcijas veikia kaip pažįstamas htons() šeimos, išskyrus atvejus, jie paketą į char masyvas, o ne kito sveikojo skaičiaus.)

#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>

/*
** packi16() -- store a 16-bit int into a char buffer (like htons())
*/ 
void packi16(unsigned char *buf, unsigned int i)
{
    *buf++ = i>>8; *buf++ = i;
}

/*
** packi32() -- store a 32-bit int into a char buffer (like htonl())
*/ 
void packi32(unsigned char *buf, unsigned long int i)
{
    *buf++ = i>>24; *buf++ = i>>16;
    *buf++ = i>>8;  *buf++ = i;
}

/*
** packi64() -- store a 64-bit int into a char buffer (like htonl())
*/ 
void packi64(unsigned char *buf, unsigned long long int i)
{
    *buf++ = i>>56; *buf++ = i>>48;
    *buf++ = i>>40; *buf++ = i>>32;
    *buf++ = i>>24; *buf++ = i>>16;
    *buf++ = i>>8;  *buf++ = i;
}

/*
** unpacki16() -- unpack a 16-bit int from a char buffer (like ntohs())
*/ 
int unpacki16(unsigned char *buf)
{
    unsigned int i2 = ((unsigned int)buf[0]<<8) | buf[1];
    int i;

    // change unsigned numbers to signed
    if (i2 <= 0x7fffu) { i = i2; }
    else { i = -1 - (unsigned int)(0xffffu - i2); }

    return i;
}

/*
** unpacku16() -- unpack a 16-bit unsigned from a char buffer (like ntohs())
*/ 
unsigned int unpacku16(unsigned char *buf)
{
    return ((unsigned int)buf[0]<<8) | buf[1];
}

/*
** unpacki32() -- unpack a 32-bit int from a char buffer (like ntohl())
*/ 
long int unpacki32(unsigned char *buf)
{
    unsigned long int i2 = ((unsigned long int)buf[0]<<24) |
                           ((unsigned long int)buf[1]<<16) |
                           ((unsigned long int)buf[2]<<8)  |
                           buf[3];
    long int i;

    // change unsigned numbers to signed
    if (i2 <= 0x7fffffffu) { i = i2; }
    else { i = -1 - (long int)(0xffffffffu - i2); }

    return i;
}

/*
** unpacku32() -- unpack a 32-bit unsigned from a char buffer (like ntohl())
*/ 
unsigned long int unpacku32(unsigned char *buf)
{
    return ((unsigned long int)buf[0]<<24) |
           ((unsigned long int)buf[1]<<16) |
           ((unsigned long int)buf[2]<<8)  |
           buf[3];
}

/*
** unpacki64() -- unpack a 64-bit int from a char buffer (like ntohl())
*/ 
long long int unpacki64(unsigned char *buf)
{
    unsigned long long int i2 = ((unsigned long long int)buf[0]<<56) |
                                ((unsigned long long int)buf[1]<<48) |
                                ((unsigned long long int)buf[2]<<40) |
                                ((unsigned long long int)buf[3]<<32) |
                                ((unsigned long long int)buf[4]<<24) |
                                ((unsigned long long int)buf[5]<<16) |
                                ((unsigned long long int)buf[6]<<8)  |
                                buf[7];
    long long int i;

    // change unsigned numbers to signed
    if (i2 <= 0x7fffffffffffffffu) { i = i2; }
    else { i = -1 -(long long int)(0xffffffffffffffffu - i2); }

    return i;
}

/*
** unpacku64() -- unpack a 64-bit unsigned from a char buffer (like ntohl())
*/ 
unsigned long long int unpacku64(unsigned char *buf)
{
    return ((unsigned long long int)buf[0]<<56) |
           ((unsigned long long int)buf[1]<<48) |
           ((unsigned long long int)buf[2]<<40) |
           ((unsigned long long int)buf[3]<<32) |
           ((unsigned long long int)buf[4]<<24) |
           ((unsigned long long int)buf[5]<<16) |
           ((unsigned long long int)buf[6]<<8)  |
           buf[7];
}

/*
** pack() -- store data dictated by the format string in the buffer
**
**   bits |signed   unsigned   float   string
**   -----+----------------------------------
**      8 |   c        C         
**     16 |   h        H         f
**     32 |   l        L         d
**     64 |   q        Q         g
**      - |                               s
**
**  (16-bit unsigned length is automatically prepended to strings)
*/ 

unsigned int pack(unsigned char *buf, char *format, ...)
{
    va_list ap;

    signed char c;              // 8-bit
    unsigned char C;

    int h;                      // 16-bit
    unsigned int H;

    long int l;                 // 32-bit
    unsigned long int L;

    long long int q;            // 64-bit
    unsigned long long int Q;

    float f;                    // floats
    double d;
    long double g;
    unsigned long long int fhold;

    char *s;                    // strings
    unsigned int len;

    unsigned int size = 0;

    va_start(ap, format);

    for(; *format != '\0'; format++) {
        switch(*format) {
        case 'c': // 8-bit
            size += 1;
            c = (signed char)va_arg(ap, int); // promoted
            *buf++ = c;
            break;

        case 'C': // 8-bit unsigned
            size += 1;
            C = (unsigned char)va_arg(ap, unsigned int); // promoted
            *buf++ = C;
            break;

        case 'h': // 16-bit
            size += 2;
            h = va_arg(ap, int);
            packi16(buf, h);
            buf += 2;
            break;

        case 'H': // 16-bit unsigned
            size += 2;
            H = va_arg(ap, unsigned int);
            packi16(buf, H);
            buf += 2;
            break;

        case 'l': // 32-bit
            size += 4;
            l = va_arg(ap, long int);
            packi32(buf, l);
            buf += 4;
            break;

        case 'L': // 32-bit unsigned
            size += 4;
            L = va_arg(ap, unsigned long int);
            packi32(buf, L);
            buf += 4;
            break;

        case 'q': // 64-bit
            size += 8;
            q = va_arg(ap, long long int);
            packi64(buf, q);
            buf += 8;
            break;

        case 'Q': // 64-bit unsigned
            size += 8;
            Q = va_arg(ap, unsigned long long int);
            packi64(buf, Q);
            buf += 8;
            break;

        case 'f': // float-16
            size += 2;
            f = (float)va_arg(ap, double); // promoted
            fhold = pack754_16(f); // convert to IEEE 754
            packi16(buf, fhold);
            buf += 2;
            break;

        case 'd': // float-32
            size += 4;
            d = va_arg(ap, double);
            fhold = pack754_32(d); // convert to IEEE 754
            packi32(buf, fhold);
            buf += 4;
            break;

        case 'g': // float-64
            size += 8;
            g = va_arg(ap, long double);
            fhold = pack754_64(g); // convert to IEEE 754
            packi64(buf, fhold);
            buf += 8;
            break;

        case 's': // string
            s = va_arg(ap, char*);
            len = strlen(s);
            size += len + 2;
            packi16(buf, len);
            buf += 2;
            memcpy(buf, s, len);
            buf += len;
            break;
        }
    }

    va_end(ap);

    return size;
}

/*
** unpack() -- unpack data dictated by the format string into the buffer
**
**   bits |signed   unsigned   float   string
**   -----+----------------------------------
**      8 |   c        C         
**     16 |   h        H         f
**     32 |   l        L         d
**     64 |   q        Q         g
**      - |                               s
**
**  (string is extracted based on its stored length, but 's' can be
**  prepended with a max length)
*/
void unpack(unsigned char *buf, char *format, ...)
{
    va_list ap;

    signed char *c;              // 8-bit
    unsigned char *C;

    int *h;                      // 16-bit
    unsigned int *H;

    long int *l;                 // 32-bit
    unsigned long int *L;

    long long int *q;            // 64-bit
    unsigned long long int *Q;

    float *f;                    // floats
    double *d;
    long double *g;
    unsigned long long int fhold;

    char *s;
    unsigned int len, maxstrlen=0, count;

    va_start(ap, format);

    for(; *format != '\0'; format++) {
        switch(*format) {
        case 'c': // 8-bit
            c = va_arg(ap, signed char*);
            if (*buf <= 0x7f) { *c = *buf;} // re-sign
            else { *c = -1 - (unsigned char)(0xffu - *buf); }
            buf++;
            break;

        case 'C': // 8-bit unsigned
            C = va_arg(ap, unsigned char*);
            *C = *buf++;
            break;

        case 'h': // 16-bit
            h = va_arg(ap, int*);
            *h = unpacki16(buf);
            buf += 2;
            break;

        case 'H': // 16-bit unsigned
            H = va_arg(ap, unsigned int*);
            *H = unpacku16(buf);
            buf += 2;
            break;

        case 'l': // 32-bit
            l = va_arg(ap, long int*);
            *l = unpacki32(buf);
            buf += 4;
            break;

        case 'L': // 32-bit unsigned
            L = va_arg(ap, unsigned long int*);
            *L = unpacku32(buf);
            buf += 4;
            break;

        case 'q': // 64-bit
            q = va_arg(ap, long long int*);
            *q = unpacki64(buf);
            buf += 8;
            break;

        case 'Q': // 64-bit unsigned
            Q = va_arg(ap, unsigned long long int*);
            *Q = unpacku64(buf);
            buf += 8;
            break;

        case 'f': // float
            f = va_arg(ap, float*);
            fhold = unpacku16(buf);
            *f = unpack754_16(fhold);
            buf += 2;
            break;

        case 'd': // float-32
            d = va_arg(ap, double*);
            fhold = unpacku32(buf);
            *d = unpack754_32(fhold);
            buf += 4;
            break;

        case 'g': // float-64
            g = va_arg(ap, long double*);
            fhold = unpacku64(buf);
            *g = unpack754_64(fhold);
            buf += 8;
            break;

        case 's': // string
            s = va_arg(ap, char*);
            len = unpacku16(buf);
            buf += 2;
            if (maxstrlen > 0 && len > maxstrlen) count = maxstrlen - 1;
            else count = len;
            memcpy(s, buf, count);
            s[count] = '\0';
            buf += len;
            break;

        default:
            if (isdigit(*format)) { // track max str len
                maxstrlen = maxstrlen * 10 + (*format-'0');
            }
        }

        if (!isdigit(*format)) maxstrlen = 0;
    }

    va_end(ap);
}

Ir čia yra demonstracinės veiklos programą, aukščiau kodą, kad pakuočių kai duomenis į buf ir tada unpacks jį į kintamuosius. Atkreipkite dėmesį, kad kai skambina unpack() su styginių argumentas (forma specifikatorius “s”), ji protinga didžiausią ilgis skaičius priešais ją, siekiant užkirsti kelią rezervo perviršį, pvz., “96s”. Būti atsargūs, kai išpakavimo duomenų gausite per tinklą—kenkėjiškas vartotojas gali siųsti blogai pastatyti pakelių pastangas atakuoti savo sistemą!

#include <stdio.h>

// various bits for floating point types--
// varies for different architectures
typedef float float32_t;
typedef double float64_t;

int main(void)
{
    unsigned char buf[1024];
    int8_t magic;
    int16_t monkeycount;
    int32_t altitude;
    float32_t absurdityfactor;
    char *s = "Great unmitigated Zot!  You've found the Runestaff!";
    char s2[96];
    int16_t packetsize, ps2;

    packetsize = pack(buf, "chhlsf", (int8_t)'B', (int16_t)0, (int16_t)37, 
            (int32_t)-5, s, (float32_t)-3490.6677);
    packi16(buf+1, packetsize); // store packet size in packet for kicks

    printf("packet is %" PRId32 " bytes\n", packetsize);

    unpack(buf, "chhl96sf", &magic, &ps2, &monkeycount, &altitude, s2,
        &absurdityfactor);

    printf("'%c' %" PRId32" %" PRId16 " %" PRId32
            " \"%s\" %f\n", magic, ps2, monkeycount,
            altitude, s2, absurdityfactor);

    return 0;
}

Ar jūs roll savo kodą arba naudoti kito asmens, tai gera idėja turėti bendrą duomenų rinkinys, pakavimo kasdienybe, siekiant išlaikyti klaidas, patikrinti, o ne pakavimo kiekvieną bitų ranka kiekvieną kartą.

Kai pakavimo duomenys, kas gera formatą naudoti? Puikus klausimas. Laimei, RFC 4506, Išorės Duomenų pateikimo Standartas, jau nustatytas dvejetainis formatai krūva įvairių tipų, kaip slankiojo kablelio rūšių, integer tipų matricas, raw duomenys ir kt. Aš siūlau, atitinkantys kad jei jūs ketinate įdiegti duomenų sau. Bet jūs ne privalo. Paketinių Policija yra ne teisę už savo duris. Bent aš nemanau, kad jie yra.

Bet kuriuo atveju, kodavimas, duomenų kažkaip ar kitą, prieš siųsdami tai yra teisingas būdas, kaip daryti dalykus!

7.5. Sūnus Duomenų Inkapsuliacija
Ką tai iš tikrųjų reiškia, kad apimančių duomenų, bet kokiu atveju? Paprasčiausiu atveju, tai reiškia, kad jūs antraštės ten su arba kai identifikuojančios informacijos arba paketinių ilgis, arba abu.

Kas turi savo antraštę atrodo? Na, tai tik keletas dvejetainių duomenų, kad yra ir kokia, jūsų manymu, yra būtina užbaigti savo projektą.

Oho. Tai neaiškus.

Gerai. Pavyzdžiui, tarkime, kad turite multi-user pokalbių programa, kuri naudoja SOCK_STREAM. Kai vartotojas tipų (“sako”) kažkas, du gabaliukai informacija turi būti perduodami į serverį: tai, kas buvo pasakyta ir kas sakė jis.

Iki šiol tokia gera? “Kokia problema?” jūs klausiate.

Problema yra ta, kad laiškai gali būti įvairaus ilgio. Vienas žmogus, pavadintas “tomas” gali sakyti “Labas”, o kitas asmuo, pavadintas “Benjaminas” gali pasakyti: “Ei, vaikinai, ką yra?”

Todėl galite send() visą šią medžiagą klientams, kaip ji ateina. Jūsų siunčiamo duomenų srauto atrodo taip:

 

Ir t.t. Kaip klientas, žinoti, kada vienas pranešimas prasideda ir kitos stotelės? Galite, jei jums norėjo, kad visi pranešimai pat ilgio ir tiesiog paskambinti sendall() mes įgyvendinti, viršuje. Bet, kad atliekos pralaidumo! Mes nenorime, kad send() yra 1024 baitai tik tiek “tom” gali pasakyti “Labas”.

Taigi, mes viską duomenų mažą antraštę ir paketų struktūra. Tiek kliento ir serverio žinoti, kaip pakuoti ir išpakuoti (kartais vadinama “maršalas” ir “unmarshal”) duomenis. Neatrodo dabar, bet mes pradedame nustatyti protokolą, kuriame aprašoma, kaip klientas ir serveris bendrauja!

Šiuo atveju, tarkime, vartotojo vardas yra fiksuoto ilgio, 8 simbolių, kamšalu su ‘\0’. Ir tada tarkime, kad duomenų yra kintamo ilgio, ne daugiau kaip 128 rašmenys. Leiskite pažvelgti pavyzdys paketo struktūros, jog mes galime naudoti šią situaciją:

  1. len (1 baitas, nepasirašytas)—bendras ilgis pakelio, skaičiuojanti 8-baitas vartotojo vardas ir pokalbių duomenų.
  2. vardas, pavardė (8 baitai)—vartotojo vardas, NUL-paminkštintas, jei reikia.
  3. chatdata (n-baitų)—duomenimis, ne daugiau kaip 128 baitai. Ilgis pakelio turėtų būti skaičiuojami ilgis šio duomenų plius 8 (ilgis vardas srityje, virš).

Kodėl aš pasirinkau 8-baitas, ir 128 baitų ribos srityse? Aš ištraukti juos iš, oro, darant prielaidą, kad jie nori būti pakankamai ilgas. Gal, nors, 8 baitai yra pernelyg riboti savo poreikius, ir jūs galite turėti 30-baitas pavadinimas srityje, ar kas. Pasirinkimas yra jūsų.

Naudojant aukščiau paketinių apibrėžimas, pirmasis paketas turėtų būti tokia informacija (hex ir ASCII):

   0A     74 6F 6D 00 00 00 00 00      48 69
(length)  T  o  m    (padding)         H  i

Ir antra, yra panašūs:

   18     42 65 6E 6A 61 6D 69 6E      48 65 79 20 67 75 79 73 20 77 ...
(length)  B  e  n  j  a  m  i  n       H  e  y     g  u  y  s     w  ...

(Ilgis yra saugomi Tinklo Baitų eilės, žinoma. Šiuo atveju, tai tik vienas baitas, kad nesvarbu, bet apskritai jums norime, kad visi jūsų dvejetainis sveikieji skaičiai turi būti saugomi Tinklo Baitų Kad jūsų paketus.)

Kai siunčiate šiuos duomenis, jums turėtų būti saugi ir naudoti komandą panašus į sendall(), aukščiau, todėl žinote visus duomenys yra siunčiami, net jei tai trunka kelis skambučius, send() gauti iš visų jėgų.

Taip pat, kai jūs gaunate šiuos duomenis, jums reikia padaryti šiek tiek papildomo darbo. Norėdami būti saugūs, jūs turite manyti, kad jūs galite gauti dalinį paketų (pvz., gal mes gauti “18 42 65 6E 6A” iš Benjamino, aukščiau, bet, kad visi mes į šį kvietimą recv()). Mes turime skambinti recv() vėl ir vėl, kol paketas yra visiškai gautos.

Bet kaip? Na, mes žinome, kad baitų skaičius, mes turime gauti visas paketas būti baigtas, nes šis skaičius yra daigstytas ant priekinės pakelio. Mes taip pat žinome, maksimalus paketo dydis yra 1+8+128 arba 137 baitų (nes tai, kaip mes nustatėme, paketinių.)

Ten iš tikrųjų yra pora dalykų, kuriuos galite padaryti čia. Kadangi jūs žinote, kiekvienas pakelis prasideda nuo ilgio, galite skambinti recv() tiesiog gauti paketo ilgį. Tada, kai jūs turite, kad galite skambinti į ją dar kartą, nurodant tiksliai likusias ilgis pakelio (galbūt dar kartą, kad gauti visus duomenis), kol jūs turime visą pakelį. Dėl šio metodo privalumas yra tai, kad jums reikia tik buferio pakankamai didelis, kad vienas pakelis, o trūkumas yra, kad jums reikia kreiptis recv() ne mažiau kaip du kartus, kad gauti visus duomenis.

Kitas variantas yra tiesiog paskambinti recv() ir pasakyti sumą, kurią esate pasiruošę gauti yra didžiausia baitų skaičius paketo. Tada tai, ką jūs gaunate, klijuoti jį ant nugaros, buferis, ir, galiausiai, patikrinkite, norėdami pamatyti, jei paketas yra pilnas. Žinoma, jūs galite gauti kai kurių kito pakelio, todėl jums reikia turėti kambarį.

Ką galite padaryti, tai paskelbti matrica pakankamai didelė, kad du pakelius. Tai yra jūsų darbas, masyvas, kur jums bus rekonstruoti paketus, kaip jie atvyksta.

Kiekvieną kartą, kai jūs recv() duomenis, jūs galėsite pridėti jį į rezervą ir patikrinkite, norėdami pamatyti, jei paketas yra pilnas. Tai yra, baitų skaičių buferio dydis yra lygus arba didesnis už ilgį nurodyta antraštėje (+1, nes ilgis antraštės neturi būti baitų ilgio sau). Jei baitų skaičių rezervas yra mažesnis nei 1, paketinių nėra visiškai aišku. Jūs turite padaryti ypatingas atvejis, nors, nes pirmasis baitas yra šiukšlių, ir jūs negalite pasikliauti ją ištaisyti paketo ilgį.

Kai paketas yra pilnas, galite su juo daryti, kas jums bus. Jį, naudoti ir pašalinti jį iš savo darbo rezervą.

Oho! Jūs žongliravimas, kad jūsų galva dar? Na, štai antrą vienas-du punch: galite skaityti anksčiau pabaigoje vienas pakelis ir į kitą, į vieną recv() skambučio. Tai reiškia, kad jūs turite darbo buferis su vienu pilnu paketu, ir nebaigtas dalis šalia paketinių! Kruvinas gi. (Bet tai yra, kodėl jums padarė savo darbą, buferis, pakankamai didelis, kad turėti du pakelius—tuo atveju, jei tai įvyko!)

Nes jūs žinote, ilgis pirmasis paketas iš antraštės, ir jums buvo nuolat stebėti baitų skaičių darbą, buferis, jums gali atimti ir apskaičiuoti, kiek baitų darbo buferis priklauso sekundę (nebaigta) paketinių. Kai jūs tvarkomi pirmasis, galite ištrinti jį iš darbo buferis ir perkelti dalies antra paketinių žemyn į priekinį buferį, kad taip viskas pasirengęs pereiti į kitą recv().

(Kai kurie iš jūsų skaitytojų dėmesį, kad iš tikrųjų juda dalies antra paketinio darbo pradžios rezervo užima laiko, ir programos gali būti koduojamos nereikalauja, tai sukamaisiais rezervo. Deja, jums poilsio, diskusija dėl apykaitinės buferiai peržengia šio straipsnio. Jei jūs vis dar įdomu, patraukti duomenų struktūras knygos ir eiti iš ten.)

Aš niekada sakė, kad tai buvo lengva. Gerai, aš pasakyti, kad tai buvo lengva. Ir tai yra; jums tiesiog reikia praktikos ir gana greitai ji ateis jums natūraliai. Pagal Excalibur aš prisiekiu!

7.6. Transliacijos Paketus—Hello, World!
Iki šiol, šis vadovas kalbėjo apie duomenų siuntimo iš vienos priimančiosios į vieną kitą šeimininką. Bet tai yra įmanoma, aš reikalauju, kad jūs galite, tinkamai institucija, siunčia duomenis į kelis šeimininkai, tuo pačiu metu!

Su UDP (tik UDP, ne TCP) ir IPv4 standarto, tai daroma per mechanizmas vadinamas transliavimo. Su IPv6, transliavimo nepalaikomi, ir jūs turite kreiptis į dažnai pranoksta technika multicasting, kurios, deja, aš ne aptarti šiuo metu. Bet pakankamai starry-eyed ateities—mes įstrigo 32-bitų metu.

Bet palaukite! Jūs negalite tiesiog paleisti ir pradėti transliuoti willy-nilly; Jūs turite nustatyti lizdo funkciją SO_BROADCAST prieš galite siųsti transliacijos paketo tinkle. Tai tarsi vienas iš tų mažai plastikiniai dangteliai jie įdėti per raketų paleidimo jungiklį! Tai tiesiog, kiek energijos jūs laikykite savo rankas!

Bet rimtai, nors, yra pavojus, kad naudojant transliacijos paketus, ir tai yra: kiekviena sistema, kad gauna transliacijos paketo turi anuliuoti visus svogūnai-odos sluoksnius duomenų inkapsuliacija, kol jis suranda kas uostų duomenys yra lemta. Ir tada ji perduoda duomenis per arba į jūrą. Bet kuriuo atveju, tai daug darbo, kiekviena mašina, kurią gauna transliacijos paketo, ir kadangi visa tai iš jų dėl vietinio tinklo, kad galėtų būti daug mašinų, daro daug nereikalingo darbo. Kai žaidimas Doom pirmą kartą išėjo, tai buvo skundas apie savo tinklo kodą.

Dabar, kai yra daugiau nei vienas būdas odos katė… palaukit. Ten tikrai daugiau nei vienas būdas odos katę? Kokios išraiškos yra tai, kad? Uh, ir be to, yra daugiau nei vienas būdas siųsti transliacijos paketo. Taigi, gauti mėsos ir bulvių visos dalykas: kaip jūs nurodyti tikslo adresą transliacijos pranešimą? Yra dvi bendros būdais:

  1. Siųsti duomenis į specialų potinklio s transliacijos adresas. Tai yra antrinis tinklas numeris vienas-bitų nustatyti priimančiosios dalis adresą. Pavyzdžiui, namuose, mano tinklas yra 192.168.1.0, mano netmask yra 255.255.255.0, todėl paskutinio baito adresas yra mano priimančiosios numeris (nes pirmas tris baitus pagal prie tinklo, tinklo, numeris). Taigi, mano transliavimo (broadcast) adresas 192.168.1.255. Pagal Unix, ifconfig komanda bus iš tikrųjų duoti jums visus šiuos duomenis. (Jei įdomu, bitwise logika gauti jūsų transliacijos adresu yra network_number OR (NE netmask).) Galite siųsti šio tipo transliacijos paketo nuotolinio tinklus, taip pat jūsų vietinio tinklo, tačiau galite paleisti rizikos pakelio buvo numestas paskirties interneto maršrutizatorių. (Jei jie nebuvo lašas ji, tada kai atsitiktinai autobusiukas galėtų pradėti potvynių savo LAN su transliacijos srauto.)
  2. Siųsti duomenis į “global” transliacijos adresas. Tai 255.255.255.255, taip pat žinomas kaip INADDR_BROADCAST. Daugelis mašinų automatiškai bitwise O tai su savo tinklo numeris konvertuoti į tinklo transliacijos adresą, bet kai nebus. Jis kinta. Maršrutizatoriai neperduoda šios rūšies transliacijos paketo išjungti jūsų vietinio tinklo, ironiška pakankamai.

Taigi, kas atsitinka, jei bandote siųsti duomenis transliacijos adresas pirmiausiai nustatymas SO_BROADCAST lizdas variantas? Gerai, tegul ugnį iki senų gerų paplepėti ir klausytojas, ir pamatyti, kas atsitiks.

$ talker 192.168.1.2 foo
sent 3 bytes to 192.168.1.2
$ talker 192.168.1.255 foo
sendto: Permission denied
$ talker 255.255.255.255 foo
sendto: Permission denied

Taip, jis nėra laimingas ne visiems…nes mes ne nustatyti SO_BROADCAST lizdo funkciją. Tai padaryti, ir dabar jūs galite sendto(), kur norite!

Tiesą sakant, tai vienintelis skirtumas tarp UDP programa, kuri gali transliacijos ir vienas, kad negaliu. Taigi imkime senas paplepėti taikymo ir įtraukti viena dalis, kuri nustato, SO_BROADCAST lizdo funkciją. Mes tai vadiname programos broadcaster.c:

/*
** broadcaster.c -- a datagram "client" like talker.c, except
**                  this one can broadcast
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define SERVERPORT 4950    // the port users will be connecting to

int main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_in their_addr; // connector's address information
    struct hostent *he;
    int numbytes;
    int broadcast = 1;
    //char broadcast = '1'; // if that doesn't work, try this

    if (argc != 3) {
        fprintf(stderr,"usage: broadcaster hostname message\n");
        exit(1);
    }

    if ((he=gethostbyname(argv[1])) == NULL) {  // get the host info
        perror("gethostbyname");
        exit(1);
    }

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    // this call is what allows broadcast packets to be sent:
    if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast,
        sizeof broadcast) == -1) {
        perror("setsockopt (SO_BROADCAST)");
        exit(1);
    }

    their_addr.sin_family = AF_INET;     // host byte order
    their_addr.sin_port = htons(SERVERPORT); // short, network byte order
    their_addr.sin_addr = *((struct in_addr *)he->h_addr);
    memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);

    if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]), 0,
             (struct sockaddr *)&their_addr, sizeof their_addr)) == -1) {
        perror("sendto");
        exit(1);
    }

    printf("sent %d bytes to %s\n", numbytes,
        inet_ntoa(their_addr.sin_addr));

    close(sockfd);

    return 0;
}

Kas skiriasi tarp šio ir “normalus” UDP kliento/serverio situaciją? Nieko! (Išskyrus klientas gali siųsti transliacijos pakelių šiuo atveju.) Kaip, pavyzdžiui, eiti į priekį ir paleisti seną UDP klausytojas programos viename lange, ir transliuotojas kitoje. Jums turėtų būti, dabar galės padaryti visus tuos siunčia, kad nepavyko, viršuje.

$ broadcaster 192.168.1.2 foo
sent 3 bytes to 192.168.1.2
$ broadcaster 192.168.1.255 foo
sent 3 bytes to 192.168.1.255
$ broadcaster 255.255.255.255 foo
sent 3 bytes to 255.255.255.255

Ir jūs turėtumėte pamatyti klausytojas reaguoja, kad jis gavo pakelių. (Jei klausytojas neatsiliepia, tai gali būti dėl to, kad jis privalo IPv6 adresas. Pabandykite pakeisti AF_UNSPEC į smulkmenas.c AF_INET priversti IPv4.)

Gerai, kad yra gana įdomi. Bet dabar ugnį iki klausytoją į kitą mašiną šalia tavęs tame pačiame tinkle, taip, kad jūs turite dvi kopijas vyksta, vienas ant kiekvienos mašinos, ir paleisti transliuotojas vėl jūsų transliacijos adresu… Ei! Tiek klausytojai gauti paketą, net jei jums tik vadinamas sendto() vieną kartą! Cool!

Jei klausytojas gauna duomenis galite siųsti tiesiogiai, bet nėra duomenų apie transliacijos adresą, gali būti, kad jūs turite jūsų ugniasienės vietos aparatas, kuris blokuoja paketus. (Taip, Pat ir Bapper, ačiū už suprasdamas, kol aš padariau, kad taip yra, kodėl mano mėginio kodą nebuvo darbo. Aš jums sakė, aš norėčiau paminėti jūs vadove, ir čia yra. Taigi nyah.)

Vėlgi, būkite atsargūs su transliacijos paketus. Kadangi kiekvienas mašina LAN bus priverstas kovoti su paketinių, ar ji recvfrom()s tai ar ne, jis gali pateikti gana apkrova visą kompiuterių tinklą. Jie tikrai turi būti naudojama taupiai ir tinkamai.

8. Bendri Klausimai

Kur aš galiu gauti tuos header files?
Jei jūs neturite juos į savo sistemą jau, tikriausiai jų nereikia. Patikrinkite, ar vadovas jūsų konkrečios platformos. Jei esate pastate Windows, jums reikia tik #include <winsock.h>.

Ką daryti, kai bind() ataskaitos “Adresas jau naudojamas”?
Jūs turite naudoti setsockopt() su SO_REUSEADDR galimybė klausytis lizdą. Check out skyriuje bind() ir skyriuje select() pavyzdys.

Kaip man gauti sąrašą, atidarykite lizdai sistemos?
Naudoti netstat. Patikrinkite, ar žmogus puslapyje, visą informaciją, bet jums turėtų gauti kai gera produkcijos tik rašyti:

$ netstat

Tik pavyko nustatyti, kuri lizdas yra susijęs su kokia programa. 🙂

Kaip galiu peržiūrėti maršruto lentelę?

Paleisti maršrutą komandą (/sbin labiausiai Linuxes) arba komandą netstat -r.

Kaip aš galiu vykdyti kliento ir serverio programų, jei turiu tik vieną kompiuterį? Nereikia man reikia tinklo parašyti tinklo programas?

Laimei jums, beveik visos mašinos įgyvendinti kilpinės jungties tinklo “prietaisas”, kad sėdi į branduolį, o apsimeta, kad tinklo kortelė. (Tai yra sąsaja išvardytos kaip “lo”, kelvados lentelėje.)

Apsimesti, kad esate prisijungęs mašina pavadino “ožka”. Paleisti kliento viename lange ir serverio į kitą. Arba paleisti serverio fone (“server &”) ir paleisti kliento tame pačiame lange. To padarinys-iš kilpinės jungties įrenginys yra tai, kad jūs galite kliento ožkų arba kliento localhost (nuo “localhost” gali apibrėžti savo /etc/hosts failą), ir jūs turite klientą kalbėti į serverį be tinklo!

Trumpai tariant, jokių pokyčių reikia, bet kodo, padaryti jį paleisti ant vieno ne-tinklinių mašina! Huzzah!

Kaip aš galiu pasakyti, jei nuotolinio pusėje nutraukė ryšį?

Galite sakyti, nes recv() grąžins 0.

Kaip man įgyvendinti “ping” įrankis? Kas yra ICMP? Kur galiu sužinoti daugiau apie raw, kištukiniai lizdai ir SOCK_RAW?

Visi jūsų raw lizdai klausimus bus atsakyta RW. Richard Steven UNIX Tinklo Programavimo knygų. Taip pat, ieškoti ping/ pakatalogį, UNIX Tinklo Programavimo kodą, galima rasti internete.

Kaip man pakeisti arba sutrumpinti timeout dėl kvietimo connect()?

Vietoj to, kad suteiktų jums, tiksliai tą patį atsakymą, kad W. Richard Stevens duos jūs, aš jums tiesiog nurodyti jums lib/connect_nonb.c UNIX Tinklo Programavimo kodą.

Jo esmė yra, kad jums padaryti lizdas aprašų su socket(), nustatykite, kad jis ne antiblokavimo, call connect(), ir jei viskas gerai connect() grąžins -1 nedelsiant ir errno bus EINPROGRESS. Tada skambinate, select() su kokia timeout norite, einančios lizdas aprašų tiek skaitymo ir rašymo rinkiniai. Jei tai nepadeda, timeout, tai reiškia, kad prisijungti() skambutis baigtas. Šiuo metu, jūs turite naudoti connect() su SO_ERROR galimybė gauti grąžą, vertė iš connect() skambučio, kuris turėtų būti lygus nuliui, jei ten buvo ne klaida.

Galiausiai, jūs tikriausiai norite nustatyti lizdą būti blokavimas, prieš vėl galite pradėti perduoti duomenis per jį.

Atkreipkite dėmesį, kad ši turi pridėtinę naudą, leidžiant savo programa daryti ką nors kita, o tai jungiantis per daug. Galite atlikti, pavyzdžiui, nustatyti skirtąjį laiką, kad nors nedidelis, kaip 500 ms, ir atnaujinti indikatorius ekrane kiekviena pertrauka, tada skambinti, select() dar kartą. Kai skambinote, select() ir tarkim, 20 kartų, jūs žinote, kad atėjo laikas atsisakyti ryšį.

Kaip ir sakiau, patikrinti Stevens šaltinio visiškai puikus pavyzdys.

Kaip man sukurti Windows?

Pirma, ištrinti ir įdiegti Windows, Linux ar BSD. };-). Ne, iš tikrųjų, tiesiog žr. skyrių dėl pastato Windows įvadas.

Kaip man sukurti Solaris/SunOS? Aš nuolat gauti linker klaidų, kai bandau sukompiliuoti!

Į linker klaidų atsitikti, nes Saulė dėžės nėra automatiškai sudaryti lizdo bibliotekos. Žr. skyrių dėl statybos Solaris/SunOS įvedimo pavyzdys, kaip tai padaryti.

Kodėl select() išlaikyti iškristi ant signalo?

Signalai yra linkę sukelti blokuojamas sistemos ragina grįžti -1 su errno nustatyta EINTR. Kai jums nustatyti signalo apdorojimo programa su sigaction(), galite nustatyti, kad vėliavos SA_RESTART, kuris turėtų iš naujo paleisti sistemą skambučių po to, kai jis buvo nutrauktas.

Žinoma, tai ne visada veikia.

Mano mėgstamiausia šio klausimo sprendimas susijęs su goto pareiškimą. Jūs žinote, tai erzina jūsų dėstytojai, be pabaigos, todėl eiti į jį!

select_restart:
if ((err = select(fdmax+1, &readfds, NULL, NULL, NULL)) == -1) {
    if (errno == EINTR) {
        // some signal just interrupted us, so restart
        goto select_restart;
    }
    // handle the real error here:
    perror("select");
}

Žinoma, jums nereikia naudoti goto šiuo atveju, galite naudoti kitas struktūras, jį kontroliuoti. Bet aš manau, kad eiti į teiginys yra iš tikrųjų siurblys.

Kaip aš galiu įgyvendinti nutrauktas dėl ryšį su recv()?

Naudoti select()! Jis leidžia jums nurodyti timeout parametras lizdas aprašai, kad jūs ieškote skaityti. Arba, jūs galite wrap visą funkcionalumą į vieną funkciją, pavyzdžiui, šis:

#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>

int recvtimeout(int s, char *buf, int len, int timeout)
{
    fd_set fds;
    int n;
    struct timeval tv;

    // set up the file descriptor set
    FD_ZERO(&fds);
    FD_SET(s, &fds);

    // set up the struct timeval for the timeout
    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    // wait until timeout or data received
    n = select(s+1, &fds, NULL, NULL, &tv);
    if (n == 0) return -2; // timeout!
    if (n == -1) return -1; // error

    // data must be here, so do a normal recv()
    return recv(s, buf, len, 0);
}
.
.
.
// Sample call to recvtimeout():
n = recvtimeout(s, buf, sizeof buf, 10); // 10 second timeout

if (n == -1) {
    // error occurred
    perror("recvtimeout");
}
else if (n == -2) {
    // timeout occurred
} else {
    // got some data in buf
}
.
.
.

Pranešimą, kad recvtimeout() grąžina -2 atveju pauzės. Kodėl gi ne return 0? Na, jei prisimenate, grįžti vertė 0 ryšį su recv() reiškia, kad nuotolinis pusėje nutraukė ryšį. Taip, kad grįžti vertė yra jau kalbėta, ir -1-tai “klaida”, todėl aš pasirinko -2, kaip mano timeout indikatorius.

Kaip man užšifruoti arba suspausti duomenis prieš išsiunčiant jį per socket?

Vienas paprastas būdas padaryti šifravimas yra naudojamas SSL (secure sockets layer), bet tai peržengia šiame vadove. (Patikrinkite OpenSSL project daugiau informacijos.)

Tačiau darant prielaidą, kad jūs norite prijungti arba naudoti savo kompresorius ar šifravimo sistema, tai tiesiog klausimas mąstymą, savo duomenis, kiek veikia per žingsnių seką tarp abiejų pusių. Kiekvienas žingsnis pakeitimus, duomenys tam tikru būdu.

  1. serverio nuskaito duomenis iš failo (ar kur)
  2. serverio užšifruoja/suspaudžia duomenis (jums pridėti šią dalis)
  3. serverio send()s užšifruoti duomenys

Dabar atvirkščiai:

  1. kliento recv()s užšifruoti duomenys
  2. kliento decrypts/išglaudina duomenų (jums pridėti šią dalis)
  3. kliento rašo duomenis į failą (ar kur)

Jei jūs ketinate suspausti ir šifruoti, tik nepamirškite suspausti pirmas. 🙂

Tik tol, kol užsakovas tinkamai anuliuoja, kas serverio nėra, duomenys bus gerai, galų gale, nesvarbu, kiek tarpinių žingsnių galite pridėti.

Taigi, viskas, ką jums reikia padaryti, kad naudoti mano kodas yra rasti vietą tarp kai duomenys yra skaityti ir duomenys yra siunčiami (naudojant send()) per tinklą, ir lazdas, kai kodas yra, kad ar šifravimo.

Kas tai yra “PF_INET” aš nuolat matome? Ar tai susiję su AF_INET?

Taip, taip. Žr. skyrių apie socket() duomenis.

Kaip aš galiu parašyti serverio, kuris priima apvalkalo (shell) komandos nuo kliento ir vykdo juos?

Paprastumo dėlei sakykime, klientui connect(), send(), ir close() ryšys (tai yra, nėra jokių vėlesnių sistema skambučius be kliento jungtis dar kartą.)

Procesas klientas taip yra šis:

  1. connect() serveris
  2. siųsti(“/sbin/ls > /tmp/client.out”)
  3. close() ryšys

Tuo tarpu, serveris apdoroja duomenis ir atlikti tai:

  1. accept() ryšys iš kliento
  2. recv(str) komandų eilutę
  3. close() ryšys
  4. sistema(str) paleisti komandą

Saugokitės! Turintys serverio vykdyti tai, ką klientas sako, yra kaip suteikti remote shell prieiga, ir žmonės gali daryti tokius dalykus, į savo sąskaitą, kai jie prijungti prie serverio. Pavyzdžiui, ankstesniame pavyzdyje, ką daryti, jei klientas siunčia “rm-rf ~”? Jis naikina viską, kas nuo jūsų sąskaitos, kad tai, ką!

Taigi, jūs gaunate protingas, ir jums apsaugoti klientą nuo bet kokių išskyrus porą komunalinių paslaugų, kad jūs žinote, yra saugūs, kaip foobar naudingumas:

if (!strncmp(str, "foobar", 6)) {
    sprintf(sysstr, "%s > /tmp/server.out", str);
    system(sysstr);
}

Tačiau jūs vis dar nesaugu, deja: ką daryti, jei klientas įveda “foobar; rm-rf ~”? Saugiausias dalykas, kurį reikia padaryti, tai parašyti šiek tiek įprastas, kad kelia pabėgti (“\”) simbolį priešais visi ne raidinių-skaitinių simbolių (įskaitant tarpus, jei reikia) argumentų už komandą.

Kaip matote, saugumas yra gana didelė problema, kai serveris pradeda vykdančioji dalykų klientas siunčia.

Aš siuntimas nužudė duomenų, bet kai aš recv(), jis gauna tik 536 baitų arba 1460 baitų metu. Tačiau, jei aš paleisti jį į savo vietos mašina, jis gauna visus duomenis tuo pačiu metu. Kas vyksta?

Jūs esate pataikyti MTU—maksimalus dydis fizinė terpė gali dirbti. Dėl vietos mašina, naudojate kilpinės jungties įrenginys, kuris gali dirbti 8K ar daugiau ne problema. Bet dėl Ethernet, kuris gali tik rankena 1500 baitų su antraštę, paspausite, kad riba. Per modemą, su 576 MTU (ir vėl antraštės), jūs nukentėjo net apatinės ribos.

Jūs turite įsitikinkite, kad visi duomenys siunčiami, visų pirma. (Žr. sendall() funkcijų įgyvendinimo detalės.) Kai esate tikri, kad, tada jums reikia kreiptis recv() cikle, kol visi jūsų duomenys yra skaityti.

Perskaitykite skyrių Sūnus Duomenų Inkapsuliacija informacijos gauti pilną paketus duomenis, naudojant kelis skambučius recv().

Aš dėl ” Windows box, ir aš neturiu fork() sistema skambinti ar bet kokią struct sigaction. Ką daryti?

Jei jie visur, jie bus būti POSIX bibliotekų, kurios gali būti siunčiami su savo sudarytojas. Kadangi aš neturiu Windows box, aš tikrai negaliu pasakyti jums atsakyti, bet man atrodo, kad prisiminti, kad “Microsoft” turi POSIX suderinamumo sluoksnis ir tai, kur fork() būtų. (Ir gal net sigaction.)

Ieškoti pagalbos, kad atėjo su VC++ “šakutės” arba “POSIX” ir pamatyti, jei ji suteikia jums jokių užuominų.

Jei tai nepadeda, griovys fork()/sigaction stuff ir pakeiskite jį Win32 ekvivalentas: CreateProcess(). Aš nežinau, kaip naudoti CreateProcess()—ji mano bazillion argumentų, tačiau ji turėtų būti įtraukti į dokumentų, kad atėjo su VC++.

Aš esu už ugniasienės—kaip man pasakyti žmonėms, už užkardos sužinoti savo IP adresą, todėl jie gali prisijungti prie mano mašinos?

Deja, paskirties užkardos yra tam, kad žmonės už užkardos prijungti prie mašinos viduje užkardą, tad jiems tai padaryti yra iš esmės laikomas saugumo pažeidimą.

Tai nereiškia, kad viskas prarasta. Vienas dalykas, jūs galite vis dar dažnai connect(), per firewall, jei tai daro kažkoks masquerade ar NAT ar kažką panašaus, kad. Tiesiog kurti savo programas, taip, kad jūs visada vienas inicijuoti ryšį, ir jums bus gerai.

Jei tai netenkina, galite kreiptis į savo sysadmins kišti skylę užkardą taip, kad žmonės gali prisijungti prie jūsų. Užkarda gali priekį, kad jums arba per tai NAT programinę įrangą, arba per proxy arba kažką panašaus, kad.

Reikia žinoti, kad skylę užkarda yra nieko, turi būti priimtas lengvai. Jūs turite įsitikinkite, kad jums nereikia duoti blogų žmonių prieigos prie vidaus tinklo; jei jūs esate pradedantysis, tai daug sunkiau padaryti programinę įrangą saugūs, nei galima įsivaizduoti.

Nekurk savo sysadminas proto ne man. 😉

Kaip man parašyti packet sniffer? Kaip aš įdėti savo Ethernet sąsaja į promiscuous mode?

Tų, kurie neturi žinoti, kai tinklo kortelė yra “promiscuous mode”, ji perduoda VISUS paketus operacinė sistema, ne tik tuos, kurie buvo skirti šio ypač mašina. (Mes kalbame Ethernet-sluoksnis adresai čia, o ne IP adresai–bet kadangi ethernet yra žemesniojo lygio nei IP, visus IP adresus būtų veiksmingai perduodami taip pat. Žr. skirsnį Žemo Lygio Nesąmonė ir Tinklų Teorija daugiau info.)

Tai yra pagrindas, kaip packet sniffer darbai. Tai kelia sąsaja į promiscuous mode, tada OS gauna kiekvieną paketinių, kad eina ant vielos. Jūs turite lizdo tam tikros rūšies, kad jūs galite perskaityti šią duomenis.

Deja, atsakymas į klausimą priklauso nuo to, ant platformos, bet, jei esate Google, pavyzdžiui, “windows promiscuous ioctl” jūs tikriausiai gauti kažkur. Ten kas atrodo padorus writeup, Linux Journal, taip pat.

Kaip galiu nustatyti pasirinktinį reik už TCP arba UDP lizdas?
Tai priklauso nuo jūsų sistemos. Jūs galite ieškoti grynosios SO_RCVTIMEO ir SO_SNDTIMEO (naudoti su setsockopt()), kad pamatytumėte, jei jūsų sistema palaiko tokią funkciją.

Linux vyras puslapis siūlo naudoti alarm() arba setitimer() pakeisti.

Kaip aš galiu pasakyti, kurie uostus galima naudoti? Čia yra sąrašas “oficialus” prievado numerius?

Dažniausiai tai nėra problema. Jei rašote, sako, žiniatinklio serveris, tada tai yra gera idėja naudoti gerai žinomų 80 prievadas savo programinę įrangą. Jei esate rašyti tik savo specializuotos serveris, tada pasirinkite uostą, atsitiktinai (bet didesnę nei 1023) ir suteikti jai pabandyti.

Jei uostas jau yra naudojama, jūs gausite “Adresas jau naudojamas” klaida bandant bind(). Pasirinkite kitą uostą. (Tai gera idėja, kad vartotojas savo programinę įrangą, nurodyti alternatyvų uosto arba su config failą arba komandinės eilutės jungiklis.)

Ten yra sąrašas oficialiai prievado numerius tvarko Interneto priskirtų Numerių Institucija (IANA). Tiesiog todėl, kad kažkas (per 1023) yra tame sąraše, nereiškia, kad jūs negalite naudoti uosto. Pavyzdžiui, Id Software DOOM naudoja tą patį uostą, kaip “mdqs”, nesvarbu, kad yra. Visi, kad klausimai yra, kad niekas kitas ta pačia mašina naudoja tą uostą, kai norite jį naudoti.

9. Vyras Puslapius

Unix pasaulyje, yra daug vadovus. Jie turi mažai skirsniuose apibūdinti atskirų funkcijų, kad jūs turite savo žinioje.

Žinoma, vadovas būtų per daug dalykas, kad tipo. Aš turiu galvoje, ne vienas Unix pasaulyje, įskaitant ir save, mėgsta tipo, kad daug. Iš tiesų aš gali eiti ir į labai išsamiai apie tai, kiek aš norėčiau būti terse, bet vietoj to, aš turi būti trumpi ir ne pagimdė jums ilgai kalbantis diatribes apie tai, kaip visiškai nuostabiai trumpa, aš norėčiau būti, bet kokiomis aplinkybėmis į jų visumą.

[Plojimai]

Ačiū. Ką aš gaunu ne tai, kad šie puslapiai yra vadinamas “žmogus puslapių” Unix pasaulyje, ir aš turiu įtraukti savo asmeninių sutrumpintas variantas čia savo skaitymo malonumą. Dalykas yra, kad daugelis šių funkcijų, turi žymiai daugiau bendro tikslo, nei aš nuomos, bet aš tik ketina pateikti dalys, kurios yra svarbios Interneto Lizdai Programavimo.

Bet palaukite! Kad ne visi, kad negerai su mano vyras puslapiai:

  • Jie yra neišsamūs ir parodyti tik pagrindai nuo vadovo.
  • Yra daug daugiau žmogus tinklalapius, kaip šis realiame pasaulyje.
  • Jie yra kitokie, nei tie, kurie ant savo sistemos.
  • Antraštiniai failai gali būti skirtingų tam tikrų funkcijų į savo sistemą.
  • Funkcijos parametrai gali būti skirtingų tam tikrų funkcijų į savo sistemą.

Jei norite, kad realios informacijos, patikrinkite savo vietos Unix vyras puslapių įvesdami žmogus, bet, kai “bet” yra kažkas, kad jūs esate labai domina, pavyzdžiui, “sutinku”. (Aš tikiu, kad “Microsoft Visual Studio turi kažką panašaus į jų pagalbos skyriuje. Bet “vyras” yra geriau, nes jis yra vienas baitas tikslesniais nei “pagalba”. Unix vėl laimi!)

Taigi, jei šie yra tokia ydinga, kodėl netgi įtraukti juos visus Vadove? Na, yra keletas priežasčių, bet geriausia yra tai, kad a) šios versijos yra skirtos konkrečiai link tinklo programavimo ir yra lengviau virškinamas nei realus tie, ir (b) šių versijų yra pavyzdžių!

Oi! O kalbant apie pavyzdžius, nemanau, linkę įdėti į visų klaidų tikrinimas, nes ji tikrai padidina ilgio kodas. Bet jums turėtų visiškai ar klaidų tikrinimas gana daug, bet kada galite padaryti bet sistema skambina nebent esate visiškai 100% tikras, kad jis nesiruošia nepavyks, ir jūs tikriausiai turėtų tai daryti net tada!

9.1. accept()
Priimti į priimamą ryšį klausytis socket

Prototipai

#include <sys/types.h>
#include <sys/socket.h>

int accept(int s, struct sockaddr *addr, socklen_t *addrlen);

Aprašymas

Kai jūs ‘ ve gone per problemų gauti SOCK_STREAM lizdą ir nustatyti jį už priimamus ryšius su listen(), tada galite skambinti accept(), kad iš tikrųjų gauti sau naują lizdą aprašų naudoti vėliau bendravimas su naujai prijungtas klientas.

Senas lizdas, kad jūs naudojate klausytis, vis dar egzistuoja, ir bus naudojamas ir toliau accept() ragina, kaip jie ateina.

s Į listen() lizdas aprašų.
addr Tai yra užpildyti adresas svetainę, kuri jungiasi prie jūsų.
addrlen Tai yra užpildytas su sizeof() struktūra grįžo į addr parametras. Galite nekreipti dėmesio į tai, jei jūs prisiimate jūs gaunate struct sockaddr_in atgal, kuriame jūs žinote, kad esate, nes tai, tipo, jūs išlaikėte už addr.

accept() paprastai blokas, ir jūs galite naudoti select() peek klausytis lizdas aprašų į priekį laiko, kad pamatytumėte, jei tai “pasiruošę skaityti”. Jei taip, tada ten naują ryšį, laukia, kol bus accept()! Yay! Taip pat jūs galite nustatyti O_NONBLOCK vėliavos klausytis lizdą naudojant fcntl(), ir tada ji niekada blokas, pasirinkti, o ne grįžti -1 su errno nustatyta EWOULDBLOCK.

Lizdą aprašų grąžinti accept() yra bona fide lizdas aprašų, atidaryti ir prijungtas prie nuotolinio kompiuterio. Jūs turite close() jį, kai baigsite su juo.

Grįžti Vertė

accept() grąžina naujai prijungtas lizdas aprašų, arba -1 klaidų, su errno nustatytas tinkamai.

Pavyzdžiui

struct sockaddr_storage their_addr;
socklen_t addr_size;
struct addrinfo hints, *res;
int sockfd, new_fd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

getaddrinfo(NULL, MYPORT, &hints, &res);

// make a socket, bind it, and listen on it:

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
bind(sockfd, res->ai_addr, res->ai_addrlen);
listen(sockfd, BACKLOG);

// now accept an incoming connection:

addr_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);

// ready to communicate on socket descriptor new_fd!

Taip Pat Žr.
socket(), getaddrinfo(), listen(), struct sockaddr_in


9.2. bind()
Susieti kištukinis lizdas su IP adresą ir prievado numerį

Prototipai

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);

Aprašymas

Kai nuotolinio įrenginio nori prisijungti prie savo serverio programa, ji turi du gabalus informacija: IP adresą ir prievado numerį. Kvietimas bind() leidžia jums padaryti tik tai.

Pirma, jums skambinti getaddrinfo(), apkrova iki struct sockaddr su paskirties adresą ir prievado informaciją. Tada galite skambinti socket(), siekiant gauti lizdas aprašų, ir tada jums pereiti į lizdą ir adresą į bind(), ir IP adresą ir prievado stebuklingai (naudojant faktinius magija) jungiasi į lizdą!

Jei nežinai savo IP adresą, ar jūs žinote, jūs turite tik vieną IP adresą ant mašinos, ar jums nereikia rūpintis, kuris iš mašinos IP adresai yra naudojamas, galite tiesiog pereiti AI_PASSIVE vėliava patarimų parametras getaddrinfo(). Kas tai yra užpildykite IP adreso dalis struct sockaddr ypatingą reikšmę, kurią pasakoja bind(), kad jis turėtų automatiškai užpildyti šią šeimininko IP adresas.

Ką ką? Kas ypatingą vertę yra pakrautas į struct sockaddr jo IP adresą gali automatiškai užpildyti adresas dabartinį šeimininką? Aš pasakysiu jums, bet atminkite, tai yra tik tada, jei jūs esate užpildę struct sockaddr ranka; jei ne, naudokite rezultatai nuo getaddrinfo(), kaip nurodyta aukščiau. IPv4, sin_addr.s_addr srityje struct sockaddr_in struktūra yra nustatyta INADDR_ANY. IPv6, sin6_addr srityje struct sockaddr_in6 struktūra yra paskirtas į pasaulinės kintamasis in6addr_any. Arba, jei esate skelbiantis naują struct in6_addr, galite paleisti tai, kad IN6ADDR_ANY_INIT.

Galiausiai, addrlen parametras turėtų būti nustatytas sizeof my_addr.

Grįžti Vertė

Grįžta nulis sėkmės, arba -1 klaidų (ir errno bus nustatyti prioritetai.)

Pavyzdžiui

// modern way of doing things with getaddrinfo()

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

getaddrinfo(NULL, "3490", &hints, &res);

// make a socket:
// (you should actually walk the "res" linked list and error-check!)

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// bind it to the port we passed in to getaddrinfo():

bind(sockfd, res->ai_addr, res->ai_addrlen);
// example of packing a struct by hand, IPv4

struct sockaddr_in myaddr;
int s;

myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(3490);

// you can specify an IP address:
inet_pton(AF_INET, "63.161.169.137", &(myaddr.sin_addr));

// or you can let it automatically select one:
myaddr.sin_addr.s_addr = INADDR_ANY;

s = socket(PF_INET, SOCK_STREAM, 0);
bind(s, (struct sockaddr*)&myaddr, sizeof myaddr);

Taip Pat Žr.
getaddrinfo(), socket(), struct sockaddr_in, struct in_addr


9.3. connect()
Prisijungti lizdą prie serverio

Prototipai

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *serv_addr,
            socklen_t addrlen);

Aprašymas

Sukūrę lizdas aprašų su socket() ryšį, galite connect(), kad lizdas nuotolinio serverio, naudojant gerai, pavadintas prisijungti() sistema skambina. Viskas, ką jums reikia padaryti, tai išlaikyti jį lizdą aprašų ir adresą serverio jus domina gauti žinoti geriau. (Oh, ir ilgis adresą, kuris yra paprastai perduodama funkcijas, kaip šis.)

Paprastai ši informacija ateina kartu kaip rezultatas, skambutis getaddrinfo(), bet jūs galite užpildyti savo struct sockaddr, jei norite.

Jeigu dar nesate vadinamas bind() socket aprašų, ji automatiškai jungiasi prie jūsų IP adresą ir atsitiktinių uosto vietos. Paprastai tai yra tiesiog gerai su jumis, jei jūs ne serverį, nes jūs tikrai nerūpi, koks jūsų vietinis prievadas yra; jums tik rūpi, ką nuotolinio uosto yra, todėl jūs galite įdėti jį į serv_addr parametras. Galite skambinti bind(), jei jūs tikrai norite, kad jūsų klientas, kištukinis lizdas turi būti apie konkretų IP adresą ir prievado, tačiau tai yra gana retas.

Kai lizdas yra connect(), jūs nemokamai send() ir recv() duomenų apie tai, kad jūsų širdis geidžia.

Speciali pastaba: jei jūs connect() SOCK_DGRAM UDP lizdas prie nutolusio kompiuterio, galite naudoti send() ir recv(), taip pat sendto() ir recvfrom(). Jei norite.

Grįžti Vertė

Grįžta nulis sėkmės, arba -1 klaidų (ir errno bus nustatyti prioritetai.)

Pavyzdžiui

// connect to www.example.com port 80 (http)

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;

// we could put "80" instead on "http" on the next line:
getaddrinfo("www.example.com", "http", &hints, &res);

// make a socket:

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// connect it to the address and port we passed in to getaddrinfo():

connect(sockfd, res->ai_addr, res->ai_addrlen);

Taip Pat Žr.
socket(), bind()


9.4. uždaryti()
Uždaryti lizdas aprašų

Prototipai

#include <unistd.h>

int close(int s);

Aprašymas

Po to, kai jūs baigėte, naudodami lizdas koks išprotėjęs schema, jūs turite pramanytas ir nenorite send() arba recv() arba, iš tiesų, nieko daryti, dar ne visi su lizdu, galite close(), ir ji bus atlaisvinta, niekada būti naudojamas dar kartą.

Nuotolinio pusėje gali pasakyti, jei tai atsitiks vienas iš dviejų būdų. Vienas: jei nuotolinio pusėje ragina recv(), jis grąžina 0. Dviejų: jei nuotolinio pusėje skambučius, send(), ji bus gauti signalą SIGPIPE ir send() grąžins -1 ir errno bus EPIPE.

“Windows” vartotojams: funkcija, jums reikia naudoti taip vadinamas closesocket(), close(). Jei bandote naudoti close() socket aprašų, tai įmanoma Windows bus gauti piktas… Ir jums nebūtų patinka, kai jis piktas.

Grįžti Vertė

Grįžta nulis sėkmės, arba -1 klaidų (ir errno bus nustatyti prioritetai.)

Pavyzdžiui

s = socket(PF_INET, SOCK_DGRAM, 0);
.
.
.
// a whole lotta stuff...*BRRRONNNN!*
.
.
.
close(s);  // not much to it, really.

Taip Pat Žr.

socket(), shutdown()


9.5. getaddrinfo(), freeaddrinfo(), gai_strerror()

Gauti informaciją apie priimančiosios vardas ir/arba paslaugų ir apkrova iki struct sockaddr rezultatu.

Prototipai

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *nodename, const char *servname,
                const struct addrinfo *hints, struct addrinfo **res);

void freeaddrinfo(struct addrinfo *ai);

const char *gai_strerror(int ecode);

struct addrinfo {
  int     ai_flags;          // AI_PASSIVE, AI_CANONNAME, ...
  int     ai_family;         // AF_xxx
  int     ai_socktype;       // SOCK_xxx
  int     ai_protocol;       // 0 (auto) or IPPROTO_TCP, IPPROTO_UDP 

  socklen_t  ai_addrlen;     // length of ai_addr
  char   *ai_canonname;      // canonical name for nodename
  struct sockaddr  *ai_addr; // binary address
  struct addrinfo  *ai_next; // next structure in linked list
};

Aprašymas

getaddrinfo() yra puiki funkcija, kad bus grąžinti informacija apie konkretų pavadinimą (pvz., IP adresas) ir apkrova iki struct sockaddr jums, rūpintis smėlinis duomenis (pavyzdžiui, jei tai IPv4 arba IPv6.) Jis pakeičia senas funkcijas gethostbyname() ir getservbyname().Aprašymas, žemiau, rasite daug informacijos, kad gali būti šiek tiek varginantis procesas, tačiau faktinis naudojimas yra gana paprasta. Ji gali būti verta patikrinti pavyzdžiai pirmas.

Pavadinimą kuris jus domina eina nodename parametras. Adresas gali būti arba pavadinimą, pvz., “www.example.com” arba IPv4, arba IPv6 adresą (praėjo kaip string). Šis parametras taip pat gali būti NULL, jei naudojate AI_PASSIVE vėliava (žr. toliau.)

Į servname parametras yra iš esmės prievado numerį. Tai gali būti prievado numerį (praėjo kaip eilutę, pvz., “80”), arba tai gali būti paslaugos pavadinimas, kaip “http” ir “tftp” arba “smtp” arba “pop” ir kt. Gerai žinoma paslaugų pavadinimai gali būti rasti IANA Uosto Sąrašą arba į savo /etc/paslaugos failą.

Galiausiai, įvesties parametrų, mes turime patarimų. Tai tikrai, kur jums nustatyti, ką getaddrinfo() funkcija ketina daryti. Nulis visos struktūros prieš naudodami su memset(). Leiskite pažvelgti į laukus, jums reikia nustatyti prieš naudojimą.

Į ai_flags galima nustatyti įvairių dalykų, bet čia yra pora svarbių klausimų. (Kelias vėliavas, gali būti nurodyta bitwise-sandarinimo žiedas juos kartu su | operatorių.) Patikrinkite, ar jūsų vyras puslapyje visą sąrašą vėliavas.

AI_CANONNAME sukelia ai_canonname to, kad užpildyti su šeimininko canonical (iš tikrųjų) vardu. AI_PASSIVE sukelia rezultatas IP adresą, užpildyti su INADDR_ANY (IPv4)arba in6addr_any (IPv6); tai sukelia kitą kvietimą į bind() auto-užpildyti IP adresas struct sockaddr adresas dabartinio šeimininko. Tai puikiai tinka sukurti serverį, kai nenorite, kad jame “užkoduoja” adresą.

Jei jums nereikia naudoti AI_PASSIVE, vėliavos, tada jūs galite pereiti NULL į nodename (nuo bind() bus užpildyti ją jums vėliau).

Toliau dėl su įėjimo paramters, jūs tikriausiai norite nustatyti ai_family į AF_UNSPEC kuris pasakoja getaddrinfo() ieškoti IPv4 ir IPv6 adresus. Taip pat galite apriboti save į vieną ar kitą su AF_INET ar AF_INET6.

Be to, socktype srityje turėtų būti nustatytas SOCK_STREAM ar SOCK_DGRAM, priklausomai nuo to, kokio tipo lizdas norite.

Galiausiai, tiesiog palikite ai_protocol 0 automatiškai pasirinkti jūsų protokolo tipą.

Dabar, kai galite gauti visą, kad stuff ten, jūs galite pagaliau paskambinkite getaddrinfo()!

Žinoma, tai yra, kur prasideda linksmybės. Res dabar rodo, kad susiję sąrašą struct addrinfos, ir jūs galite eiti per šį sąrašą gauti visus adresus, kurie atitinka tai, ką jūs išlaikė su užuominomis.

Dabar, tai galima gauti kai kuriuos adresus, kad neveikia dėl vienos ar kitos priežasties, todėl tai, ką Linux vyras puslapį daro, yra kilpos, per sąrašą, daro paskambinti į socket() ir connect() (arba bind(), jei jūs nustatote serverio su AI_PASSIVE vėliava), kol tai pavyks.

Galiausiai, kai baigsite su susijusį sąrašą, jums reikia kreiptis freeaddrinfo(), kad atlaisvintumėte atminties (ar jis bus nutekėjo, ir Kai kurie Žmonės nusivilia.)

Grįžti Vertė

Grįžta nulis sėkmės, arba nulio klaidų. Jeigu ji grąžina nulio, galite naudoti funkciją gai_strerror(), siekiant gauti versija spausdinimui klaidos kodas grįžti vertę.

Pavyzdžiui

// code for a client connecting to a server
// namely a stream socket to www.example.com on port 80 (http)
// either IPv4 or IPv6

int sockfd;  
struct addrinfo hints, *servinfo, *p;
int rv;

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
hints.ai_socktype = SOCK_STREAM;

if ((rv = getaddrinfo("www.example.com", "http", &hints, &servinfo)) != 0) {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
    exit(1);
}

// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
    if ((sockfd = socket(p->ai_family, p->ai_socktype,
            p->ai_protocol)) == -1) {
        perror("socket");
        continue;
    }

    if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
        perror("connect");
        close(sockfd);
        continue;
    }

    break; // if we get here, we must have connected successfully
}

if (p == NULL) {
    // looped off the end of the list with no connection
    fprintf(stderr, "failed to connect\n");
    exit(2);
}

freeaddrinfo(servinfo); // all done with this structure
// code for a server waiting for connections
// namely a stream socket on port 3490, on this host's IP
// either IPv4 or IPv6.

int sockfd;  
struct addrinfo hints, *servinfo, *p;
int rv;

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP address

if ((rv = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
    exit(1);
}

// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
    if ((sockfd = socket(p->ai_family, p->ai_socktype,
            p->ai_protocol)) == -1) {
        perror("socket");
        continue;
    }

    if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
        close(sockfd);
        perror("bind");
        continue;
    }

    break; // if we get here, we must have connected successfully
}

if (p == NULL) {
    // looped off the end of the list with no successful bind
    fprintf(stderr, "failed to bind socket\n");
    exit(2);
}

freeaddrinfo(servinfo); // all done with this structure

Taip Pat Žr.
gethostbyname(), getnameinfo()


9.6. gethostname()
Grąžina sistemos pavadinimas

Prototipai

#include <sys/unistd.h>

int gethostname(char *name, size_t len);

Aprašymas

Jūsų sistema turi pavadinimą. Jie visi daryti. Tai yra šiek tiek daugiau Unixy dalykas, nei kitų networky stuff, mes jau kalbame apie tai, bet jis vis dar turi savo reikmėms.

Pavyzdžiui, jūs galite gauti savo kompiuterio vardą, tada skambinti gethostbyname(), norėdami sužinoti savo IP adresą.

Parametro pavadinimas turėtų reikšti, buferis, kad bus surengti pavadinimą, ir len yra dydžio, kad rezervo baitais. gethostname() nebus perrašyti pabaigos buferis (tai gali grąžinti klaidos, arba ji paprasčiausiai gali nustoti rašyti), ir ji bus NUL-nutraukti string jeigu kambaryje tai buferis.

Grįžti Vertė
Grįžta nulis sėkmės, arba -1 klaidų (ir errno bus nustatyti prioritetai.)

Pavyzdžiui.

char hostname[128];

gethostname(hostname, sizeof hostname);
printf("My hostname: %s\n", hostname);

Taip Pat Žr.
gethostbyname()


9.7. gethostbyname(), gethostbyaddr()
Gauti IP adresą kompiuterio vardą, arba atvirkščiai

Prototipai

#include <sys/socket.h>
#include <netdb.h>

struct hostent *gethostbyname(const char *name); // DEPRECATED!
struct hostent *gethostbyaddr(const char *addr, int len, int type);

Aprašymas

ATKREIPKITE DĖMESĮ: kad šios dvi funkcijos yra pakeistas getaddrinfo() ir getnameinfo()! Visų pirma, gethostbyname() neveikia gerai su IPv6.

Šios funkcijos žemėlapis atgal ir pirmyn tarp priimančiosios pavadinimai bei IP adresai. Pavyzdžiui, jei turite “www.example.com” galite naudoti gethostbyname(), siekiant gauti jo IP adresą ir laikykite jį struct in_addr.

Ir atvirkščiai, jei turite struct in_addr arba struct in6_addr, galite naudoti gethostbyaddr(), siekiant gauti hostname atgal. gethostbyaddr() yra suderinami su IPv6, bet jums reikia naudoti naujesni shinier getnameinfo() vietoj.

(Jei turite eilutės, kurioje yra IP adresas, taškų ir skaičių formatą, kuriuo norite ieškoti kompiuterio vardą (hostname), norite geriau išjungti, naudojant getaddrinfo() su AI_CANONNAME vėliava.)

gethostbyname() seka, pavyzdžiui, “www.yahoo.com” ir grąžina struct hostent, kuriame tonų informaciją, įskaitant IP adresą. (Kita informacija yra oficialus pavadinimą, sąrašą slapyvardžiai, adresas, tipas, ilgis, adresai, o adresų sąrašą—tai bendrosios paskirties struktūra, kad tai gana paprasta naudoti mūsų konkrečiais tikslais, kai tu matai, kaip.)

gethostbyaddr() mano struct in_addr ar struct in6_addr ir atneša jums iki atitinkamą pavadinimą (jei toks yra), taigi tai tarsi priešingas gethostbyname(). Kaip ir parametrus, nors adr yra char*, jūs iš tikrųjų norite perduoti žymeklį į struct in_addr. len turėtų būti sizeof(struct in_addr), ir tipo turėtų būti AF_INET.

Taigi, kas tai yra struct hostent, kad gauna grįžo? Ji turi daug sričių, kuriose pateikiama informacija apie priimančiosios klausimas.

char *h_name Nekilnojamojo canonical pavadinimą.
char **h_aliases Sąrašas slapyvardžiai, kurie gali būti prieinama su matricos—paskutinis elementas yra NULL
int h_addrtype Rezultatas adresą tipas, kuris tikrai turėtų būti AF_INET mūsų tikslais.
int length Ilgis adresai baitų, kuris yra 4 a IP (4 versija) adresus.
char **h_addr_list Sąrašas IP adresų, tai šeimininkė. Nors tai yra char**, tai tikrai masyvo struct in_addr*s nuslėpti. Paskutinis masyvo elementas yra NULL.
h_addr Bendrai apibrėžtas alias h_addr_list[0]. Jei jūs tiesiog norite, bet senas IP adresą šiam host (taip, jie gali turėti daugiau nei vieną) tiesiog naudokite šį lauką.

Grįžti Vertė
Grąžina rodyklę į atstojamoji struct hostent sėkmės, arba NULL klaidų.

Vietoj įprasto perror() ir visi, kad kita, norite paprastai naudoti klaidos pranešimo, šios funkcijos turi lygiagrečiai rezultatai kintamasis h_errno, kuris gali būti atspausdintas, naudojant funkcijas herror() arba hstrerror(). Jie veikia tiesiog kaip klasikinis errno, perror(), ir strerror() funkcijos esate pripratę.

Pavyzdžiui

// THIS IS A DEPRECATED METHOD OF GETTING HOST NAMES
// use getaddrinfo() instead!

#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    int i;
    struct hostent *he;
    struct in_addr **addr_list;

    if (argc != 2) {
        fprintf(stderr,"usage: ghbn hostname\n");
        return 1;
    }

    if ((he = gethostbyname(argv[1])) == NULL) {  // get the host info
        herror("gethostbyname");
        return 2;
    }

    // print information about this host:
    printf("Official name is: %s\n", he->h_name);
    printf("    IP addresses: ");
    addr_list = (struct in_addr **)he->h_addr_list;
    for(i = 0; addr_list[i] != NULL; i++) {
        printf("%s ", inet_ntoa(*addr_list[i]));
    }
    printf("\n");

    return 0;
}
// THIS HAS BEEN SUPERCEDED
// use getnameinfo() instead!

struct hostent *he;
struct in_addr ipv4addr;
struct in6_addr ipv6addr;

inet_pton(AF_INET, "192.0.2.34", &ipv4addr);
he = gethostbyaddr(&ipv4addr, sizeof ipv4addr, AF_INET);
printf("Host name: %s\n", he->h_name);

inet_pton(AF_INET6, "2001:db8:63b3:1::beef", &ipv6addr);
he = gethostbyaddr(&ipv6addr, sizeof ipv6addr, AF_INET6);
printf("Host name: %s\n", he->h_name);

Taip Pat Žr.
getaddrinfo(), getnameinfo(), gethostname(), errno, perror(), strerror(), struct in_addr


9.8. getnameinfo()
Ieškoti pavadinimą ir paslaugos pavadinimas informacijos pateikta struct sockaddr.

Prototipai

#include <sys/socket.h>
#include <netdb.h>

int getnameinfo(const struct sockaddr *sa, socklen_t salen,
                char *host, size_t hostlen,
                char *serv, size_t servlen, int flags);

Aprašymas

Ši funkcija priešingai getaddrinfo(), tai reiškia, kad ši funkcija trunka jau pakrautas struct sockaddr ir daro pavadinimas ir paslaugos pavadinimas lookup. Jis pakeičia senas gethostbyaddr() ir getservbyport() funkcijas.

Jūs turite perduoti žymeklį į struct sockaddr (kuris iš tikrųjų yra turbūt struct sockaddr_in ar struct sockaddr_in6, kad jūs mesti) sa parametras, ir ilgis, kad struct į sälen.

Tą pavadinimą ir paslaugos pavadinimas bus parašyta rajone, nurodė, kad pagal kompiuterio ir serverio parametrus. Žinoma, turite nurodyti, max ilgis šias atsargas, hostlen ir servlen.

Galiausiai, yra keletas vėliavų, jūs galite praeiti, bet čia pora gerų. NI_NOFQDN sukels priimančiosios būti tik pavadinimą, o ne visą domeno vardas. NI_NAMEREQD sukels funkcija žlugti, jei pavadinimas negali būti nustatyta su DNS lookup (jei nenurodysite šios vėliavos ir pavadinimai negali būti rastas, getnameinfo() bus įdėti eilutę versiją IP adresas priimančiosios vietoj.)

Kaip visada, patikrinkite savo vietos žmogus puslapių, pilnas samtelis.

Grįžti Vertė

Grįžta nulis sėkmės, ar ne-nulis klaidų. Jei grįžti vertė yra ne nulis, jis gali būti perduotas gai_strerror(), siekiant gauti žmonėms suprantama eilutę. Matyti getaddrinfo daugiau informacijos.

Pavyzdžiui

struct sockaddr_in6 sa; // could be IPv4 if you want
char host[1024];
char service[20];

// pretend sa is full of good information about the host and port...

getnameinfo(&sa, sizeof sa, host, sizeof host, service, sizeof service, 0);

printf("   host: %s\n", host);    // e.g. "www.example.com"
printf("service: %s\n", service); // e.g. "http"

Taip Pat Žr.
getaddrinfo(), gethostbyaddr()


9.9. getpeername()
Grįžti adresas info apie nuotolinio pusėje ryšys

Prototipai

#include <sys/socket.h>

int getpeername(int s, struct sockaddr *addr, socklen_t *len);

Aprašymas

Kai jūs turite accept() nuotolinio ryšio, arba connect() serverį, dabar jūs turite tai, kas yra žinoma kaip “lygaus lygiems”. Jūsų bendraamžių-tai tiesiog kompiuterio, prie kurio esate prisijungę, galima identifikuoti pagal IP adresą ir prievadą. Taip…

getpeername() tiesiog grąžina struct sockaddr_in alsuoja informacija apie mašina, prie kurio esate prisijungę.

Kodėl jis vadinamas “vardas”? Na, yra daug skirtingų rūšių, kištukiniai lizdai, ne tik Interneto Lizdai, kaip mes naudojame šiame vadove, ir toks “vardas” buvo gražus bendrasis terminas, kuris apima visus atvejus. Mūsų atveju, nors, bendraamžių “pavadinimas” yra tai IP adresą ir prievadą.

Nors funkcija grąžina dydis atstojamoji adresas len, turite prieškrūvio len dydis, adr.

Grįžti Vertė

Grįžta nulis sėkmės, arba -1 klaidų (ir errno bus nustatyti prioritetai.)

Pavyzdžiui

// assume s is a connected socket

socklen_t len;
struct sockaddr_storage addr;
char ipstr[INET6_ADDRSTRLEN];
int port;

len = sizeof addr;
getpeername(s, (struct sockaddr*)&addr, &len);

// deal with both IPv4 and IPv6:
if (addr.ss_family == AF_INET) {
    struct sockaddr_in *s = (struct sockaddr_in *)&addr;
    port = ntohs(s->sin_port);
    inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
} else { // AF_INET6
    struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
    port = ntohs(s->sin6_port);
    inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
}

printf("Peer IP address: %s\n", ipstr);
printf("Peer port      : %d\n", port);

Taip Pat Žr.
gethostname(), gethostbyname(), gethostbyaddr()


9.10. errno
Turi klaidos kodas paskutinį sistema skambinti

Prototipai

#include <errno.h>

int errno;

Aprašymas

Tai yra kintamasis, kuris turi klaidų, informacijos daug, sistema skambina. Jei pamenate, dalykų, pavyzdžiui, socket() ir listen() -1 klaidų, ir jie nustatyti tikslią vertę errno, kad jums žinoti konkrečiai kuris įvyko klaida.

Antraštės failą errno.h sąrašus krūva nuolat simboliniai pavadinimai klaidų, pvz., EADDRINUSE, EPIPE, ECONNREFUSED ir kt. Savo vietos žinyno puslapių, pasakys jums, ką kodai gali būti grąžinamas kaip klaida, ir jūs galite naudoti šiuos ne paleisti laikas tvarkyti įvairius klaidų įvairiais būdais.

Arba, dažniau, galite skambinti perror() arba strerror(), siekiant gauti žmonėms suprantama versijos klaidos.

Vienas dalykas, reikia pažymėti, jums multiįvėrime entuziastai, yra tai, kad daugelyje sistemų errno yra apibrėžti threadsafe būdu. (Tai yra, tai nėra iš tikrųjų pasaulio kintamąjį, bet jis elgiasi kaip pasaulio kintamasis būtų iš vieno sriegiu aplinkai).

Grįžti Vertė

Kad kintamojo vertė yra naujausias klaida paaiškėjo, kurie gali būti kodo “sėkmės”, jei paskutinį veiksmą pavyko.

Pavyzdžiui

s = socket(PF_INET, SOCK_STREAM, 0);
if (s == -1) {
    perror("socket"); // or use strerror()
}

tryagain:
if (select(n, &readfds, NULL, NULL) == -1) {
    // an error has occurred!!

    // if we were only interrupted, just restart the select() call:
    if (errno == EINTR) goto tryagain;  // AAAA!  goto!!!

    // otherwise it's a more serious error:
    perror("select");
    exit(1);
}

Taip Pat Žr.
perror(), strerror()


9.11. fcntl()
Kontrolės lizdas aprašai

Prototipai

#include <sys/unistd.h>
#include <sys/fcntl.h>

int fcntl(int s, int cmd, long arg);

Aprašymas

Ši funkcija paprastai yra naudojamos siekiant padaryti failą, blokavimo ir kitų failų-orientuotų dalykų, bet ji taip pat turi keletą lizdas susijusias funkcijas, kad gali matyti, ar naudoti, laikas nuo laiko.

Parametras s yra lizdas aprašų norite veikti, cmd turėtų būti nustatytas F_SETFL, ir arg gali būti viena iš šių komandų. (Kaip ir sakiau, ten daugiau fcntl(), nei aš leisčiau čia, bet aš stengiuosi likti lizdas orientuota.)

O_NONBLOCK Nustatyti lizdas turi būti ne antiblokavimo. Žr. skyrių dėl blokavimo daugiau informacijos.
O_ASYNC Nustatyti lizdas daryti asinchroninis I/O., Kai duomenys yra pasirengusi būti recv() lizdas, signalo SIGIO bus padidintas. Tai retai galima pamatyti, ir peržengia vadovas. Ir aš manau, kad tai prieinama tik tam tikrų sistemų.

Grįžti Vertė
Grįžta nulis sėkmės, arba -1 klaidų (ir errno bus nustatyti prioritetai.)

Skirtingas fcntl() sistema skambinti iš tikrųjų yra įvairių grįžti vertės, bet aš ne kuriems juos čia, nes jie ne lizdas-susiję. Matyti savo vietos fcntl() vyras puslapyje daugiau informacijos.

Pavyzdžiui

int s = socket(PF_INET, SOCK_STREAM, 0);

fcntl(s, F_SETFL, O_NONBLOCK);  // set to non-blocking
fcntl(s, F_SETFL, O_ASYNC);     // set to asynchronous I/O

Taip Pat Žr.
Blokuoja, send()


9.12. htons(), htonl(), ntohs(), ntohl()
Konvertuoti kelių baitų sveikasis skaičius tipų, iš priimančiosios baitų, kad tinklo baitų eilės

Prototipai

#include <netinet/in.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

Aprašymas

Tik, kad tu labai nelaiminga, skirtinguose kompiuteriuose naudoja skirtingas baitas orderings viduje jų įvairaus baitų ilgio sveikųjų skaičių (t. y. bet koks sveikasis skaičius, kad didesnio nei simb.) To padarinys yra tai, kad jei jums send() dviejų baitų short int iš Intel dėžutės Mac (kol jie tapo Intel dėžutėse, o taip pat, aš turiu galvoje), ką vienas kompiuteris, mano numeris 1, kiti pagalvos, skaičius-256, ir atvirkščiai.

Būdas išspręsti šią problemą yra visiems atidėti į šalį savo nesutarimus ir susitarti, kad Motorola ir IBM turėjo teisę, ir Intel padariau tai keistai taip, ir taip, mes visi konvertuoti mūsų baitas orderings “big-endian” prieš siunčiant juos. Kadangi Intel yra mašina, tai daug labiau politiškai teisinga skambinkite mums geriausias baitų seka Tinklo baitų eilės. Taigi, šios funkcijos konvertuoti savo gimtąją baitų, kad tinklo baitų eilės ir atgal.

(Tai reiškia, Intel šios funkcijos pakeisti visus baitų aplink, ir ant PowerPC jie nieko, nes baitų jau yra Tinklo Baitų Tvarka. Bet jūs visada turėtų naudoti juos savo kodą, bet kokiu atveju, nes kas nors gali ir nori ją kurti Intel mašina ir vis dar viskas veikia tinkamai.)

Atkreipkite dėmesį, kad tipų dalyvauja 32-bitų (4 baitų, tikriausiai int) ir 16-bitų (2 baitų, labai tikėtina, trumpas) numeriai. 64-bitų mašinos gali būti htonll() 64-bitų ints, bet aš nemačiau. Jums tereikia parašyti savo.

Bet kokiu atveju, šių funkcijų darbą yra tai, kad pirmiausia nuspręsti, jei esate konvertuoti iš priimančiosios (kompiuterio) baitų eilės arba iš tinklo baitų tvarka. Jei “host”, pirmąją raidę funkcija, jūs ketinate skambinti yra “h”. Priešingu atveju tai “n”, “tinklas”. Viduryje iš pareigų pavadinimas visada yra “iki” kadangi jūs esate konvertuoti iš vieno į kitą, ir priešpaskutinė raidė rodo, kas jūs esate konvertuoti į. Paskutinis laiškas dydžio yra duomenų, “s” trumpam, arba “l” ilgai. Taigi:

htons() priimančiosios tinklo trumpas
htonl() priimančiosios tinklo ilgai
ntohs() tinklo surengti trumpą
ntohl() tinklo priimančiosios ilgai

Grįžti Vertė

Kiekviena funkcija grąžina pakeista vertė.

Pavyzdžiui

uint32_t some_long = 10;
uint16_t some_short = 20;

uint32_t network_byte_order;

// convert and send
network_byte_order = htonl(some_long);
send(s, &network_byte_order, sizeof(uint32_t), 0);

some_short == ntohs(htons(some_short)); // this expression is true

9.13. inet_ntoa(), inet_aton(), inet_addr
Konvertuoti IP adresus iš taškų ir skaičių eilutę į struct in_addr ir atgal

Prototipai

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// ALL THESE ARE DEPRECATED!  Use inet_pton()  or inet_ntop() instead!!

char *inet_ntoa(struct in_addr in);
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);

Aprašymas

Šios funkcijos yra nebenaudojamas, nes jie negali dirbti IPv6! Naudoti inet_ntop() arba inet_pton() vietoj! Jie įtraukti, nes čia jie gali dar galima rasti gamtoje.

Visų šių funkcijų konvertuoti struct in_addr (dalis jūsų struct sockaddr_in, labiausiai tikėtina) į eilutę, taškų ir skaičių formatą (pvz., “192.168.5.10”), ir atvirkščiai. Jei jūs turite IP adresą perduoti komandinės eilutės ar kažką, tai yra lengviausias būdas gauti struct in_addr, ar panašiai. Jei jums reikia daugiau galios, pabandykite kai kurios DNS funkcijas, pavyzdžiui, gethostbyname() arba bandyti perversmo d ‘ État, savo vietos šalyje.

Funkcija inet_ntoa() paverčia tinklo adresą struct in_addr į taškus-ir-numerių formatas eilutę. “n”, “ntoa” stovi tinkle, ir “a” reiškia ASCII dėl istorinių priežasčių (taip, tai “Tinklas ASCII”—”toa” priesaga turi analogiškas draugui C biblioteka, vadinama atoi(), kuri konvertuoja ASCII styginių sveikasis skaičius.)

Funkcija inet_aton() yra priešinga, konvertuoti iš taškų ir skaičių eilutę į in_addr_t (kuris yra tipo lauko s_addr jūsų struct in_addr.)

Galiausiai, funkcija inet_addr() yra senesnis funkcija, kad tai iš esmės tas pats dalykas, kaip inet_aton(). Tai teoriškai nebenaudojamas, bet jį matysite daug ir policijos nebus ateiti jums, jei jūs jį naudoti.

Grįžti Vertė

inet_aton() grąžina ne nulis, jei adresas yra galiojantis vieną, ir jis grįžta nulis, jei adresas bus neteisingas.

inet_ntoa() grąžina taškų-ir-numerių seka statinį rezervo, kad yra perrašyta, su kiekviena skambinimo funkciją.

inet_addr() grąžina adreso in_addr_t, arba -1, jei ten klaida. (Tai yra tas pats rezultatas, kaip jei jūs bandė konvertuoti į eilutę “255.255.255.255”, kuris yra galiojantis IP adresas. Tai yra, kodėl inet_aton() yra geriau.)

Pavyzdžiui

struct sockaddr_in antelope;
char *some_addr;

inet_aton("10.0.0.1", &antelope.sin_addr); // store IP in antelope

some_addr = inet_ntoa(antelope.sin_addr); // return the IP
printf("%s\n", some_addr); // prints "10.0.0.1"

// and this call is the same as the inet_aton() call, above:
antelope.sin_addr.s_addr = inet_addr("10.0.0.1");

Taip Pat Žr.
inet_ntop(), inet_pton(), gethostbyname(), gethostbyaddr()


9.14. inet_ntop(), inet_pton()
Konvertuoti IP adresus, kad žmonėms suprantama forma ir atgal.

Prototipai

#include <arpa/inet.h>

const char *inet_ntop(int af, const void *src,
                      char *dst, socklen_t size);

int inet_pton(int af, const char *src, void *dst);

Aprašymas

Šios funkcijos yra susijusios su žmogaus-skaitymo, IP adresus ir juos konvertuoti į jų dvejetaine naudoti su įvairiomis funkcijomis ir sistema skambina. “n” reiškia “tinklo” ir “p” “pristatymas”. Arba “teksto pateikimas”. Bet jūs galite galvoti apie tai, kaip “versija spausdinimui”. “ntop” yra “tinklas spausdinimui”. Matai?

Kartais jūs nenorite pažvelgti į krūvą dvejetainis skaičius, kai žiūri į IP adresą. Jūs norite, kad jis gražus spausdintine forma, kaip 192.0.2.180, ar 2001:db8:8714:3a90::12. Tokiu atveju, inet_ntop() yra skirtas jums.

inet_ntop() mano adresas šeima af parametras (AF_INET ar AF_INET6). Src parametras turėtų būti rodyklė į struct in_addr ar struct in6_addr, kuriame adresą, kurį norite konvertuoti į eilutę. Pagaliau dst ir dydžio yra žymiklį į paskirties eilutę ir maksimalaus ilgio eilutė.

Ką turėtų maksimalią dst string būti? Kas yra ilgiausia IPv4 ir IPv6 adresus? Laimei, yra pora makrokomandas, siekiant padėti jums. Maksimalus ilgis yra: INET_ADDRSTRLEN ir INET6_ADDRSTRLEN.

Kartais, jums gali būti eilutės, kurioje yra IP adresas elektronine forma, ir jūs norite pakuoti į struct sockaddr_in arba struct sockaddr_in6. Tokiu atveju, priešingai, funcion inet_pton() yra, ką jūs po.

inet_pton() taip pat atkreipia adresą šeimos (AF_INET ar AF_INET6) af parametras. Src parametras yra žymeklį į eilutės, kurioje yra IP adresas spausdintine forma. Galiausiai dst parametras taškų, kai rezultatas turi būti saugomi, kuris yra tikriausiai struct in_addr ar struct in6_addr.

Šios funkcijos neturite DNS paieška—jums reikia getaddrinfo().

Grįžti Vertė

inet_ntop() grąžina dst parametras sėkmės, arba NULL nesėkmės (ir errno yra nustatytas).

inet_pton() grąžina 1 sėkmės. Jis grįžta -1, jei buvo padaryta klaida (errno yra nustatytas), arba 0 jei pirkimo nėra galiojantis IP adresas.

Pavyzdžiui

// IPv4 demo of inet_ntop() and inet_pton()

struct sockaddr_in sa;
char str[INET_ADDRSTRLEN];

// store this IP address in sa:
inet_pton(AF_INET, "192.0.2.33", &(sa.sin_addr));

// now get it back and print it
inet_ntop(AF_INET, &(sa.sin_addr), str, INET_ADDRSTRLEN);

printf("%s\n", str); // prints "192.0.2.33"
// IPv6 demo of inet_ntop() and inet_pton()
// (basically the same except with a bunch of 6s thrown around)

struct sockaddr_in6 sa;
char str[INET6_ADDRSTRLEN];

// store this IP address in sa:
inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &(sa.sin6_addr));

// now get it back and print it
inet_ntop(AF_INET6, &(sa.sin6_addr), str, INET6_ADDRSTRLEN);

printf("%s\n", str); // prints "2001:db8:8714:3a90::12"
// Helper function you can use:

//Convert a struct sockaddr address to a string, IPv4 and IPv6:

char *get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen)
{
    switch(sa->sa_family) {
        case AF_INET:
            inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr),
                    s, maxlen);
            break;

        case AF_INET6:
            inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr),
                    s, maxlen);
            break;

        default:
            strncpy(s, "Unknown AF", maxlen);
            return NULL;
    }

    return s;
}

Taip Pat Žr.
getaddrinfo()


9.15. listen()
Pasakykite lizdas klausytis įeinančių skambučių

Prototipai

#include <sys/socket.h>

int listen(int s, int backlog);

Aprašymas

Galite imtis savo lizdą aprašų (pagaminti su socket() sistema ryšį) ir pasakyti, kad klausytis įeinančių skambučių. Tai yra tai, kas skiria serveriai iš klientų, vaikinai.

Atsilikimas parametras gali reikšti pora skirtingus dalykus priklausomai nuo to, sistema jus, bet mažai svarbu, kaip daugelis, kol bus jungtys jūs galite turėti iki branduolio pradeda atmesti naujus. Taigi, kaip naujos jungtys ateiti, jums turėtų būti greitai accept() juos taip, kad atsilikimas nėra užpildyti. Pabandykite nustatyti jį iki 10, arba tiek, ir jei jūsų klientai pradėti gauti “Ryšio atsisakė” esant didelėms apkrovoms, nustatyti, kad jis didesnis.

Prieš skambindami listen(), serveris turėtų skambinti bind() klijuoti prie tam tikro prievado numerį. Kad prievado numerį (dėl serverio IP adresą) bus vienas, kad klientams prisijungti.

Grįžti Vertė

Grįžta nulis sėkmės, arba -1 klaidų (ir errno bus nustatyti prioritetai.)

Pavyzdžiui

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

getaddrinfo(NULL, "3490", &hints, &res);

// make a socket:

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// bind it to the port we passed in to getaddrinfo():

bind(sockfd, res->ai_addr, res->ai_addrlen);

listen(sockfd, 10); // set s up to be a server (listening) socket

// then have an accept() loop down here somewhere

Taip Pat Žr.
accept(), bind(), socket()


9.16. perror(), strerror()
Spausdinimo klaidos kaip žmogaus skaitomą eilutę

Prototipai

#include <stdio.h>
#include <string.h>   // for strerror()

void perror(const char *s);
char *strerror(int errnum);

Aprašymas

Nes tiek daug funkcijų, return -1 klaidų ir nustatyti, kad kintamojo vertė errno, kad yra tam tikrų skaičių, tai tikrai bus malonu, jei jūs galite lengvai spausdinti, kad tokia forma, kad prasmės jums.

Mercifully, perror() daro. Jei norite gauti daugiau aprašymą būti išspausdinti iki klaidą, galite taško parametras s (arba galite palikti s kaip NIEKINIS ir nieko papildomai bus atspausdinti.)

Trumpai tariant, ši funkcija priima errno vertybes, kaip ECONNRESET, ir spausdina juos gražiai, kaip.

Funkcija strerror(), yra labai panašus į perror(), išskyrus atvejus, ji pateikia žymiklį į klaidos pranešimą string už tam tikrą vertę (paprastai galite pereiti į kintamąjį errno.)

Grįžti Vertė

strerror() grąžina rodyklę į klaidos pranešimą eilutę.

Pavyzdžiui

int s;

s = socket(PF_INET, SOCK_STREAM, 0);

if (s == -1) { // some error has occurred
    // prints "socket error: " + the error message:
    perror("socket error");
}

// similarly:
if (listen(s, 10) == -1) {
    // this prints "an error: " + the error message from errno:
    printf("an error: %s\n", strerror(errno));
}

Taip Pat Žr.
errno


9.17. poll()
Bandymas renginius keli lizdai vienu metu

Prototipai

#include <sys/poll.h>

int poll(struct pollfd *ufds, unsigned int nfds, int timeout);

Aprašymas

Ši funkcija yra labai panaši į select(), kad jie abu žiūrėti rinkiniai failų deskriptoriai renginių, tokių kaip gaunamus duomenis pasiruošę recv(), lizdas pasiruošę send() duomenis, out-of-band duomenų pasiruošę recv(), klaidų ir pan.

Pagrindinė idėja yra, kad jums perduoti masyvo nfds struct pollfds, ufds, kartu su timeout milisekundėmis (1000 milisekundžių per sekundę.) Ši pertrauka gali būti ir neigiamas, jei norite laukti amžinai. Jei ne įvykių atsitinka dėl bet kurios iš lizdo aprašai pagal timeout, poll() bus grąžinti.

Kiekvienas elementas, masyvo struct pollfds yra vienas lizdas aprašų, ir sudaro šie laukai:

struct pollfd {
    int fd;         // the socket descriptor
    short events;   // bitmap of events we're interested in
    short revents;  // when poll() returns, bitmap of events that occurred
};

Prieš skambindami poll(), apkrova, min su lizdu aprašų (jei nustatyta fd neigiamas skaičius, tai struct pollfd yra ignoruojami ir jos revents laukas yra lygus nuliui) ir tada statyti renginių lauke, bitwise-sandarinimo žiedas šios makrokomandos:

POLLIN Įspėti mane, kai duomenys yra pasirengusi recv() lizdą.
POLLOUT Įspėti mane, kai aš galiu send() duomenų, kad ši kištukinis lizdas be blokavimas.
POLLPRI Įspėti mane, kai iš juostosduomenys yra pasirengusi recv() lizdą.

Kai poll() kvietimas grįžta, revents srityje bus pastatytas bitwise-ARBA virš laukų, sakau jums, aprašai, kurie iš tikrųjų turėjo, kad įvykis įvyks. Be to, šie kitų sričių, gali būti pateikti:

POLLERR Klaida įvyko dėl šio lizdo.
POLLHUP Nuotolinio pusėje ryšio pakabinti.
POLLNVAL Kažkas negerai su lizdu aprašų fd—gal tai jt inicializuoti?

Grįžti Vertė

Grąžina skaičių elementų ufds masyvo, kad buvo įvykis įvyksta dėl jų; tai gali būti nulis, jei skirtojo laiko klaida. Taip pat grįžta -1 klaidų (ir errno bus nustatyti prioritetai.)

Pavyzdžiui

int s1, s2;
int rv;
char buf1[256], buf2[256];
struct pollfd ufds[2];

s1 = socket(PF_INET, SOCK_STREAM, 0);
s2 = socket(PF_INET, SOCK_STREAM, 0);

// pretend we've connected both to a server at this point
//connect(s1, ...)...
//connect(s2, ...)...

// set up the array of file descriptors.
//
// in this example, we want to know when there's normal or out-of-band
// data ready to be recv()'d...

ufds[0].fd = s1;
ufds[0].events = POLLIN | POLLPRI; // check for normal or out-of-band

ufds[1].fd = s2;
ufds[1].events = POLLIN; // check for just normal data

// wait for events on the sockets, 3.5 second timeout
rv = poll(ufds, 2, 3500);

if (rv == -1) {
    perror("poll"); // error occurred in poll()
} else if (rv == 0) {
    printf("Timeout occurred!  No data after 3.5 seconds.\n");
} else {
    // check for events on s1:
    if (ufds[0].revents & POLLIN) {
        recv(s1, buf1, sizeof buf1, 0); // receive normal data
    }
    if (ufds[0].revents & POLLPRI) {
        recv(s1, buf1, sizeof buf1, MSG_OOB); // out-of-band data
    }

    // check for events on s2:
    if (ufds[1].revents & POLLIN) {
        recv(s1, buf2, sizeof buf2, 0);
    }
}

Taip Pat Žr.
select()


9.18. recv(), recvfrom()
Gauti duomenys lizdas

Prototipai

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int s, void *buf, size_t len, int flags);
ssize_t recvfrom(int s, void *buf, size_t len, int flags,
                 struct sockaddr *from, socklen_t *fromlen);

Aprašymas

Kai jūs turite lizdas iki ir prijungtas, galite skaityti gaunamus duomenis iš tolimosios pusės, naudojant recv() (TCP SOCK_STREAM lizdus) ir recvfrom() (UDP SOCK_DGRAM lizdus).

Abi funkcijos imtis lizdas aprašų s, a žymiklį į buferį buf, dydis (baitais) buferinės apsaugos len, ir dėl vėliavų, kad kontrolės funkcijas, kaip dirbti.

Be to, recvfrom() mano struct sockaddr*, nuo kurios bus jums pasakyti, kai duomenys buvo gauti, ir bus užpildyti fromlen dydis struct sockaddr. (Jūs turite taip pat inicijuoti fromlen būti dydį arba iš struct sockaddr.)

Taigi, ką nuostabų vėliavas, jūs galite pereiti į šią funkcija? Čia yra keletas iš jų, bet jūs turėtumėte patikrinti savo vietos žmogus puslapiuose daugiau informacijos ir kas iš tiesų yra remiama jūsų sistemoje. Jūs bitwise-ar jie kartu, ar tiesiog nustatyti vėliavas 0, jei norite, kad ji būtų reguliariai vanilės recv().

MSG_OOB Gauti iš plačiajuosčio duomenų. Tai, kaip gauti duomenis, kurie buvo atsiųsti jums su MSG_OOB vėliava send(). Kaip priimančioje pusėje, jūs turėjo signalas SIGURG iškėlė sakau, yra skubiai duomenis. Jūsų tvarkytojas tai signalas, galėtumėte skambinti ir recv() su šiuo MSG_OOB vėliava.
MSG_PEEK Jei norite skambinti recv() “tik apsimesti”, galite skambinti su šia vėliava. Tai bus pasakyti, kas laukia buferio, kai skambinate recv() “nekilnojamojo” (t. y. be MSG_PEEK vėliava. Tai kaip nukniaukti žvilgtelėti į kitą recv() skambučio.
MSG_WAITALL Pasakykite recv(), kad negali grįžti, kol visi duomenys nurodėte len parametras. Ji nepaisys jūsų norų ekstremaliomis aplinkybėmis, tačiau, pavyzdžiui, jei signalą, nutraukia skambutį arba jei kai kurie klaida įvyksta, arba, jei nuotolinio pusėje nutraukia ryšį, ir tt. Nereikia būti proto su juo.

Kai skambinate recv(), ji bus blokuoti, kol yra tam tikrų duomenų skaityti. Jei norite ne blokuoti, nustatyti lizdas ne antiblokavimo arba teiraukitės select() arba apklausos poll() norėdami pamatyti, jei yra gaunamus duomenis prieš skambinant recv() arba recvfrom().

Grįžti Vertė

Grąžina baitų skaičius, faktiškai gavo (kurie gali būti mažiau, negu jūs prašoma len parametras), arba -1 klaidų (ir errno bus nustatyti prioritetai.)

Jei nuotolinio pusėje yra nutraukė ryšį, recv() grąžins 0. Tai yra normalus būdas nustatyti, jei nuotolinio pusėje yra nutraukė ryšį. Normalumas yra gera, sukilėlių!

Pavyzdžiui

// stream sockets and recv()

struct addrinfo hints, *res;
int sockfd;
char buf[512];
int byte_count;

// get host info, make socket, and connect it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
getaddrinfo("www.example.com", "3490", &hints, &res);
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
connect(sockfd, res->ai_addr, res->ai_addrlen);

// all right!  now that we're connected, we can receive some data!
byte_count = recv(sockfd, buf, sizeof buf, 0);
printf("recv()'d %d bytes of data in buf\n", byte_count);
// datagram sockets and recvfrom()

struct addrinfo hints, *res;
int sockfd;
int byte_count;
socklen_t fromlen;
struct sockaddr_storage addr;
char buf[512];
char ipstr[INET6_ADDRSTRLEN];

// get host info, make socket, bind it to port 4950
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(NULL, "4950", &hints, &res);
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
bind(sockfd, res->ai_addr, res->ai_addrlen);

// no need to accept(), just recvfrom():

fromlen = sizeof addr;
byte_count = recvfrom(sockfd, buf, sizeof buf, 0, &addr, &fromlen);

printf("recv()'d %d bytes of data in buf\n", byte_count);
printf("from IP address %s\n",
    inet_ntop(addr.ss_family,
        addr.ss_family == AF_INET?
            ((struct sockadd_in *)&addr)->sin_addr:
            ((struct sockadd_in6 *)&addr)->sin6_addr,
        ipstr, sizeof ipstr);

Taip Pat Žr.
send(), sendto(), select(), poll(), Blokavimo


9.19. select()
Patikrinkite, ar lizdai aprašai yra pasirengusi skaityti/rašyti

Prototipai

#include <sys/select.h>

int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
           struct timeval *timeout);

FD_SET(int fd, fd_set *set);
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_ZERO(fd_set *set);

Aprašymas

Funkcija select() suteikia jums būdą, kaip vienu metu patikrinti keli lizdai, norėdami pamatyti, jei jie turi duomenų, kurie turi būti recv(), arba jei jūs galite send() duomenis, kad juos be blokavimas, arba, jei tam tikra išimtis įvyko.

Jums užpildyti jūsų rinkiniai lizdas aprašai, naudojant makrokomandas, kaip FD_SET(), virš. Kai jūs turite nustatyti, jums perduoti į funkciją kaip vieną iš šių parametrų: readfds jei jūs norite žinoti, jei bet kuris iš lizdų rinkinys yra pasirengusi recv() duomenis, writefds, jei bet kuris iš lizdai yra pasirengusi siųsti() duomenis, ir/arba exceptfds, jei jums reikia žinoti, kada išimtis (klaida), atsiranda dėl bet kurio iš lizdų. Bet ar visi šie parametrai gali būti NULL, jei nesate domina šių tipų įvykius. Po to, pasirinkite() grąžina, vertybių rinkiniai bus pakeistas į šou, kurie yra pasirengę skaityti arba rašyti, ir kurie turi išimčių.

Pirmasis parametras, n yra aukščiausios sunumeruoti lizdas aprašyme (jie tiesiog ints, prisiminti?) plius vienas.

Galiausiai, struct timeval, timeout, pabaigoje—tai leidžia pasakyti, select(), kiek laiko, kad patikrinti šių rinkinių. Jis bus grįžti po timeout, arba tada, kai įvyksta įvykis, kuris yra pirmas. Į struct timeval turi du laukus: tv_sec yra keletą sekundžių, kuris yra pridedamas tv_usec, skaičius mikrosekundėmis (1,000,000 mikrosekundėmis antra.)

Pagalbininkas makrokomandas, atlikite šiuos veiksmus:

FD_SET(int fd, fd_set *set); Pridėti fd rinkinys set.
FD_CLR(int fd, fd_set *set); Pašalinti fd iš rinkinio set.
FD_ISSET(int fd, fd_set *set); Grįžti tiesa, jei fd yra set.
FD_ZERO(fd_set *set); Aišku, visi įrašai iš rinkinio set.

Pastaba, skirta Linux vartotojams: Linux select() gali grįžti “paruoštas skaityti” ir tada ne iš tikrųjų būti pasiruošęs skaityti, todėl sukelia vėliau read() skambinkite, norėdami blokuoti. Jūs galite išspręsti šią klaidą, nustatant O_NONBLOCK vėliavos gavimo lizdą, todėl klaidų, su EWOULDBLOCK, tada ignoruoja tai klaida, jei tai įvyksta. Matyti fcntl() nuoroda puslapio, daugiau info apie nustatymas lizdas ne antiblokavimo.

Grįžti Vertė

Grąžina skaičių aprašų rinkinys, sėkmės, 0, jeigu išlaikymas buvo pasiektas, ar -1 klaidų (ir errno bus nustatyti prioritetai.) Taip pat, rinkiniai yra modifikuotas parodyti, kurie lizdai yra pasirengęs.

Pavyzdžiui

int s1, s2, n;
fd_set readfds;
struct timeval tv;
char buf1[256], buf2[256];

// pretend we've connected both to a server at this point
//s1 = socket(...);
//s2 = socket(...);
//connect(s1, ...)...
//connect(s2, ...)...

// clear the set ahead of time
FD_ZERO(&readfds);

// add our descriptors to the set
FD_SET(s1, &readfds);
FD_SET(s2, &readfds);

// since we got s2 second, it's the "greater", so we use that for
// the n param in select()
n = s2 + 1;

// wait until either socket has data ready to be recv()d (timeout 10.5 secs)
tv.tv_sec = 10;
tv.tv_usec = 500000;
rv = select(n, &readfds, NULL, NULL, &tv);

if (rv == -1) {
    perror("select"); // error occurred in select()
} else if (rv == 0) {
    printf("Timeout occurred!  No data after 10.5 seconds.\n");
} else {
    // one or both of the descriptors have data
    if (FD_ISSET(s1, &readfds)) {
        recv(s1, buf1, sizeof buf1, 0);
    }
    if (FD_ISSET(s2, &readfds)) {
        recv(s2, buf2, sizeof buf2, 0);
    }
}

Taip Pat Žr.
poll()


9.20. setsockopt(), getsockopt()
Nustatyti įvairios lizdas

Prototipai

#include <sys/types.h>
#include <sys/socket.h>

int getsockopt(int s, int level, int optname, void *optval,
               socklen_t *optlen);
int setsockopt(int s, int level, int optname, const void *optval,
               socklen_t optlen);

Aprašymas

Lizdai yra gana konfigūruojama žvėrys. Iš tiesų, jie yra labai konfigūruojama, aš net ketina padengti viskas čia. Tai tikriausiai sistemos priklausomi vistiek. Bet aš kalbėti apie pagrindai.

Akivaizdu, kad šios funkcijos gauti ir nustatyti tam tikras galimybes, kištukinis lizdas. Ant Linux box, visas lizdas informacija yra žmogus puslapyje lizdas 7 skyriuje. (Tipo: “žmogus 7 lizdas” gauti visų šių gėrybių.)

Kaip ir parametrus, s lizdą, jūs kalbate apie, dydis turėtų būti nustatytas į SOL_SOCKET. Tada galite nustatyti optname vardą jus domina. Vėl, pamatyti jūsų vyras puslapį visus variantus, bet čia yra kai kurie iš labiausiai įdomus iš jų:

SO_BINDTODEVICE Jungiasi ši lizdas simbolinę prietaiso pavadinimą, pvz. eth0 vietoj bind() susieti jį su IP adresu. Tipas ifconfig pagal Unix pamatyti prietaisų pavadinimai.
SO_REUSEADDR Leidžia kitų lizdų, kad bind() šiame uoste, jeigu yra aktyvus klausymas lizdą jungiasi į uostą jau. Tai leidžia jums gauti apie tuos “Adresas jau naudojamas” klaidos pranešimą, kai bandote paleisti savo serverį po avarijos.
SO_BROADCAST Leidžia UDP paketų kelio schemos (SOCK_DGRAM) lizdai siųsti ir gauti paketai siunčiami ir transliacijos adresą. Nieko, NIEKO!!—TCP stream lizdai! Hahaha!

Kaip parametras optval, tai paprastai žymiklį į int nurodant vertę klausimas. Už booleans, nulis yra klaidinga, ir ne nulis yra tiesa. Ir tai absoliutus faktas, išskyrus atvejus, kai tai skiriasi nuo jūsų sistemos. Jei nėra parametras būtų priimtas, optval gali būti NULL.

Galutinis parametras, optlen, turėtų būti nustatytas į ilgį optval, tikriausiai sizeof(int), bet kinta priklausomai nuo pasirinkimo. Atkreipkite dėmesį, kad tuo atveju, getsockopt(), tai rodyklė į socklen_t, ir nurodo, kokio dydžio objektą, kuris bus saugomas optval (siekiant išvengti buferio perpildymas). Ir getsockopt() bus pakeisti vertę optlen atspindėti baitų skaičius, iš tikrųjų.

Įspėjimas: kai kurios sistemos (visų pirma Sun ir Windows), variantas gali būti char vietoj int, ir yra nustatyta, kad, pavyzdžiui, simbolių reikšmė “1”, o ne iš int vertę 1. Dar kartą, patikrinkite savo vyro puslapiuose daugiau info su “žmogus setsockopt” ir “žmogus-7 lizdas”!

Grįžti Vertė

Grįžta nulis sėkmės, arba -1 klaidų (ir errno bus nustatyti prioritetai.)

Pavyzdžiui

int optval;
int optlen;
char *optval2;

// set SO_REUSEADDR on a socket to true (1):
optval = 1;
setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);

// bind a socket to a device name (might not work on all systems):
optval2 = "eth1"; // 4 bytes long, so 4, below:
setsockopt(s2, SOL_SOCKET, SO_BINDTODEVICE, optval2, 4);

// see if the SO_BROADCAST flag is set:
getsockopt(s3, SOL_SOCKET, SO_BROADCAST, &optval, &optlen);
if (optval != 0) {
    print("SO_BROADCAST enabled on s3!\n");
}

Taip Pat Žr.
fcntl()


9.21.send(), sendto()
Duomenų siuntimas iš daugiau nei lizdas

Prototipai

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int s, const void *buf, size_t len, int flags);
ssize_t sendto(int s, const void *buf, size_t len,
               int flags, const struct sockaddr *to,
               socklen_t tolen);

Aprašymas
Šios funkcijos siųsti duomenis į lizdą. Apskritai, send() naudojamas TCP SOCK_STREAM prijungtas lizdai, ir sendto() naudojamas UDP SOCK_DGRAM nesusijusios duomenų paketų kelio schemos lizdai. Su nesusijusios lizdai, turite nurodyti paskirties paketinių kiekvieną kartą, galite siųsti vieną, ir štai kodėl paskutinę parametrų sendto() nustatyti, kur paketinių vyksta.

Su tiek send() ir sendto(), parametras s yra lizdas, buf yra žymiklį į duomenų, kuriuos norite siųsti, len yra baitų skaičius, kurį norite siųsti, ir šaligatvio leidžia jums nurodyti daugiau informacijos apie tai, kaip duomenys turi būti išsiųsti, talpykla. Nustatyti vėliavas iki nulio, jei norite, kad tai būtų “normalus” duomenys. Čia pateikiami kai kurie dažniausiai naudojami vėliavas, bet ir patikrinti savo vietos send() vyras puslapių daugiau informacijos:

MSG_OOB Siųsti “iš juostos” duomenys. TCP tai palaiko, ir tai yra būdas pasakyti gavimo sistema, kad šie duomenys turi aukštesnį prioritetą, nei įprastas duomenų. Imtuvas gaus signalą SIGURG ir gali gauti šiuos duomenis be pirmąjį gauna visi kiti normalus duomenų eilėje.
MSG_DONTROUTE Nereikia siųsti šiuos duomenis per maršrutizatorių, tiesiog laikyti jį vietos.
MSG_DONTWAIT Jei send(), būtų galima blokuoti, nes išeinantys eismo užsikemša, jį grąžinti EAGAIN. Tai yra kaip “įjungti ne antiblokavimo tik už šį siųsti.” Žr. skyrių dėl blokavimo daugiau informacijos.
MSG_NOSIGNAL Jei send() į nuotolinio serverio, kuris nebėra recv(), jūs paprastai gauti signalo SIGPIPE. Pridėti šį vėliavos neleidžia, kad signalas iš iškeltas.

Grįžti Vertė

Grįžta baitų skaičius išsiųsti arba -1 klaidų (ir errno bus nustatyti prioritetai.) Atkreipkite dėmesį, kad baitų skaičius, iš tikrųjų išsiųstas gali būti mažiau nei jūs paprašė ją išsiųsti! Žr. skyrių apie tvarkymas dalinis send() pagalbininkas funkciją, gauti apie tai.

Taip pat, jei lizdas buvo uždarytas iš abiejų pusių, procesas raginama send() gaus signalą SIGPIPE. (Jei send() buvo vadinamas su MSG_NOSIGNAL vėliava.)

Pavyzdžiui

int spatula_count = 3490;
char *secret_message = "The Cheese is in The Toaster";

int stream_socket, dgram_socket;
struct sockaddr_in dest;
int temp;

// first with TCP stream sockets:

// assume sockets are made and connected
//stream_socket = socket(...
//connect(stream_socket, ...

// convert to network byte order
temp = htonl(spatula_count);
// send data normally:
send(stream_socket, &temp, sizeof temp, 0);

// send secret message out of band:
send(stream_socket, secret_message, strlen(secret_message)+1, MSG_OOB);

// now with UDP datagram sockets:
//getaddrinfo(...
//dest = ...  // assume "dest" holds the address of the destination
//dgram_socket = socket(...

// send secret message normally:
sendto(dgram_socket, secret_message, strlen(secret_message)+1, 0, 
       (struct sockaddr*)&dest, sizeof dest);

Taip Pat Žr.
recv(), recvfrom()


9.22. shutdown()
Sustabdyti tolesnį siunčia ir gauna ant lizdas

Prototipai

#include <sys/socket.h>

int shutdown(int s, int how);

Aprašymas

Štai ir viskas! Aš turėjau jį! Ne daugiau send() yra leidžiama tai lizdą, bet aš vis dar noriu recv() duomenų! Arba atvirkščiai! Kaip aš galiu tai padaryti?

Kai jūs close() lizdą aprašų, jis uždaromas iš abiejų pusių lizdas skaitymo ir rašymo, ir išlaisvina lizdas aprašų. Jei tiesiog norite pašalinti vieną pusę ar kitas, galite naudoti šį shutdown() skambučio.

Kaip ir parametrus, s yra akivaizdžiai lizdas norite atlikti šį veiksmą, ir kokių veiksmų, kad tai gali būti nurodomi kaip parametras. Kaip gali būti SHUT_RD, siekiant išvengti tolesnio recv(), SHUT_WR uždrausti toliau send(), arba SHUT_RDWR padaryti tiek.

Atkreipkite dėmesį, kad shutdown() nėra atlaisvinti lizdą aprašų, todėl jūs vis dar turite ilgainiui close() lizdą, net jei ji buvo visiškai uždarytas.

Tai yra retai naudojama sistema skambina.

Grįžti Vertė

Grįžta nulis sėkmės, arba -1 klaidų (ir errno bus nustatyti prioritetai.)

Pavyzdžiui

int s = socket(PF_INET, SOCK_STREAM, 0);

// ...do some send()s and stuff in here...

// and now that we're done, don't allow any more sends()s:
shutdown(s, SHUT_WR);

Taip Pat Žr.
close()


9.23. socket()
Skirti lizdas aprašų

Prototipai

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

Aprašymas

Grįžta naują lizdą aprašų, kad jūs galite padaryti sockety dalykų. Tai yra apskritai pirmą kvietimą į didžiulį procesas rašyti lizdas programa, galite naudoti ir rezultatas už vėlesnius ragina listen(), bind(), accept(), ar įvairių kitų funkcijų.

Įprastą naudojimą, jūs gaunate vertės šių parametrų iš skambinti getaddrinfo(), kaip parodyta žemiau pateiktame pavyzdyje. Bet jūs galite užpildyti juos ranka, jei jūs tikrai norite.

domain domain aprašoma, kokios lizdas jus domina. Tai galite, patikėkite, būti įvairių dalykų, bet kadangi tai yra lizdas vadovą, tai bus PF_INET IPv4,  ir PF_INET6 už IPv6.
type Taip pat, tipo parametras gali būti nemažai dalykų, tačiau jums tikriausiai bus nuostatą arba SOCK_STREAM, patikimas TCP lizdai (send(), recv()) arba SOCK_DGRAM už nepatikimi greitai UDP lizdai (sendto(), recvfrom().)

(Dar vienas įdomus lizdo tipas yra SOCK_RAW, kurios gali būti naudojamos statyti pakelių ranka. Tai gana kietas.)

protocol Galiausiai, protokolo parametras nurodo kurios protokolas naudoja su tam tikrai lizdo tipą. Kaip aš jau pasakiau, pavyzdžiui, SOCK_STREAM naudoja TCP. Laimei jums, kai naudojate SOCK_STREAM ar SOCK_DGRAM, jūs galite tiesiog nustatyti protokolo 0, ir ji jums naudoti tinkamą protokolo automatiškai. Kitu atveju galite naudoti getprotobyname() ieškoti tinkamo protokolo numeris.

Grįžti Vertė
Nauja kištukinių lizdų aprašų bus naudojama vėlesniuose skambučius, arba -1 klaidų (ir errno bus nustatyti prioritetai.)

Pavyzdžiui

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;     // AF_INET, AF_INET6, or AF_UNSPEC
hints.ai_socktype = SOCK_STREAM; // SOCK_STREAM or SOCK_DGRAM

getaddrinfo("www.example.com", "3490", &hints, &res);

// make a socket using the information gleaned from getaddrinfo():
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

Taip Pat Žr.
accept(), bind(), getaddrinfo(), listen()


9.24. struct sockaddr ir draugais
Struktūros tvarkymas interneto adresus

Prototipai

#include <netinet/in.h>

// All pointers to socket address structures are often cast to pointers
// to this type before use in various functions and system calls:

struct sockaddr {
    unsigned short    sa_family;    // address family, AF_xxx
    char              sa_data[14];  // 14 bytes of protocol address
};


// IPv4 AF_INET sockets:

struct sockaddr_in {
    short            sin_family;   // e.g. AF_INET, AF_INET6
    unsigned short   sin_port;     // e.g. htons(3490)
    struct in_addr   sin_addr;     // see struct in_addr, below
    char             sin_zero[8];  // zero this if you want to
};

struct in_addr {
    unsigned long s_addr;          // load with inet_pton()
};


// IPv6 AF_INET6 sockets:

struct sockaddr_in6 {
    u_int16_t       sin6_family;   // address family, AF_INET6
    u_int16_t       sin6_port;     // port number, Network Byte Order
    u_int32_t       sin6_flowinfo; // IPv6 flow information
    struct in6_addr sin6_addr;     // IPv6 address
    u_int32_t       sin6_scope_id; // Scope ID
};

struct in6_addr {
    unsigned char   s6_addr[16];   // load with inet_pton()
};


// General socket address holding structure, big enough to hold either
// struct sockaddr_in or struct sockaddr_in6 data:

struct sockaddr_storage {
    sa_family_t  ss_family;     // address family

    // all this is padding, implementation specific, ignore it:
    char      __ss_pad1[_SS_PAD1SIZE];
    int64_t   __ss_align;
    char      __ss_pad2[_SS_PAD2SIZE];
};

Aprašymas

Tai yra pagrindinės konstrukcijos visiems syscalls ir funkcijas, kurios susijusios su interneto adresus. Dažnai naudosite getaddrinfo() užpildyti šių struktūrų, ir tada bus juos skaityti, kai jūs turite.

Atminties, struct sockaddr_in ir struct sockaddr_in6 pačią pradžią struktūra, kaip struct sockaddr, ir jūs galite laisvai mesti rodyklę, iš vieno tipo į kitą be jokios žalos, išskyrus galimų galutinių visatos.

Juokauju dėl to pabaigoje visata dalykas…jei visata neturi pabaigos, kai jūs mesti struct sockaddr_in* struct sockaddr*, pažadu jums, tai grynas atsitiktinumas, ir jums net neturėtų nerimauti apie tai.

Taigi, turint tai omenyje, atminkite, kad jei funkcija sako, kad jis laikosi struct sockaddr*, galite mesti savo struct sockaddr_in*, struct sockaddr_in6*, arba struct sockadd_storage* norėdami, kad tipo su patogumo ir saugos.

struct sockaddr_in struktūra naudojama su IPv4 adresais (pvz., “192.0.2.10”). Tai turi spręsti šeimos (AF_INET), uosto, sin_port, ir IPv4 adresas sin_addr.

Taip pat šis sin_zero srityje struct sockaddr_in, kurių kai kurie žmonės teigia, turi būti nustatyta į nulį. Kiti žmonės nenori teigia, nieko apie tai (Linux dokumentus, neturi net nekalbant apie tai ne visi), ir nustačius jį iki nulio, neatrodo, kad būtų iš tikrųjų būtina. Todėl, jei jūs manote, kaip ji, nustatykite jį iki nulio naudojant memset().

Dabar, kad struct in_addr yra keistus žvėris skirtingose sistemose. Kartais beprotišką sąjungos su visų rūšių #apibrėžia ir kitų nesąmonių. Bet tai, ką jums reikia padaryti, tai naudoti tik s_addr srityje šią struktūrą, nes daugelis sistemų tik įgyvendinti, kad vienas.

struct sockadd_in6 ir struct in6_addr yra labai panašūs, išskyrus tai, kad jie naudojami IPv6.

struct sockaddr_storage yra struct galite pereiti priimti() arba recvfrom (), jei jūs bandote rašyti, IP versija-agnostikas kodas ir jūs neturite žinoti, jei naujas adresas bus IPv4 arba IPv6. Į struct sockaddr_storage struktūra yra pakankamai didelis, kad turėti abiejų tipų, skirtingai nuo originalaus mažas struct sockaddr.

Pavyzdžiui

// IPv4:

struct sockaddr_in ip4addr;
int s;

ip4addr.sin_family = AF_INET;
ip4addr.sin_port = htons(3490);
inet_pton(AF_INET, "10.0.0.1", &ip4addr.sin_addr);

s = socket(PF_INET, SOCK_STREAM, 0);
bind(s, (struct sockaddr*)&ip4addr, sizeof ip4addr);
// IPv6:

struct sockaddr_in6 ip6addr;
int s;

ip6addr.sin6_family = AF_INET6;
ip6addr.sin6_port = htons(4950);
inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &ip6addr.sin6_addr);

s = socket(PF_INET6, SOCK_STREAM, 0);
bind(s, (struct sockaddr*)&ip6addr, sizeof ip6addr);

Taip Pat Žr.
accept(), bind(), connect(), inet_aton(), inet_ntoa()


10. Daugiau Nuorodos

Jūs atėjote taip toli, ir dabar jūs esate rėkia daugiau! Kur dar galite eiti norėdami sužinoti daugiau apie visą šią medžiagą?

10.1. Knygos

Už senosios mokyklos faktinė laikyti ji ranka plaušienos, popieriaus, knygų, išbandyti kai kurias iš šių puikių knygų. I used to būti partneris su labai populiarus interneto knygų, bet jų naujų klientų sekimo sistema yra nesuderinama su spausdinimo dokumentą. Kaip, pavyzdžiui, aš gauti ne daugiau dėkingumo. Jei manote, užuojauta mano sunkią padėtį, paypal donorystės beej@beej.us. 🙂

Unix Network Programming, apimtis 1-2 pateikė W. Richard Stevens. Paskelbė Prentice Hall. ISBN apimtys 1-2: 0131411551, 0130810819.

Internetworking with TCP/IP, tomai I-III pateikė Douglas E. Comer ir David L. Stevens. Paskelbė Prentice Hall. ISBN apimtys I, II, ir III: 0131876716, 0130319961, 0130320714.

TCP/IP Illustrated, apimtis 1-3 pateikė W. Richard Stevens ir Gary R. Wright. Paskelbė Addison Wesley. ISBN apimtys 1, 2, ir 3 straipsnio ir 3-jų tomų rinkinys): 0201633469, 020163354X, 0201634953, (0201776316).
TCP/IP Network Administration pateikė Craig Hunt. Paskelbė O’Reilly & Associates, Inc. ISBN 0596002971.

Advanced Programming in the UNIX Environment pateikė W. Richard Stevens. Paskelbė Addison Wesley. ISBN 0201433079.

10.2. Interneto Nuorodos

Dėl interneto:

BSD Lizdai: Greitai Ir Purvinas Gruntas (Unix sistemos programavimo info, per daug!)

Unix Lizdas DUK

Supažindinsime su TCP/IP

TCP/IP DUK

Winsock DUK

Ir čia yra kai kurie svarbūs Vikipedijos puslapius:

Berkeley Lizdai

Interneto Protokolo (IP)

Perdavimo Valdymo Protokolas (Transmission Control Protocol – TCP)

Paketų Kelio Protokolo (User Datagram Protocol – UDP)

Kliento-Serverio

Serialization (pakavimo ir išpakavimo duomenys)

10.3. RFC

RFC—nekilnojamojo purvo! Tai yra dokumentai, kad apibūdinti priskirtų numerių, programavimo API, ir protokolai, kurie yra naudojamas Internete. Aš įtraukti nuorodas į keletą iš jų čia savo malonumui, taigi patraukti kibirą kukurūzų ir įdėti į jūsų pagalvoti:

RFC 1—Pirmoji RFC; tai suteikia jums idėja, ką “Interneto” buvo kaip tik taip, kaip ji atėjo į gyvenimą ir pažvelgti į tai, kaip ji buvo sukurta iš žemės. (Tai RFC yra visiškai pasenę, žinoma!)

RFC 768—User Datagram Protocol (UDP)

RFC 791—Interneto Protokolo (IP)

RFC 793—Perdavimo Valdymo Protokolas (TCP)

RFC 854—Telnet Protokolas

RFC 959—Failų Perdavimo Protokolas (FTP)

RFC 1350—Trivial File Transfer Protocol (TFTP)

RFC 1459—Internet Relay Chat Protokolo (IRC)

RFC 1918—Adresas lėšų Privačių Internetas

RFC 2131—Dinaminis Pagrindinio Kompiuterio Konfigūravimo Protokolas (Dynamic Host Configuration Protocol – DHCP)

RFC 2616—Hiperteksto Perdavimo Protokolas (Hypertext Transfer Protocol – HTTP)

RFC 2821—Paprastojo Pašto Perdavimo Protokolą (Simple Mail Transfer Protocol – SMTP)

RFC 3330—Specialiosios Paskirties IPv4 Adresus

RFC 3493—Pagrindinis Lizdas Sąsaja Plėtinius IPv6

RFC 3542—Papildomi Kištukiniai Lizdai Taikomųjų Programų Sąsaja (API) už IPv6

RFC 3849—IPv6 Adreso Priešdėlis Rezervuota Dokumentai

RFC 3920—Pailginamas Pranešimų ir Dalyvavimo Informacijos Protokolo (Extensible Messaging and Presence Protocol – XMPP)

RFC 3977—Tinklo Naujienų Perdavimo Protokolas (Network News Transfer Protocol – NNTP)

RFC 4193—Unikalus Vietos IPv6 Unicast Adresus

RFC 4506—Išorės Duomenų Pateikimo Standartas (External Data Representation Standard – XDR)

Į IETF yra gražus internetinis įrankis ieškoti ir naršyti RFC.

Grįžti į pagrindinį