| ClipArtMag Science Blog |

Free Cliparts

Tarmoq dasturlash uchun Beej qo'llanma

Перевод статьи - Beej's Guide to Network Programming

Автор - Brian

Источник оригинальной статьи:

http://beej.us/guide/bgnet/html/

Internet Soket yordamida

Brian “Beej Jorgensen” Hall

v3.1.2, Copyright © Noyabr 13, 2019

Intro

Salomla! Socket dasturlash sizni pastga bor? Bu narsalar sahifalardan tushunish uchun juda qiyinmiman? Siz salqin Internet dasturlash qilish istayman , lekin siz structoldin qo'ng'iroq qilish kerak bo'lsa tushunishga harakat s bir gob orqali majburan o'qish uchun vaqt yo'q bind()connect(), va boshqalar., va hokazo.

Xo'sh, nima taxmin! Men allaqachon bu jirkanch ish amalga ayting, va men har bir kishi bilan ma'lumot almashish uchun o'lim qilyapman! Siz to'g'ri joyga keldim. Ushbu hujjat o'rtacha vakolatli C programcı chet s berishi kerak / u bu tarmoq shovqin bir ogohlik olish kerak.

Va uni tekshiring: men nihoyat kelajak bilan ko'tarildim (faqat vaqt nikida ham!) va IPv6 uchun qo'llanmasini yangilagan! Rohatlaning!

Tomoshabin

Ushbu hujjat to'liq ma'lumot emas, balki o'quv qo'llanma sifatida yozilgan. Faqat socket dasturlash bilan boshlangan va suyanch izlayotgan shaxslar tomonidan o'qib qachon, ehtimol, uning eng yaxshi bo'ladi. Bu, albatta, sockets dasturlash uchun to'liq va umumiy hidoyat emas, har qanday vositalar bilan.

Umid qilamanki, garchi, bu odam sahifalar mantiqiy boshlash uchun faqat etarli bo'ladi… :-)

Platforma va kompilyator

Ushbu hujjat ichidagi kod Gnu kompilyatori yordamida Linux kompyuterida tuzilgangcc. Biroq, foydalanadigan har qanday platformani qurish kerak gcc. Tabiiyki, bu siz Windows uchun dasturlash bo'lsangiz amal qilmaydi-quyida , Windows dasturlash bo'limiga qarang.

Sotish Uchun rasmiy veb-Saytiga va Kitoblar

Ushbu hujjatning ushbu rasmiy joylashuvi:

U erda siz qo'llanmaning turli tillarga misol kodini va tarjimalarini topasiz.

Chiroyli tarzda bog'langan bosma nusxalarini sotib olish uchun (ba'zilari ularni "kitoblar" deb atashadi), tashrif:

Bu mening hujjat-yozuv turmush tarzini davom ettirish yordam beradi, chunki men sotib qadrlayman olaman!

Solaris/uchun eslatma Dasturchilar SunOS

Solaris yoki SunOS uchun kompilyatsiya qachon, agar to'g'ri kutubxonalarda ulash uchun, ba'zi qo'shimcha buyruq-line kalitlari belgilash kerak. Buning uchun shunchaki -lnsl -lsocket -lresolvkompilyatsiya buyrug'ining oxiriga "" ni qo'shing, shunga o'xshash:

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

Agar siz hali ham xatolar olish bo'lsa, yanada -lxnetbu buyruq qatorni oxirigacha qo'shib harakat qilib ko'rishingiz mumkin. Men bu nima bilmayman, aniq, lekin ba'zi odamlar uni kerak ko'rinadi.

Muammolarni topish mumkin, deb yana bir joy uchun qo'ng'iroq bo'ladi setsockopt(). Prototipi mening Linux qutisiga deb farq, shuning o'rniga:

    int yes=1;

buni kiriting:

    char yes='1';

Men bir Quyosh qutisi yo'q, deb, men yuqorida axborot har qanday sinov yo'q-u odamlar elektron pochta orqali menga aytgan faqat nima.

Windows uchun eslatma Dasturchilar

Qo'llanmaning ushbu nuqtasida, tarixan, men Derazalardagi bir oz torbalama qildim, shunchaki men buni juda yoqtirmasligim sababli. Lekin men, albatta, adolatli bo'lishi va Windows katta o'rnatish bazasi bor va aniq bir mukammal yaxshi operatsion tizimi, deb sizlarga aytib kerak.

Ular yo'qligi yurak fonder o'sadi qiladi aytish, va bu holda, men bu haqiqat bo'lishi uchun ishonaman. (Yoki, ehtimol, bu yosh.) Lekin men aytishim mumkin, nima, deb o'n yil keyin-plus shaxsiy ish uchun Microsoft OSes yordamida emas, balki, men juda ham baxtli emasman! Bunaqa, men qaytib o'tirib, xavfsiz aytish mumkin, "ishonch hosil, Windows foydalanish bepul his!"...Ok ha, meni tishlarimni g'ijimlab qo'yadi xolos.

Shunday qilib, men hali ham Linux sinash uchun rag'batlantirish, BSD, yoki Unix ba'zi lazzat, o'rniga.

Lekin odamlar ular kabi nima kabi, va siz Windows xalq bu ma'lumotlar siz bolalar uchun odatda amal qiladi, deb bilish xursand bo'ladi, bir necha kichik o'zgarishlar bilan, har qanday bo'lsa.

Agar, albatta, mumkin, bir salqin narsa Windows uchun Unix vositalari to'plam Cygwin 3, o'rnatish hisoblanadi. Men & # 8217; ve shunday qilib, bu barcha dasturlar unmodified kompilyatsiya qilish imkonini beradi, deb grapevine haqida eshitdim.

Ko'rib chiqish kerak, deb yana bir narsa Linux uchun Windows quyi. Bu asosan Windows 10 da Linux VM-ish narsasini o'rnatish imkonini beradi. Bu, shuningdek, albatta, siz joylashgan olasiz.

Lekin siz ba'zi narsalar sof Windows yo'l qilish mumkin. Bu sizlardan juda gutsy bo'ldi, va bu siz qilish kerak, nima: chopish va darhol Unix olish! Yo'q, yo'q-hazillashdim. Shu kunlarda Windows-friendly(ar) bo'lishim kerak…

Bu sizga nima qilish kerak bo'ladi (Agar Cygwin o'rnatish ekan !): birinchidan, men bu erda zikr tizimi header fayllar juda ko'p, barcha e'tiborsizlik. Agar o'z ichiga kerak bo'lgan barcha:

 

    #include 

Kuting! Bundan tashqariWSAStartup(), sockets kutubxonasi bilan boshqa hech narsa qilishdan oldin qo'ng'iroq qilishingiz kerak. Buning uchun kod shunday ko'rinadi:

#include 

{
    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);
    }

Bundan tashqari, Winsock kutubxonasida bog'langan uchun derleyici aytish kerak , odatda chaqirdi wsock32.libyokiwinsock32.lib, yoki ws2_32.libWinsock uchun 2.0. VC++ ostida, bu menyu orqali amalga oshirilishi mumkinProject, ostida Settings...LinkYorlig'ini bosing, va nomli qutisiga qarash "ob'ekt/kutubxona modullar". Qo'shish "wsock32.lib " (yoki qaysi biri lib sizning afzal) deb ro'yxatiga.

Yoki shunday eshitaman.

Nihoyat, agar WSACleanup()sockets kutubxona bilan barcha orqali odamsiz qachon qo'ng'iroq qilish kerak. Batafsil ma'lumot uchun onlayn yordam qarang.

Agar buni bir marta, bu yozuvning misollar qolgan odatda amal kerak, bir necha istisnolar bilan. Bir narsa uchun, siz close()rozetkani yopish uchun foydalana olmaysiz-buning o'rniga foydalanishingiz kerakclosesocket(). Shuningdek, select()faqat socket deskriptorlari bilan ishlaydi, fayl deskriptorlari emas (0for kabi stdin).

Agar foydalanishingiz mumkin, bir socket sinf ham mavjud, CSocket. Sizning derleyiciler qo'shimcha ma'lumot olish uchun sahifalar yordam tekshiring.

Winsock haqida ko'proq ma'lumot olish uchun, Winsock FAQ o'qib va u erdan borish.

Nihoyat, men Windows hech fork()tizimi qo'ng'iroq bor, deb eshitish, qaysi, afsuski, mening misollar ba'zi ishlatiladigan. Ehtimol, siz POSIX kutubxonasiga yoki biror narsaga bog'lanishingiz kerak, yoki buning o'rniga foydalanishingiz mumkinCreateProcess()fork()hech dalillarni oladi, va CreateProcess()haqida oladi 48 milliard vajlari. Agar bu qadar emas ekansiz agar, CreateThread()bu hazm qilish bir oz osonroq bo'ladi...afsuski multithreading haqida muhokama bu hujjat doirasi emas. Men faqat juda ko'p haqida gapirish mumkin, bilasizmi!

Elektron Pochta Siyosati

Men shuning uchun yozish uchun bepul his email savollar bilan yordam berish uchun odatda mavjud emasman, lekin men javob kafolat mumkin emas. Men juda band hayot olib va men faqat siz bir savolga javob mumkin emas vaqtlar bor. Bu voqea bo'lganda, men odatda faqat xabar o'chirish. Bu shaxsiy narsa emas; men faqat hech siz talab batafsil javob berish uchun vaqt yo'q bo'ladi.

Odatda, savol qanchalik murakkab bo'lsa, javob berish ehtimoli kamroq. Agar uni pochta oldin savolingizni tor va har qanday tegishli ma'lumotlarni o'z ichiga ishonch hosil bo'lishi mumkin bo'lsa (platforma kabi, derleyici, xato xabarlar siz qolibsan, va boshqa hech narsa siz meni muammo bartaraf yordam berishi mumkin, deb o'ylayman), agar javob olish uchun ancha ko'proq odamsiz. Ko'proq markerni uchun, ESR hujjatni o'qib, qanday savollar aqlli yo'l.

Agar javob olish bo'lmasa, unga ba'zi ko'proq hack, javob topishga harakat, va u hali qiyin bo'lsa, keyin topganingizni ayting ma'lumotlar bilan yana meni yozish va umid qilamanki, u meni yordam berish uchun etarli bo'ladi.

Endi men yozish va meni yozish emas, balki qanday haqida sizni badgered ayting, men faqat siz men to'liq hidoyat yillar davomida qabul qilgan barcha maqtov qadrlayman, deb xabar bermoqchiman. Bu haqiqiy ruhiy o'sish bo'ldi, va u yaxshi uchun ishlatiladi, deb eshitish meni gladdens! :-)Raxmat sizga!

Mirroring

Siz bu saytni aks xush kelibsiz ko'proq, ommaviy yoki xususiy bo'lsin. Agar ommaviy sayt oyna va meni asosiy sahifasidan unga bog'langan bo'lsangiz ,menga bir chiziq tomchi [email protected].

Tarjimonlar uchun eslatma

Agar qo'llanmani boshqa tilga tarjima qilmoqchi bo'lsangiz, menga yozib yuboring [email protected] va bosh sahifadan tarjimangizga havola etaman. Tarjima uchun ismingizni va aloqa ma'lumot kiritish uchun bilishadi.

Manba markdown bu hujjat UTF-8 kodlash foydalanadi.

Mualliflik huquqi litsenziya cheklashlar unutmang, tarqatish, va huquqiy bo'lim, quyida.

Tarjimani mehmon qilishimni istasangiz, faqat so'rang. Agar uni mezbonlik bo'lsangiz, men ham unga bog'langan olaman; har ikki holda ham yaxshi.

Copyright, tarqatish, va huquqiy

Tarmoq Dasturiy Beej ning Qo'llanma hisoblanadi Copyright © 2019 Brian "Beej Jorgensen" Zali.

Manba kodi va tarjimalari uchun maxsus istisnolar bilan, quyida, bu ish Creative Commons Attribution ostida litsenziyaga ega - notijorat - hech Lotin ishlari 3.0 litsenziya. Ushbu litsenziya nusxasini ko'rish uchun, tashrif

https://creativecommons.org/licenses/by-nc-nd/3.0/

yoki Creative Commons uchun xat yuborish, 171 Ikkinchi Ko'chasi, Suite 300, San-Fransisko, Kaliforniya, 94105, AQSH.

Litsenziyaning "No Lotin ishlari" qismiga xos istisno quyidagicha: ushbu qo'llanma har qanday tilga erkin tarjima qilinishi mumkin, agar tarjima aniq bo'lsa va qo'llanma to'liq nashr etilsa. Shu litsenziya cheklovlar original qo'llanma sifatida tarjima murojaat. Tarjimada tarjimon uchun nom va aloqa ma'lumotlari ham bo'lishi mumkin.

Ushbu hujjatda keltirilgan C manba kodi shu davlat domenga beriladi, va har qanday litsenziya cheklash butunlay bepul.

Ta'lim oluvchilar ushbu qo'llanmaning nusxalarini o'z o'quvchilariga tavsiya etish yoki yetkazib berish uchun erkin rag'batlantiriladi.

Aks holda o'zaro yozma partiyalar tomonidan kelishilgan bo'lmasa, muallif sifatida ish taklif etadi-va ish haqida hech qanday vakolatxonalari yoki kafolatlar qiladi, ifoda, nazarda tutilgan, qonuniy yoki boshqa, shu jumladan,, cheklanmagan holda, unvoni kafolatlar, savdo, muayyan maqsad uchun fitness, noninfringement, yoki yashirin yoki boshqa nuqsonlar yo'qligi, aniqlik, yoki xatolar yo'qligi mavjudligi, yoki kashf yoki yo'qligini.

Amaldagi qonun talab darajada tashqari, hech qanday holatda muallif har qanday maxsus uchun biron-bir huquqiy nazariyasi bo'yicha sizga javobgar bo'ladi, tasodifiy, muhim, jazo yoki namunali zarar ish foydalanish natijasida kelib chiqadigan, muallif bunday zarar ehtimoli tavsiya qilingan bo'lsa ham,.

[email protected] Qo'shimcha ma'lumot olish uchun murojaat.

Fidokorlik

Men bilan o'tmishda va kelajakda yordam bergan har bir kishi uchun rahmat, bu qo'llanma yozilgan olish. GNU, Linux, Slackware, vim, Python, Inkscape, pandoc, boshqa ko'plab: va men qo'llanmasini qilish uchun foydalanish bepul dasturiy ta'minot va paketlar ishlab chiqarish barcha odamlarga rahmat. Va nihoyat katta rahmat-siz yaxshilash uchun takliflar va dalda so'zlar bilan yozilgan qilgan tom ma'noda sizga minglab.

Men kompyuterlar dunyoda mening eng katta qahramonlar va inpirators ba'zi bu qo'llanmasini bag'ishlayman: Donald Knuth, Bryus Schneier, W. Richard Stevens, va Woz, mening o'quvchi, va butun ozod va ochiq manba dasturi jamoa.

Nashriyot Axborot

Bu kitob GNU tools ortilgan bir kamar Linux qutisiga Vim muharriri yordamida Markdown yozilgan. Muqova " art " va diagrammalar Inkscape bilan ishlab chiqariladi. Markdown HTML va lateks aylanadi / Python tomonidan PDF, Pandoc va XeLaTeX, Ozodlik shriftlar yordamida. Toolchain 100% bepul va ochiq kodli dasturlardan iborat.

Rozetka nima?

Siz "sockets" har doim nutq eshitish, va, ehtimol, siz ular aniq faqat nima hayron. Xo'sh, ular bu: standart Unix fayl identifikatorlari yordamida boshqa dasturlarga gapirish usuli.

Nima?

Ok - siz ba'zi Unix hacker davlat eshitgan bo'lishi mumkin, " Jeez, Unix har bir narsa bir fayl!"Bu kishi nima haqida gapirishi mumkin, chunki Unix dasturlari har qanday i/O ni qilganda, ular fayl identifikatoriga o'qish yoki yozish orqali amalga oshiradilar. Fayl identifikatori shunchaki ochiq fayl bilan bog'liq bo'lgan butun sondir. Lekin (va bu erda catch), bu fayl tarmoq aloqasi, FIFO, quvur, terminal, haqiqiy diskdagi fayl yoki boshqa narsalar haqida bo'lishi mumkin. UNIXda hamma narsa fayl! Agar Internet orqali boshqa dastur bilan muloqot qilish istayman, shuning uchun siz gonna bir fayl identifikatori orqali buni qilyapmiz, agar yaxshi iymon edim.

"Tarmoq aloqasi uchun ushbu fayl identifikatorini qaerdan olaman, Janob Smarty-shimlar?"ehtimol, hozir ongingizdagi oxirgi savol, lekin men bunga javob beraman: siz socket()tizimga muntazam qo'ng'iroq qilasiz. Bu socket identifikatorini qaytaradi, va siz maxsus send()va recv()(man sendman recv) socket qo'ng'iroqlar yordamida u orqali muloqot.

"Lekin, salom!"siz hozir haqida xitob bo'lishi mumkin. "Agar u fayl identifikatori bo'lsa, nima uchun Neptun nomidan oddiy read()va write()qo'ng'iroqlarni soket orqali muloqot qilish uchun ishlatolmayman?"Qisqa javob," mumkin!"Endi javob," mumkin, lekin send()va recv()ma'lumotlar uzatish ustidan juda katta nazorat taklif.”

Nima keyingi? Bu haqida qanday: soketlari barcha turlari bor. DARPA Internet manzillari bor (Internet Sockets), mahalliy tugun ustida yo'l nomlari (Unix Sockets), CCITT X. 25 manzillar (X. 25 xavfsiz e'tiborsizlik mumkin Sockets), va, ehtimol, boshqa ko'plab qaysi Unix lazzat siz ishlatish qarab. Ushbu hujjat faqat birinchi bilan shug'ullanadi: Internet Sockets.

Ikki turdagi Internet rozetkalari

Bu nima? Internet soketlari ikki turi mavjud? Darhamvarhami.bi Xo'p, yo'q. Yolg'on gapiryapman. Yana bor, lekin seni qo'rqitmoqchi emasdim. Men faqat bu erda ikki turdagi haqida gapirish ketyapman. Bu jumla tashqari, men sizga aytib ketyapman qaerda "xom Sockets" ham juda kuchli va siz ularni qarash kerak.

Hammasi joyida, allaqachon. Ikki turi qanday? Biri "oqim rozetkalari"; ikkinchisi" Datagram rozetkalari "bo'lib, oxiratni mos ravishda "SOCK_STREAM" va "SOCK_DGRAM" deb atash mumkin. Datagram soket ba'zan, deyiladi "connectionless soket". (connect()Agar chindan ham istasangiz, ular " d " bo'lishi mumkin-da. Qarang connect(, quyida.)

Oqim soketlari ishonchli ikki tomonlama ulangan aloqa oqimlaridir. Agar ikkita buyumni "1, 2" tartibda rozetkaga chiqarsangiz, ular "1, 2" tartibda teskari oxirida yetib keladi. Ular ham xatosiz bo'ladi. Men shunday aniq emasman, aslida, ular xatosiz bo'ladi, men faqat mening quloqlariga mening barmoqlarini qo'yish uchun ketyapman va chant la la la kim aks holda da'vo harakat qiladi, agar.

Nima oqim sockets foydalanadi? Yaxshi, ariza eshitgan bo'lishi mumkintelnet, ha? Bu oqim soket foydalanadi. Agar turi barcha belgilar siz ularni yozing shu tartibda kelishi kerak, huh? Bundan tashqari, veb-brauzerlar sahifalarni olish uchun oqim rozetkalaridan foydalanadigan HTTP protokolidan foydalanadi. Haqiqatan ham, agar siz port 80-dagi veb-saytga telnet qilsangiz va "GET / HTTP/1.0" yozing va ikki marta qaytaring, u htmlni sizga qaytarib beradi!

Agar telneto'rnatilgan yo'q va uni o'rnatish istamasangiz, yoki telnetmijozlarga ulanish haqida qay qilinmoqda, hidoyat deb nomlangan birtelnet-kabi dastur bilan keladi telnot . Bu qo'llanmaning barcha ehtiyojlari uchun yaxshi ishlashi kerak. (Telnet aslida spec'd tarmoq protokoli  va telnotbu protokolni amalga oshirmaydi.)

Qanday oqim soketlari ma'lumotlar uzatish sifati, bu yuqori darajada erishish yo'q? Ular "uzatish nazorat protokoli" deb nomlangan protokol foydalanish, aks holda "TCP" deb nomlanuvchi (qarang RFC 793 TCP haqida juda batafsil ma'lumot uchun). TCP sizning ma'lumotlaringiz ketma-ket va xatosiz kelishiga ishonch hosil qiladi. Siz "TCP/IP" ning yaxshi yarmi sifatida "TCP" ni eshitishingiz mumkin, bu erda "IP" "Internet protokoli" (qarang: RFC 791). IP Internet yo'l-yo'riq bilan, birinchi navbatda, shug'ullanadi va odatda ma'lumotlar yaxlitligi uchun javobgar emas.

Salqin. Nima Datagram soket haqida? Nima uchun ular connectionless deyiladi? Shartnoma nima, bu erda, baribir? Nega ular ishonchsiz? Xo'sh, bu erda ba'zi faktlar bor: agar datagram yuborsangiz, u kelishi mumkin. Bu tartibda chiqib kelishi mumkin. U kelsa, paket ichidagi ma'lumotlar xatosiz bo'ladi.

Datagram sockets ham yo'l-yo'riq uchun IP foydalanish, lekin ular TCP ishlatmang; ular "foydalanuvchi Datagram protokoli" foydalanish, yoki "UDP" (RFC 768).

Nima uchun connectionless ular? Xo'sh, asosan, bu oqim soketlari bilan bo'lgani kabi ochiq aloqani saqlab qolish shart emas. Siz faqat bir dasta qurish, manzil ma'lumotlar bilan unga bir IP header mag'lubiyatdir, va uni jo'natish. Hech qanday aloqa zarur. Ular, odatda, bir TCP suyakka mavjud emas yoki bir necha bu erda paketlarini tushib qachon ham ishlatiladi va u erda Koinotning oxiri degani emas. Namuna ilovalar: tftp(arzimas fayl uzatish protokoli, FTP uchun bir oz ukasi), dhcpcd (A DHCP mijoz), multiplayer o'yinlar, oqim audio, video konferentsiya, va boshqalar.

"Bir daqiqa kutib turing! tftpva dhcpcd ikkilik ilovalarni bir xostdan boshqasiga o'tkazish uchun ishlatiladi! Agar u kelganda ariza ishlash uchun kutish, agar ma'lumotlar yo'qolgan bo'lishi mumkin emas! Bu qanday qorong'u sehr?”

Yaxshi, mening inson do'stim, tftp va shunga o'xshash dasturlar UDP ustiga o'z protokoli bor. Misol uchun, tftp protokoli yuborilgan har bir paket uchun, qabul qiluvchi, "men uni oldim!"(an "" paketi) ACK. Agar asl paket jo'natuvchisi javob bermasa, besh soniya ichida, u nihoyat ACK olgunga qadar paketni qayta uzatadi. Bu tasdiqlash tartibi ishonchli ilovalarni amalga oshirishda juda muhim ahamiyatga SOCK_DGRAM ega.

O'yinlar kabi ishonchsiz ilovalar uchun, audio, yoki video, agar faqat tushib paketlarini e'tiborsizlik, yoki, ehtimol chaqqonlik ular uchun qoplash uchun harakat qilib ko'ring. (Quake futbolchilar texnik muddat bilan namoyon bu ta'sir bilib oladi: la'nati lag . "La'nati" so'zi, bu holda, har qanday juda iflos so'zlarni ifodalaydi.)

Nima uchun ishonchsiz asosiy protokoldan foydalanasiz? Ikki sabab: tezlik va tezlik. U & # 8217; s xavfsiz keldi, nima kuzatib tutish va u & # 8217; tartibda s ishonch hosil qilish uchun u nisbatan yong'in-va-unutish uchun yo'l tezroq va barcha. Agar chat xabarlarni yuborish bo'lsangiz, TCP katta; agar yuborish bo'lsangiz 40 dunyodagi futbolchilar soniyada pozitsion yangilanishlar, bir yoki ikki tushib olish, agar, ehtimol, u qadar ko'p muhim emas, va UDP yaxshi tanlov.

Past darajadagi bema'nilik va tarmoq nazariyasi

Men faqat protokollar qatlama zikr beri, u tarmoqlari, albatta, qanday ishlashi haqida gapirish vaqti keldi, va SOCK_DGRAMpaketlar qurilgan qanday ba'zi misollar ko'rsatish uchun. Deyarli, ehtimol, bu qismini o'tish mumkin. Biroq, bu yaxshi fon.

Ma'lumotlar Encapsulation.

Salom, bolalar, ma'lumotlarni kapsülleme haqida bilish vaqti keldi ! Bu juda juda muhim. Bu juda muhim, Agar siz Chico shtatidagi tarmoqlar kursini olsangiz, bu haqda bilib olishingiz mumkin ;-). Asosan, bu shunday deydi: paket tug'iladi, paket birinchi protokol (masalan, TFTP protokoli) tomonidan sarlavhada (va kamdan-kam hollarda altbilgide) o'ralgan ("encapsulated"), keyin butun narsa (TFTP sarlavhasi kiritilgan) keyingi protokol (aytaylik, UDP), keyin yana keyingi protokol (IP), keyin yana apparat (jismoniy) qatlamidagi yakuniy protokol (aytaylik, Ethernet).

Boshqa kompyuter paketni qabul qilganda, apparat Ethernet sarlavhasini chizadi, yadro ip va UDP sarlavhalarini chizadi, TFTP dasturi tftp sarlavhasini chizadi va nihoyat ma'lumotlarga ega.

Endi men nihoyat sharmandali qatlamli tarmoq modeli haqida gapirish mumkin (aka "ISO/OSI"). Bu tarmoq modeli boshqa modellarga nisbatan ko'p afzalliklarga ega bo'lgan tarmoq funksiyalari tizimini tavsiflaydi. Masalan; misol uchun, ma'lumotlar jismonan uzatiladi qanday g'amxo'rlik holda aynan bir xil bo'lgan sockets dasturlari yozish mumkin (serial, ingichka Ethernet, AUI, baribir) quyi darajadagi dasturlar siz uchun u bilan shug'ullanish, chunki. Haqiqiy tarmoq apparat va topologiyasi socket programcı uchun shaffof bo'ladi.

Har qanday keyingi uzatmadan, men to'liq puflab modeli qatlamlarini taqdim olaman. Tarmoq sinf sinovlar uchun bu eslab:

Fizik qatlam apparat (ketma-ket, Ethernet va boshqalar.). Dastur qatlami jismoniy qatlamdan tasavvur qilishingiz mumkin - bu foydalanuvchilar tarmoq bilan muloqot qiladigan joy.

Endi, bu model, ehtimol, agar chindan ham bo'ldim, agar avtomobil ta'mirlash qo'llanma sifatida foydalanish mumkin, shuning uchun umumiy bo'ladi. Unix bilan yanada izchil a qatlamli model bo'lishi mumkin:

Vaqt ichida bu nuqtada, ehtimol, bu qatlamlari original ma'lumotlar kapsülleme mos qanday ko'rish mumkin.

Oddiy paketni qurishda qancha ish borligini ko'ring? Jeez! Va siz "" yordamida paketi sarlavhalari o'zingizni kiriting catkerak! Shunchaki xazillashdim. Agar oqim soketlari uchun qilish kerak, barcha send()ma'lumotlar chiqib. Agar datagram sockets uchun qilish kerak, barcha siz tanlagan usuli paketini kapsüllemek va sendto()uni amalga. Yadro siz uchun Transport qatlami va Internet qatlamini quradi va apparat tarmoqqa kirish qatlamini yaratadi. Eh, zamonaviy texnologiyalar.

Shunday qilib, tarmoq nazariyasi bizning qisqacha foray tugaydi. Oh ha, men sizga men yo'l-yo'riq haqida aytish so'radim, har bir narsani aytib unutib: hech narsa! To'g'ri, bu haqda umuman gapirmoqchi emasman. Router ip header uchun paketini chiziqlar, uning yo'l-yo'riq stol maslahatlashadi ,blah blah blah. Agar chindan ham g'amxo'rlik bo'lsa IP RFC 12 tekshiring. Agar bu haqda bilib hech qachon, yaxshi, agar yashash olaman.

IP-Manzillar, structs va Ma'lumotlar Munging

Bu erda biz bir o'zgarish uchun kodni gapirish olish o'yin qismi bo'ldi.

Lekin birinchi, ning ko'proq non-kodni muhokama qilaylik! Yo ' - o'q! Birinchidan, IP-manzillar va portlar haqida gapirishni istayman. Keyin biz sockets API saqlaydi va IP manzillar va boshqa ma'lumotlarni manipulyatsiya haqida gaplashamiz.

IP-Manzillar, versiyalari 4 va 6

Ben Kenobi hali Obi Wan Kenobi deb nomlangan edi orqaga yaxshi eski kunlarda, Internet protokoli Version 4 deb nomlangan ajoyib tarmoq yo'l-yo'riq tizimi bor edi, shuningdek, IPv4 chaqirdi. Bu manzillar to'rt bayt tashkil etdi edi (A. K. A. to'rt "oktetlar"), va tez-tez "nuqta va raqamlar" shaklida yozilgan, shunday qilib, kabi: 192.0.2.111.

Ehtimol, uni atrofida ko'rgan ayting.

Aslida, bu yozma sifatida, Internetda deyarli har bir sayt IPv4 foydalanadi.

Har bir inson, jumladan, Obi Wan, baxtli edi. Things katta edi, vint Cerf nomi bilan ba'zi naysayer qadar biz IPv4 manzillar chopish haqida edi hammani ogohlantirdi!

(Qiyomat va zulmat kelayotgan IPv4 Apocalypse hammani ogohlantirish tashqari, Vint Cerf ham Internet otasi bo'lish uchun yaxshi ma'lum. Shunday qilib, men, albatta, ikkinchi hech qanday holatda emasman-uning hukm taxmin.)

Manzillar tugadimi? Bu qanday bo'lishi mumkin? Demoqchimanki, 32-bitli IPv4 adresda milliardlab ip adreslar borga o'xshaydi. Biz, albatta, u erda kompyuterlar milliardlab bormi?

Darhamvarhami.bi

Shuningdek, boshida, faqat bir necha kompyuterlar bor edi va har bir milliard imkonsiz bir qator deb o'yladim qachon, ba'zi katta tashkilotlar saxovat o'z foydalanish uchun IP-manzillar millionlab ajratilgan edi. (&T DA Xerox, MIT, Ford, HP, IBM, GE kabi,, va ba'zi bir oz kompaniyasi Apple deyiladi, bir necha ism.)

Aslida, agar u bir nechta stopgap choralari bo'lmasa, biz uzoq vaqt oldin tugardik.

Lekin hozir biz bir IP-manzil ega har bir inson haqida gaplashib turibmiz bir davrda yashayapmiz, har bir kompyuter, har kalkulyator, har bir telefon, har bir mashinalar metr, va (nima uchun emas, balki) har kuchukcha it, shuningdek.

Va shunday qilib, IPv6 tug'ildi. Chunki Vint Cerf, ehtimol, o'lmas (hatto agar uning jismoniy formasi kerak o'tishi, osmon saqlasin, u, ehtimol, allaqachon mavjud bo'lib, ba'zi turdagi hyper-aqlli ELIZA dasturini amalga ichida tub-tubiga uchib o'tadi hamda Internet2), hech kim istaydi uchun bor eshitish uchun uni aytish yana "men aytgan shunday qilib, siz" agar biz etarlicha yo'q manzillar keyingi versiyasi Internet Protokoli.

Bu sizga nimani taklif qiladi?

Biz juda ko'p manzillar kerak, deb. Biz nafaqat ikki barobar ko'p manzillar kerak, deb, emas, balki bir milliard marta ko'p, emas, balki bir ming trillion marta ko'p, lekin 79 million milliard TRILLION marta ko'p iloji manzillar! Olaman ko'rsatish 'em!deb

Siz "Beej, shu rostmi? Men katta raqamlarni inkor qilish uchun barcha asoslarim bor."Xo'sh, 32 bit va 128 bit o'rtasidagi farq juda ko'p bo'lmasligi mumkin; bu faqat 96 bit, to'g'rimi? Lekin unutmang, biz-ku gaplashib kuchlar bu yerda: 32 bit ifodalaydi ba'zi 4 milliard sonlar (232),- da 128 bit ifodalaydi haqida 340 trillion trillion trillion raqamlari (real, 2128). Bu koinotdagi har bir yulduz uchun bir million IPv4 Internet o'xshaydi.

IPv4 ning bu nuqta-va raqamlarini ham unuting; endi bizda olti burchakli vakillik bor, har bir ikki baytli bo'lak kolonka bilan ajratilgan, bu kabi:

    2001:0db8:c9d2:aee5:73e3:934a:a5ae:9551

Bu hammasi emas! Marta uchastkalar, agar unda nol poda bilan IP-manzil olaman, va siz ikki colons o'rtasida ularni kompres mumkin. Va siz har bir bayt juft uchun etakchi nol off tark mumkin. Masalan; misol uchun, manzillar bu juft har teng:

    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

Manzil::1-loopback manzili . Bu har doim "men hozir ishlayotgan bu mashina"degan ma'noni anglatadi. Ichida IPv4, bu loopback manzil hisoblanadi 127.0.0.1.

Nihoyat, IPv4 manzillari uchun IPv6-moslashuv rejimi mavjud. Misol uchun, IPv4 manzilini 192.0.2.33IPv6 manzili sifatida ifodalash uchun xohlasangiz, quyidagi belgilardan foydalanasiz:"::ffff:192.0.2.33".

Biz jiddiy kulgili gaplashib turibmiz.

Aslida, u IPv6 yaratuvchilari juda cavalierly himoyalangan foydalanish uchun manzillar trillionlab va trillionlab off lopped qilgan, bunday jiddiy qiziqarli bo'ldi, lekin biz juda ko'p bor, shubhasiz, kim ham endi sanab yotipti? Galaktikada har bir sayyorada har bir erkak, ayol, bola, kuchuk va mashinalar o'lchagich uchun juda ko'p narsa bor. Va menga ishoning, Galaktikadagi har bir sayyorada mashinalar metrlari bor. Buni haqiqat deb bilasiz.

Subnets

Tashkiliy sabablarga ko'ra, "bu ip-manzilning birinchi qismi bu bit orqali IP-manzilning tarmoq qismidir va qolgan qismi mezbon qismidir .

Misol uchun, IPv4 bilan, siz bo'lishi mumkin192.0.2.12, va biz birinchi uch bayt tarmoq va oxirgi bayt mezbon edi, deb aytish mumkin. Yoki, boshqa yo'l qo'yish, biz 12tarmoq ustida xost haqida gaplashib 192.0.2.0turibmiz (biz mezbon edi bayt amalga nol qanday ko'rish).

Va endi ko'proq eskirgan ma'lumot uchun! Tayyormi? Qadimda subnetlarning" sinflari " bo'lib, unda manzilning birinchi bir, ikki yoki uch bayti tarmoq qismi bo'lgan. Agar tarmoq uchun bir bayt va uy egasi uchun uch bor uchun etarli omadli bo'lsa edi, agar bo'lishi mumkin 24 sizning tarmoq ustida xostlar bit-qiymat (16 million yoki shunday). Bu" A sinf " tarmog'i edi. Qarama-qarshi uchida bir "sinf C" edi, tarmoq uch bayt bilan, va xost bir bayt (256 hosts, himoyalangan edi minus bir er-xotin).

Ko'rib turganingizdek, faqat bir necha sinf sifatida bor edi, sinf Cs katta dasta, va o'rtasida ba'zi sinf Bs.

IP-manzilning tarmoq qismi netmask deb nomlangan narsa bilan tasvirlangan , siz bitwise-va IP-manzil bilan undan tarmoq raqamini olish uchun. Netmask odatda kabi bir narsa ko'rinadi 255.255.255.0. (E. g. bu netmask bilan, sizning IP bo'lsa192.0.2.12, keyin tarmoq 192.0.2.12va 255.255.255.0qaysi beradi 192.0.2.0.)

Afsuski, bu internetning yakuniy ehtiyojlari uchun etarlicha yaxshi tanlanmagan edi; biz sinf C tarmoqlaridan juda tez chiqib ketgan edik va biz, albatta, sinfdan tashqarida edik, shuning uchun ham so'rash qiyin emas. Buni bartaraf etish uchun netmask uchun ruxsat etiladigan kuchlar faqat 8, 16 yoki 24 emas, balki o'zboshimchalik bilan bitlar soni bo'lishi kerak. Bas, siz bir netmask bo'lishi mumkin , aytaylik255.255.255.252, qaysi 30 tarmoq bit, va 2 tarmoq to'rt xostlar uchun ruxsat xost bit. (Netmask har doim 1-bit bir guruh 0-bit tomonidan ta'qib bir guruh ekanligini unutmang.)

Lekin u bir netmask kabi raqamlar katta mag'lubiyatga foydalanish bir oz noiloj ekan255.192.0.0. Birinchidan, odamlar qancha bit haqida intuitiv tasavvurga ega emaslar, ikkinchidan, bu juda ixcham emas. Shunday qilib, yangi uslub keldi va bu juda yaxshi. Siz faqat IP-manzil keyin bir slash qo'yish,va keyin kasr tarmoq bit soni bilan amal. Bu kabi: 192.0.2.12/30.

Yoki, IPv6 uchun, bu kabi bir narsa: 2001:db8::/32yoki 2001:db8:5413:4028::9db9/64.

Port Raqamlari

Agar yaxshi eslayman bo'lsangiz, men Internet qatlamini edi qatlamli tarmoq modeli bilan ilgari sizga taqdim (IP) mezbon-to-mezbon Transport qatlami off bo'linib (TCP va UDP). Keyingi xat oldin bu haqida tezlashtirish uchun qiling.

Chiqadi, deb bir IP-manzil o'zga (IP qatlami tomonidan ishlatiladigan), TCP tomonidan ishlatiladi yana bir manzil bor (oqim sockets) va, tasodifan, UDP tomonidan (datagram sockets). Bu port soni. Bu ulanish uchun mahalliy manzilga o'xshash 16-bit raqam.

Bir mehmonxona ko'cha manzili sifatida IP-manzil o'ylab ko'ring, va xona raqami sifatida port soni. Bu juda yaxshi o'xshashlik; ehtimol, keyinchalik men avtomobil sanoatini jalb qilaman.

Kiruvchi pochta va veb-xizmatlar hal kompyuter bor istayman aytish-qanday qilib bitta IP-manzil bilan bir kompyuterda ikki farqlash, albatta, siz?

Internetda turli xil xizmatlar turli taniqli port raqamlariga ega. Siz katta IANA Port ro'yxatda ularni barcha ko'rish mumkin yoki, Agar Unix qutisiga bo'lsangiz, sizning /etc/servicesfaylida. HTTP (web) hisoblanadi port 80, telnet hisoblanadi port 23, SMTP hisoblanadi port 25, o'yin TAQDIR ishlatiladi port 666, va hokazo. va hokazo. Portlar ostida 1024 ko'pincha maxsus hisoblanadi,va odatda foydalanish uchun maxsus OS imtiyozlar talab.

Va bu haqda gap!

Bayt Tartibi

Qirollik buyrug'i bilan! Ikki bayt Ordering bor lozim, oxirat Cho'loq va muhtasham deb nomlanuvchi bo'lishi!

Men hazil qilaman, lekin biri ikkinchisidan yaxshiroqdir. :-)

Albatta, bu aytish oson yo'li yo'q, bas, men faqat uni amalga blurt olaman: sizning kompyuter orqa orqasida teskari tartibda bayt saqlash bo'lishi mumkin. Bilaman! Hech kim sizga aytishga majbur bo'ldim.

Bu narsa, Internet dunyosidagi har bir kishi, agar siz ikki bayt hex raqamini ifodalashni istasangizb34f, uni ikkita keyingi baytda saqlashingiz b34fmumkin . Mantiqiy, va, Wilford Brimli sifatida  sizga aytaman, bu, albatta, to'g'ri narsa. Avval katta uchi bilan saqlangan bu raqam Big-Endian deb ataladi .

Afsuski, bir necha kompyuterlar butun dunyo bo'ylab bu erda va u erda tarqalib, Intel yoki Intel-mos protsessor bilan ya'ni narsa, bayt bekor saqlash, b34fkeyingi bayt 4ftomonidan ta'qib qilib, shuning uchun xotirasida saqlanadi b3edi . Bu saqlash usuli oz-Endian deyiladi .

Lekin kutib turing, men hali terminologiya bilan amalga emasman! Bu kabi tartibi AQSh tarmoq turlari, chunki yana-to'g'ri Big-Endian ham tarmoq bayt tartibi deb ataladi.

Kompyuteringiz mezbon bayt tartibda raqamlarini saqlaydi . Bu Intel 80x86 bo'lsa, mezbon bayt tartibi oz-Endian emas. Bu Motorola 68k bo'lsa, mezbon bayt tartibi katta-Endian emas. Bu PowerPC bo'lsa, mezbon bayt tartibi... yaxshi, bu bog'liq!

Paketlarni qurayotganingizda yoki ma'lumotlar tuzilmalarini to'ldirayotganingizda ikki va to'rt bayt raqamingiz tarmoq bayt tartibida ekanligiga ishonch hosil qilishingiz kerak. Agar ona mezbon bayt tartibini bilmasangiz, lekin qanday qilib bu, albatta, mumkin?

Yaxshi xabar! Siz faqat mezbon bayt tartibi to'g'ri emas taxmin olish, va siz har doim tarmoq bayt tartibda uni o'rnatish uchun bir funktsiya orqali qiymatini ishlatish. U bor bo'lsa, funktsiya sehrli konvertatsiya qilamiz, va bu yo'l kodi turli endianness mashinalari uchun ko'chma bo'ladi.

Barcha righty. Aylantirish mumkin raqamlar ikki turi mavjud: short(ikki bayt) va long(to'rt bayt). Bu vazifalarunsigned, shuningdek o'zgarishlar uchun ishlaydi. shortAgar tarmoq bayt maqsadida mezbon bayt maqsadida bir aylantirish istayman sen. Boshlanish bilan "h" uchun "uy egasi", kuzatib, u bilan "uchun", keyin "n" uchun "tarmoq", va "s" uchun "qisqa": h-uchun-n-s, yoki htons() (o'qish: "Mezbonlik qilish Tarmoq Qisqa").

Bu deyarli juda oson…

Siz istagan "n", "h", "s", va "l" har birlashmasidan foydalanishingiz mumkin, albatta, ahmoq kishilarning sanab emas. Misol uchun, bir yo'q stolh()("uzoq xost qisqa") funktsiya—emas, balki, bu partiya da, baribir. Lekin bor:

 

Funktsiya Dilshodbek Yuldashev
htons() host to network short
htonl() host to network long
ntohs() network to host short
ntohl() network to host long

Asosan, siz & # 8217; ll tarmoq bayt tartibi uchun raqamlar aylantirish uchun ular tel chiqib oldin istayman, va ular tel off keladi, deb bayt tartibi mezbonlik qilish, ularni aylantirish.

64-bitli varyantni bilmadim, uzr. Va siz suzuvchi nuqtasini qilish bo'lsangiz , Serialization haqida bo'limiga nazoratuzoq quyida.

Men aks holda aytish ekan, bu hujjatda raqamlari mezbon bayt tartibda faraz.

structs

Xo'sh, biz nihoyat shu yerdamiz. Dasturlash haqida gapirish vaqti keldi. Ushbu bo'limda, men sockets interfeysi tomonidan ishlatiladigan turli ma'lumotlar turlarini qamrab olaman, ulardan ba'zilari tushunishga haqiqiy ayiq, chunki.

Birinchidan, oson: socket identifikatori. A socket deskriptor quyidagi turi hisoblanadi:

    int

Faqat muntazam int.

Things bu erdan g'alati olish, bas, faqat orqali o'qish va men bilan qo'pol.

Mening Birinchi Struct™—struct addrinfo. Bu tuzilma yanada so'nggi ixtiro, va keyingi foydalanish uchun socket manzil tuzilmalarni tayyorgarlik uchun ishlatiladi. Bundan tashqari, mezbon nomi lookups ishlatiladi bo'lyapdi, va xizmat nomi lookups. Biz haqiqiy foydalanish uchun bo'lsin, keyinchalik ko'proq mantiqiy olaman, lekin faqat u ulanish paytida qo'ng'iroq olaman birinchi narsalardan biri ekanini hozircha bilaman.

    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
    };

Siz & # 8217; ll bu struct bir oz up yuklashingiz, va keyin qo'ng'iroq getaddrinfo(). Bu sizga kerak bo'lgan barcha sovg'alar bilan to'ldirilgan bu tuzilmalar yangi bog'liq ro'yxatiga bir namoyishchi qaytib olaman.

Siz sohasida IPv4 yoki IPv6 foydalanish uchun uni majburai_family, yoki baribir foydalanish kabi qoldiring mumkinAF_UNSPEC. Sizning kodi IP versiyasi-agnostic bo'lishi mumkin, chunki, bu salqin.

Bu bilan bog'liq ro'yxati unutmang: ai_nextkeyingi element da ball-agar tanlash uchun bir necha natijalar bo'lishi mumkin. Men ishlagan birinchi natija foydalanish edim, lekin siz turli ish ehtiyojlarini bo'lishi mumkin; men hamma narsani bilmayman, odam!

Siz ai_addrsohasida struct addrinfobir pointer ekanligini ko'rasiz struct sockaddr. Bu erda biz ip-manzil tuzilmasidagi narsalarning jiddiy tafsilotlariga kirishni boshlaymiz.

Odatda, bu tuzilmalar uchun yozish kerak bo'lishi mumkin emas; oftentimes, getaddrinfo()siz uchun to'ldirish uchun qo'ng'iroq struct addrinfosiz & # 8217; ll kerak bo'lgan barcha. Siz, ammo, structqadriyatlarni olish uchun bu s ichida peer kerak, bas, men bu erda ularni taqdim qilyapman.

(Shuningdek, oldin yozilgan barcha kod struct addrinfoixtiro qilindi, biz qo'lidan barcha bu narsalar qadoqlangan, bas, siz aynan shunday yovvoyi amalga IPv4 kodi ko'p ko'rasiz. Bilasizmi, ushbu qo'llanmaning eski versiyalarida va boshqalar.)

Ba'zi structS IPv4 bor, ba'zi IPv6 bor, va ba'zi ham. Qaydadir qaydadir yasayman.

Baribir, struct sockaddrsoketlari ko'p turlari uchun ushlab socket manzil axborot.

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

sa_family turli xil narsalar bo'lishi mumkin, lekin bu AF_INEThujjatda qilgan har bir narsa uchun (IPv4) yoki AF_INET6(IPv6) bo'ladi. sa_datarozetkaga uchun manzil manzili va port raqamini o'z ichiga oladi. Agar tediously qo'lidan manzilini to'plami istamayman, chunki, bu juda noiloj sa_databo'ladi.

Bilan shug'ullanish struct sockaddruchun dasturchilar parallel tuzilmani yaratdilar: struct sockaddr_in("in" for "Internet") IPv4 bilan foydalanish uchun.

Va bu muhim bit: a struct sockaddr_ina pointer a struct sockaddrva aksincha uchun pointer tashlash mumkin. Shunday bo'lsa-daconnect(), bir istaydistruct sockaddr*, agar siz hali ham foydalanish struct sockaddr_inva oxirgi daqiqada uni tashlab mumkin!

    // (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
    };

Bu tuzilma oson socket manzili elementlarini mos yozuvlar qiladi. sin_zero(A uzunligi tuzilishini pad kiritilgan qaysi struct sockaddr) funktsiyasi bilan barcha nol o'rnatilgan bo'lishi kerak, deb unutmang memset(). Bundan tashqarisin_family, a-ga mos keladigan sa_familystruct sockaddrva " "ga o'rnatilishi kerakAF_INET. Nihoyat, sin_porttarmoq bayt tartibda bo'lishi kerak (foydalanib htons()!)

Chuqurroq qazaylik! Siz sin_addrmaydon bir qarang struct in_addr. Bu narsa nima? Yaxshi, haddan tashqari dramatik bo'lishi uchun emas, balki, lekin u hamma vaqt qo'rqinchli uyushmalari biri:

    // (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)
    };

Whoa! Xo'sh, u birlashma edi, lekin hozir o'sha kunlar ketgan ko'rinadi. Yaxshi riddance. Agar turi bo'lishi uchun e'lon inaqilgan bo'lsastruct sockaddr_in, basina.sin_addr.s_addr,, 4-bayt IP-manzil zikr (tarmoq bayt tartibda). Sizning tizimi hali uchun Xudo-dahshatli ittifoqini foydalanadi bo'lsa ham unutmangstruct in_addr, men yuqorida singari siz hali ham aynan bir xil tarzda 4-bayt IP manzilini mos yozuvlar mumkin (#defines tufayli bu).

Nima IPv6 haqida? Shunga o'xshash structs buning uchun mavjud, shuningdek:

    // (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
    };

IPv6 IPv4 IPv4 bir manzil va port, bir qator bor, faqat kabi IPv6 bir manzil va port, bir qator bor, deb unutmang.

Bundan tashqari, men hozir uchun IPv6 oqimi axborot yoki tortib ID sohalarda haqida gapirish emasman unutmang ... bu faqat bir starter hidoyat bo'lib. :-)

Oxirgi lekin eng kam emas, bu erda yana bir oddiy tuzilishi, struct sockaddr_storagedeb IPv4 va IPv6 tuzilmalarni ham ushlab etarlicha katta bo'lishi uchun mo'ljallangan. Qarang, ba'zi qo'ng'iroqlar uchun, ba'zan u bir IPv4 yoki IPv6 manzili bilan to'ldiring yog'adiganga bo'lsa oldindan bilmayman.struct sockaddr Bas, siz katta tashqari juda o'xshash, bu parallel tarkibida o'tibstruct sockaddr, keyin siz kerak turiga tashlab:

    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];
    };

Nima muhim siz sohasida manzil oilasini ko'rish mumkinss_family, deb hisoblanadi—u yoki yo'qligini ko'rish uchun bu tekshirish AF_INETAF_INET6(IPv4 yoki IPv6 uchun). Keyin siz uni xohlaysizmi struct sockaddr_inyoki struct sockaddr_in6xohlaysizmi.

, Bir Qismi IP-Manzillar Deux

Yaxshiyamki siz uchun, siz IP-manzillar manipulyatsiya qilish imkonini beradi vazifalari bir guruh bor. Ularni qo'l bilan tushunishga va longoperator bilan to'ldirishga hojat yo'q<<.

Birinchidan, siz bir bor aytaylikstruct sockaddr_in ina, va siz unga saqlamoqchi bo'lgan IP-manzil "10.12.110.57"yoki "2001:db8:63b3:1::3490" bor. Agar foydalanmoqchi bo'lgan funktsiya,inet_pton(), belgilash yoki qarab ham bir yoki bir ichiga raqamlar-va-nuqta qayd bir IP manzilini o'zgartiradi struct in_addrstruct in6_addrAF_INETAF_INET6. ("pton"tarmog'iga taqdimot" uchun turadi "- deb eslash osonroq bo'lsa, siz "tarmog'iga bosma" uni qo'ng'iroq qilishingiz mumkin.) Konversiyani quyidagicha amalga oshirish mumkin:

    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

(Tezkor eslatma: ishlarni eski yo'li deb nomlangan vazifani inet_addr()yoki boshqa vazifani ishlatiladi inet_aton(); bu endi eskirgan va IPv6 bilan ishlamaydi.)

Endi yuqoridagi kod parchalari juda mustahkam emas, chunki xato tekshirilmaydi. Qarang, inet_pton()-1xato qaytadi, yoki 0 manzil xabarchi bo'lsa. Shunday qilib, foydalanishdan oldin natija 0 dan katta ekanligiga ishonch hosil qilish uchun tekshiring!

Hammasi joyida, endi siz ularning ikkilik vakolatxonalari uchun string IP manzillar aylantirish mumkin. Nima atrofida boshqa yo'l haqida? Agar sizda mavjud bo'lsa struct in_addrva siz uni raqamlar va nuqtalarda chop etishni istasangiz nima bo'ladi? (Yoki struct in6_addrsiz istagan bir, uh," hex-va-colons " qayd.) Bu holda, siz vazifasini ishlatish uchun kerakli olaman inet_ntop()("ntop ""taqdimot tarmoq" degan ma'noni anglatadi-bu eslash oson bo'lsa, siz "bosma tarmoq" uni qo'ng'iroq qilishingiz mumkin), bu kabi:

// 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);

Agar uni qo'ng'iroq qachon, agar manzil turini o'tib olaman (IPv4 yoki IPv6), manzil, bir mag'lubiyatga bir pointer natija ushlab, va bu mag'lubiyatga maksimal uzunligi. (Ikki so'l qulay eng yirik IPv4 yoki IPv6 manzilini ushlab kerak string hajmini o'tkazadi: INET_ADDRSTRLENva INET6_ADDRSTRLEN.)

(Yana bir tez eslatma ishlarni eski yo'l yana bir bor zikr qilish: bu konvertatsiya qilish tarixiy vazifasi deb atalgan inet_ntoa(). Bundan tashqari, eskirgan va IPv6 bilan ishlamaydi.)

Nihoyat, bu vazifalar faqat soni IP—manzillar bilan ishlash-ular bir hostname har qanday nameserver DNS qidirish qilmaydi, " www.example.com"kabi. Siz buni uchun foydalanadigetaddrinfo(), agar keyinchalik ko'rasiz sifatida.

Xususiy (Yoki Uzilgan) Tarmoqlar

Joylarda uchastkalar o'z himoya qilish uchun dunyoning qolgan tarmoq maxfiy qilsangiz bir xavfsizlik devori bor. Va tez-tez marta, xavfsizlik devori "tashqi" uchun "ichki" IP-manzillar tarjima (dunyoda har bir kishi biladi , deb) tarmoq Manzili tarjima deb nomlangan jarayon yordamida IP-manzillar, yoki NAT.

Hali asabiylashyapsizmi? "Qaerda u barcha bu g'alati narsalar bilan bo'lyapti?”

Xo'sh, dam olish va o'zingizni alkogolsiz (yoki spirtli) ichimlikni sotib oling, chunki yangi boshlovchi sifatida siz nat haqida tashvishlanishingiz shart emas, chunki u siz uchun oshkora amalga oshiriladi. Lekin siz ko'rib turgan tarmoq raqamlari bilan chalkashib ketganingizda xavfsizlik devori orqasida tarmoq haqida gapirishni xohladim.

Masalan; misol uchun, men uyda bir xavfsizlik devori bor. DSL kompaniyasi tomonidan menga ajratilgan ikkita statik IPv4 manzilim bor va hali tarmoqda yettita Kompyuterim bor. Bu qanday mumkin? Ikkita kompyuter bir xil IP-manzilni almasha olmaydi, yoki boshqa ma'lumotlar qaysi biriga borishni bilmaydi!

Javob: ular bir xil IP manzillarini almashishmaydi. Ular o'ziga ajratilgan 24 million IP-manzilli xususiy tarmoqda. Ular faqat men uchun. Xo'sh, men uchun hamma boshqa hech kimga tegishli emas. Mana nima bo'layapti:

Agar uzoq kompyuterga kirsam, 192.0.2.33-dan kirganimni aytadi, bu mening ISP menga taqdim etgan davlat IP-manzili. Ammo mahalliy kompyuterimdan uning IP-manzili nima ekanligini so'rasam, 10.0.0.5 deydi. IP adresni bir biridan kim tarjima qilyapti? Ana, otaxon! NAT amaldagi ekan!

10.x.x.x faqat to'liq uzilgan tarmoqlarda yoki xavfsizlik devorlari ortida bo'lgan tarmoqlarda foydalanish uchun bir nechta himoyalangan tarmoqlardan biri. Agar foydalanish uchun xususiy tarmoq raqamlari mavjud bo'lgan tafsilotlar RFC 1918 belgilangan, lekin siz ko'rasiz ba'zi umumiy bo'lganlar 10.x.x.xva192.168.x.x, qaerda x0-255, odatda. Kamroq keng tarqalgan172.y.x.x, qaerda yo'rtasida ketadi 16 va 31.

Bir NATing xavfsizlik devori ortida tarmoqlari bu himoyalangan tarmoqlari birida bo'lishi shart emas, lekin ular tez-tez bor.

(Qiziqarli fakt! Mening tashqi IP-manzilim haqiqatan ham emas 192.0.2.33192.0.2.xTarmoq "haqiqiy" ip-manzillarni hujjatlarda ishlatish uchun himoyalangan, xuddi ushbu qo'llanma kabi! Wowzers!)

Ipv6da xususiy tarmoqlar ham bor. Ular bilan boshlash olaman fdXX:(yoki, ehtimol, kelajakdafcXX:), RFC boshiga sifatida 4193. NAT va IPv6 odatda aralashmaydi, ammo (Agar siz Ipv6ni ushbu hujjatning doirasidan tashqarida bo'lgan IPv4 gateway narsasiga aylantirmasangiz) - nazariyada sizning ixtiyoringizda juda ko'p manzillarga ega bo'lasiz. Lekin siz tashqarida yo'l bo'lmaydi, bir tarmoq ustida o'zingiz uchun manzillar ajratish bo'lsangiz, bu buni qanday bo'ladi.

IPv4 dan sakrash uchun IPv6

Lekin men faqat u IPv6 bilan yer olish uchun mening kodi o'zgartirish nima bilishni istayman! Ayting endi!

Ok! Ok!

Bu erda deyarli hamma narsa men yuqorida, ustidan ketdi ayting bir narsa, lekin u sabrsiz uchun qisqa versiyasi. (Albatta, bundan ortiq narsa bor,lekin bu qo'llanma uchun amal qiladi.)

  1. Avvalo, getaddrinfo()struct sockaddro'rniga qo'l bilan tuzilmalarni qadoqlash, barcha ma'lumot olish uchun foydalanishga harakat qiling. Bu sizga ip versiyasi-agnostic davom etadi, va keyingi qadamlar ko'p bartaraf etadi.

  2. Agar IP versiyasi bilan bog'liq hech narsa qattiq-kodlash odamsiz topish har qanday joy, bir yordamchi funktsiyasi bilan o'rash uchun harakat qilib ko'ring.

  3. Uchun o'zgartirish AF_INETAF_INET6.

  4. Uchun o'zgartirish PF_INETPF_INET6.

  5. INADDR_ANYin6addr_anyTopshiriqlarni biroz farqli bo'lgan topshiriqlarga o'zgartirish:

        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

    Bundan tashqari, qiymat IN6ADDR_ANY_INITe'lon qilinganda initsializator sifatida foydalanish mumkinstruct in6_addr, shunga o'xshash:

        struct in6_addr ia6 = IN6ADDR_ANY_INIT;
  6. Buning o'rniga struct sockaddr_infoydalanishstruct sockaddr_in6, tegishli sifatida sohalarda "6" kiritish uchun ishonch hosil bo'lish (struct s qarang, yuqorida). sin6_zeroMaydon yo'q.

  7. Buning o'rniga struct in_addrfoydalanishstruct in6_addr, tegishli sifatida sohalarda "6" kiritish uchun ishonch hosil bo'lish (structs qarang, yuqorida).

  8. O'rniga inet_aton()yoki inet_addr(), foydalanish inet_pton().

  9. O'rniga inet_ntoa(), foydalanish inet_ntop().

  10. O'rniga gethostbyname(), ustun foydalanish getaddrinfo().

  11. Buning o'rnigagethostbyaddr(), ustun foydalanish getnameinfo()(gethostbyaddr()hali IPv6 bilan ishlash mumkin bo'lsa-da,).

  12. INADDR_BROADCAST endi ishlamaydi. IPv6 multicast o'rniga foydalanish.

Et voila!

Tizim qo'ng'iroqlari yoki büstü

Bu biz tizimi qo'ng'iroqlar ichiga olish bo'lim (va boshqa kutubxona qo'ng'iroqlar) Agar Unix qutisiga tarmoq funksiyalarini kirish imkonini beradi, yoki bu borada sockets API qo'llab-quvvatlaydi har qanday quti (BSD, Windows, Linux, Mac, nima-bor-siz.) Agar bu vazifalardan biri qo'ng'iroq qachon, kernel ustidan oladi va avtomatik ravishda siz uchun barcha ishlarni qiladi.

Eng odamlar bu erda atrofida xususda olish joy bu narsalarni qo'ng'iroq qilish uchun nima tartibi. Bu, mansahifalar hech foydalanish bor, agar ehtimol kashf ayting sifatida. Xo'sh, bu dahshatli vaziyat bilan yordam berish uchun, men aniq quyidagi bo'limlarda tizimi qo'ng'iroqlar amalga yotar harakat ayting (taxminan) agar dasturlarida ularni qo'ng'iroq qilish kerak bo'ladi shu tartibda.

Bu, bu erda va u erda namuna kodi bir necha dona bilan birlashganda, ba'zi sut va cookies (men siz o'zingizni ta'minlash kerak bo'ladi qo'rqaman), va ba'zi xom jasorat va, va siz Jon Postel o'g'li kabi Internet atrofida ma'lumotlarni beyazlatma turaman!

(Iltimos, qisqalik uchun quyidagi ko'plab kod parchalari zarur xatolarni tekshirishni o'z ichiga olmaydi. Va ular juda tez-tez qo'ng'iroqlar natija getaddrinfo()muvaffaqiyatli va bog'liq ro'yxatda bir amal kirishini qaytish uchun, deb taxmin. Bu vaziyatlarda har ikki to'g'ri yakka dasturlari hal etiladi, garchi, bas, bir model sifatida o'sha foydalanish.)

getaddrinfo()- Ishga tayyorlaning!

Bu variantlar bir poda bilan bir funktsiya haqiqiy beygir emas, lekin foydalanish aslida juda oddiy. Bu structsizga keyinchalik kerak s o'rnatish yordam beradi.

Tarixning kichik bir qismi: gethostbyname()DNS lookups qilish uchun chaqirilgan funktsiyani ishlatishingiz kerak edi. Keyin siz & # 8217; D ichiga qo'l bilan bu ma'lumotlarni yuklashingiz struct sockaddr_in, va bu sizning qo'ng'iroqlar foydalanish.

Bu endi kerak emas, Yaxshiyamki. (Nor u kerak emas, Agar IPv4 va IPv6 uchun ham ishlaydi kodni yozish bo'lsangiz!) Bu zamonaviy paytlarda, siz hozir getaddrinfo()siz uchun yaxshi narsalar har qanday qilsa vazifasini bor, DNS va xizmat nomi lookups jumladan, va structsizga kerak s to'ldiradi, bundan tashqari!

Keling, bir ko'rib chiqaylik!

    #include 
    #include 
    #include 
    
    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);

Siz bu vazifani uch kirish parametrlarini berish,va u sizga bog'liq-ro'yxatiga bir namoyishchi beradi,res, natijalari.

nodeParametr ulanish uchun mezbon nomi, yoki IP-manzil.

Keyingi service"80", yoki /etc/services"http" yoki "ftp" yoki "telnet" yoki "smtp" yoki baribir kabi (IANA Port Ro'yxatida 20 yoki UNIX mashina ustida fayl topilgan) muayyan xizmat nomi kabi, bir port raqami bo'lishi mumkin parametr hisoblanadi.

Nihoyat, hintsparametr struct addrinfosiz allaqachon tegishli ma'lumotlar bilan to'ldirilgan ayting bir ishora.

Agar siz xostingizning IP-manzilini tinglashni istagan server bo'lsangiz, bu erda namuna qo'ng'irog'i, port 3490. Bu aslida har qanday tinglash yoki tarmoq o'rnatish qilmaydi unutmang; u faqat biz keyinchalik foydalanish olaman tuzilmalarni sozlash:

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
hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

if ((status = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
    fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
    exit(1);
}

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

// ... do everything until you don't need servinfo anymore ....

freeaddrinfo(servinfo); // free the linked-list

Men o'rnatilgan e'tibor ai_familyberingAF_UNSPEC, shunday qilib, biz IPv4 yoki IPv6 foydalanish, agar men farqi yo'q, deb. Siz AF_INETyoki AF_INET6maxsus bir yoki boshqa bo'lsangiz, uni o'rnatishingiz mumkin.

Bundan tashqari, siz AI_PASSIVEu erda bayroqni ko'rasiz; bu getaddrinfo()mening mahalliy uy egasi manzilini soket tuzilmalariga tayinlash uchun aytadi. Bu yaxshi, chunki siz uni qattiq kodlashingiz shart emas. (Yoki men hozirda bor qaerda birinchi parametr sifatida ma'lum bir manzil qo'yish mumkingetaddrinfo()NULL, u erda.)

Keyin qo'ng'iroq qilamiz. Xato bor bo'lsa ( getaddrinfo()non-nol qaytaradi), biz funktsiyasi yordamida uni chop etishingiz mumkingai_strerror(), ko'rib turganingizdek. Har bir narsa to'g'ri ishlaydi bo'lsa, garchi, servinfos bog'liq ro'yxatiga ishora qiladistruct addrinfo, qaysi har struct sockaddrbiz keyinchalik foydalanishingiz mumkin, ayrim turdagi bir o'z ichiga oladi! Keng tarqalgan!

Nihoyat, biz oxir-oqibat, barchagetaddrinfo(), shuning uchun xushmuomala, biz uchun ajratilgan bog'liq ro'yxati bilan amalga qolinishini, biz mumkin (va kerak) bir qo'ng'iroq bilan uni barcha ozod freeaddrinfo().

Agar siz ma'lum bir serverga ulanishni istagan mijoz bo'lsangiz, masalan, qo'ng'iroq qiling "www.example.net" port 3490. Shunga qaramay, bu aslida ulanmaydi, lekin keyinchalik foydalanadigan tuzilmalarni o'rnatadi:

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.

Men servinfobu manzil axborot barcha turlari bilan bog'liq ro'yxati deb tutish. Ushbu ma'lumotni namoyish qilish uchun tezkor demo dasturini yozaylik. Bu qisqa dastur  buyruq qatorni belgilash baribir xost uchun IP-manzillar chop etadi:

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 

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;
}

Ko'rib turganingizdek, kodi getaddrinfo()siz buyruq qatorni o'tib baribir chaqiradi , deb ishora bog'liq ro'yxatini to'ldiradires, va keyin, biz ro'yxati ustida yineleme va bosma narsalar amalga yoki nima bo'lishidan qat'iy nazar, albatta, mumkin.

(Biz IP versiyasiga qarab s turli turdagi qazish kerak qaerda u erda chirkinlik bir oz borstruct sockaddr. Buning uchun uzr so'rayman! Men uning atrofida bir yaxshi yo'l ishonch hosil emasman.)

Namuncha yugurasiz! Har bir screenshots sevadi:

    $ 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

Endi biz nazorat ostida, deb bor, biz getaddrinfo()boshqa socket vazifalariga o'tib olish natijalarini foydalanish va olaman, uzoq so " ng, bizning tarmoq ulanishini tashkil olish! O'qib turing!

socket()- Fayl Deskriptorini oling!

Men endi uni o'chirib qo'yish mumkin deb o'ylayman—men socket()tizimi qo'ng'iroq haqida gapirish kerak. Mana borini:

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

Lekin bu dalillar nima? Ular siz istagan socket qanday aytish imkonini beradi (IPv4 yoki IPv6, oqim yoki datagram, va TCP yoki UDP).

Odamlar bu qadriyatlarni hardcode bo'lardi bo'lishi uchun ishlatiladi, va, albatta, hali buni mumkin. (domainis PF_INETyoki PF_INET6typeis SOCK_STREAMyoki SOCK_DGRAMva protocol0berilganlar uchun to'g'ri protokolni tanlash uchun o'rnatilishi mumkin type. Yoki kerakli protokol qarash qo'ng'iroq mumkingetprotobyname(), "TCP" yoki "udp".)

(Bu PF_INETnarsa AF_INETmaydon initializing qachon foydalanishingiz mumkin, deb bir yaqin qarindoshi sizning sin_familystruct sockaddr_in. Aslida, ular & # 8217; qayta shunday yaqindan ular aslida bir xil qiymatga ega, deb bog'liq, va ko'p dasturchilar qo'ng'iroq socket()va AF_INETo'rniga birinchi argument sifatida o'tadi PF_INET. Endi, ba'zi sut va Cookie olish, bu hikoya uchun vaqti keldi, chunki. Bir vaqtlar, uzoq vaqt oldin, ehtimol, manzil oilasi ("AF" "AF_INET" "" """""""" PF " ning protokol oilasi ("PF") tomonidan ko'rsatilgan bir nechta protokollarni qo'llab-quvvatlashi mumkin “PF_INET"turadi). Bunaqasi bo'magan. Va ular barcha baxtli hech keyin yashagan, oxiri. Shunday qilib, eng to'g'ri narsa AF_INETsizning struct sockaddr_inva sizning da'vatingizda foydalanishdir PF_INETsocket().)

Baribir shunisi yetarli. Nima siz, albatta, istayman qo'ng'iroq natijalaridan qadriyatlarni foydalanish getaddrinfo()hisoblanadi, va socket()bu kabi to'g'ridan-to'g'ri ularni boqish:

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);

socket() sizga keyinchalik tizim qo'ng'iroqlarida yoki xatolikda foydalanishingiz mumkin bo'lgan soket identifikatorini qaytaradi-1. Global o'zgaruvchi errnoxatoning qiymatiga o'rnatiladi (errno batafsil ma'lumot uchun man sahifasiga va ko'p sonli dasturlarda foydalanish bo'yicha tezkor eslatmaga qarangerrno).

Yaxshi, yaxshi, yaxshi, lekin bu soket qanday yaxshi? Javob o'zi tomonidan, albatta, hech qanday yaxshi, deb, va siz o'qish va u har qanday mantiqiy uchun ko'proq tizim qo'ng'iroqlar qilish kerak.

bind()—Qaysi portdaman?

Bir socket bor bir marta, mahalliy mashina ustida bir port bilan bu socket dotsent bo'lishi mumkin. (listen()Agar ma'lum bir port—multiplayer tarmoq o'yinlari bo'yicha kiruvchi ulanishlar uchun fiz agar ular sizga aytib, bu, albatta, tez-tez amalga oshiriladi "ulanish 192.168.5.10 port 3490".) Port raqami kernel tomonidan ma'lum bir jarayonning soket identifikatoriga kiruvchi paketga mos kelish uchun ishlatiladi. Agar faqat bir yumush connect()bo'ladi bo'lsangiz (agar mijoz odamsiz, chunki, emas, balki server), bu, ehtimol, keraksiz bo'lishi hisoblanadi. Baribir o'qing, faqat tepishar uchun.

Bu erda bind()tizim chaqiruvi uchun konspekt:

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

sockfd socket fayl deskriptori bilan qaytariladimisocket()my_addrstruct sockaddrsizning manzili haqida ma'lumot o'z ichiga olgan bir pointer emasmi, ya'ni, port va IP-manzil. addrlendeb manzili bayt uzunligi hisoblanadi.

Kishsir. bi Bu bir bo'lakka singib ketishiga sal qoldi. Dastur ustida ishlayotgan xost uchun socket bog'laydi misol bor bo'lsin, port 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);

Bayroqni ishlatibAI_PASSIVE, dasturni ishlayotgan xostning ipiga bog'lash uchun aytaman. Agar ma'lum bir mahalliy IP-manzil bog'lab bo'lsangiz, tomchi AI_PASSIVEva birinchi argument uchun IP-manzil qo'yish getaddrinfo().

bind() bundan tashqari, xato qaytadi -1va xato qiymatiga o'rnatadierrno.

Eski kod ko'p qo'lda struct sockaddr_inchaqirib oldin paketlarnibind(). Shubhasiz, bu IPv4-maxsus, lekin, albatta, hech narsa IPv6 bilan bir xil narsa qilib sizni to'xtatish yo'q, foydalanish getaddrinfo()oson bo'ladi tashqari, odatda. Yaxshiyamki, eski kod bu kabi ko'rinadi:

// !!! 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);

Yuqoridagi kodda INADDR_ANYs_addrmahalliy IP-manzilingizga (bayroq kabi, yuqorida) bog'lashni xohlasangiz, siz ham maydonga tayinlashingiz AI_PASSIVEmumkin. Ning IPv6 versiyasi INADDR_ANYin6addr_anysohasida ichiga beriladi global o'zgaruvchilar sin6_addrsizning struct sockaddr_in6. (IN6ADDR_ANY_INITAgar o'zgarmaydigan initializator foydalanishingiz mumkin, bir so'l ham bor.)

Qo'ng'iroq paytida uchun amalga tomosha qilish uchun yana bir narsabind(): & # 8217; t port sizning raqamlar bilan underboard borish yo'q. Quyida barcha portlar 1024 himoyalangan (agar superuser ekansiz ekan)! Siz yuqorida har qanday port raqami bo'lishi mumkin, o'ng qadar 65535 (ular allaqachon boshqa dastur tomonidan ishlatiladigan emas taqdim).

Ba'zan, agar farq mumkin, agar server rerun harakat va bind()muvaffaqiyatsiz, da'vo " foydalanish allaqachon Manzili."Bu nimani anglatadi? Xullas, bir oz ulangan rozetka hali ham yadroda osilib turibdi va portni ushlab turibdi. Siz uni tozalash uchun kutish mumkin (bir daqiqa yoki shunday), yoki u port qayta imkonini beruvchi dastur kodni kiritish, bu kabi:

 

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);
} 

Haqida bir kichik qo'shimcha yakuniy eslatmabind(): agar juda uni chaqirish kerak bo'lmaydi vaqtlar bor. Agar connect()uzoq mashina ing va siz mahalliy port nima farqi yo'q bo'lsa (telnetagar faqat uzoq port haqida g'amxo'rlik qaerda bo'lgani kabi), agar shunchaki qo'ng'iroq mumkinconnect(), u socket erkin bo'lsa ko'rish uchun nazorat olaman, va bind()zarur bo'lsa, bir foydalanilmagan mahalliy portga uni bo'ladi.

connect()- Salom, siz!

Keling, bir necha daqiqaga telnet dasturini ko'rib chiqaylik. Sizning foydalanuvchingiz sizga (xuddi TRON filmidagi kabi ) socket file deskriptorini olishni buyuradi. Siz rioya va qo'ng'iroq socket(). Keyin foydalanuvchi sizga "10.12.110.57"portda "23" (standart telnet porti) ga ulanishni aytadi. Yow! Endi nima qilasiz?

Siz uchun Lucky, dastur, agar connect()uzoq xost ulanish uchun qanday-siz hozir bo'limiga perusing qilyapmiz. Shunday ekan, olg'a jo'yali o'qing! Yo'qotishga vaqt yo'q!

connect()Qo'ng'iroq quyidagicha:

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

sockfd bizning do'stona mahalla socket fayl identifikatori emasmi, socket()qo'ng'iroq bilan qaytib, serv_addrbir struct sockaddrmaqsad port va IP manzilini o'z ichiga olgan bo'lib,, va addrlenserver manzili tuzilishi bayt uzunligi.

Bu ma'lumotlar barcha qo'ng'iroq natijalari dan gleaned bo'lishi mumkingetaddrinfo(), qaysi jinslar.

Bu ko'proq mantiqiy boshlaydi? Men bu yerdan sizni eshita olmayman, shuning uchun umid qilishim kerak. "www.example.com", Portga rozetkaga ulanadigan joyga misol keltiraylik 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);

Yana, eski-maktab dasturlari struct sockaddr_inuchun o'tishi uchun o'z s to'ldirilgan connect(). Agar xohlasangiz, buni qilishingiz mumkin. bind()Yuqoridagi, bo'limda o'xshash eslatma qarang.

Dan qaytish qiymatini tekshirish uchun ishonch connect()hosil qiling-u -1xato qaytadi va o'zgaruvchini o'rnatish olaman errno.

 

Bundan tashqari, biz qo'ng'iroq qilmadi e'tibor beringbind(). Asosan, biz mahalliy port raqami haqida g'amxo'rlik yo'q; biz faqat biz fiz qaerda g'amxo'rlik (uzoq port). Yadro biz uchun mahalliy portni tanlaydi va biz ulanadigan sayt avtomatik ravishda bu ma'lumotni bizdan oladi. Hech qanday tashvish.

listen()- Kimdir meni chaqirishni ma'qul ko'radimi?

Ok, sur'atlar o'zgarishi uchun vaqt. Nima siz uzoq xost ulanish istamasangiz. Aytish, faqat tepishar uchun, kiruvchi ulanishlar uchun kutish va ba'zi tarzda ularni band istayman, deb. Jarayon ikki qadamdir: avval siz listen(), keyin siz accept()(pastga qarang).

Tinglash qo'ng'irog'i juda oddiy, ammo biroz tushuntirishni talab qiladi:

    int listen(int sockfd, int backlog); 

sockfd tizim qo'ng'irog'idan odatiy socket fayl identifikatori bormisocket()backlogulanishlar soni kiruvchi navbatda ruxsat etiladi. Bu nimani anglatadi? Xo'sh, kiruvchi ulanishlar siz ularni qadar bu navbatda kutish uchun boryapmiz accept()(pastroqqa qarang) va bu navbatda mumkin qancha chegarasi bo'ladi. Eng tizimlari jimgina haqida bu raqamni cheklash 20; ehtimol, yoki uni belgilash bilan yuz olishingiz mumkin 510.

Yana, odatdagidek, listen()qaytadi -1va errnoxato sozlash.

Ehtimol, tasavvur qila oladigan bo'lsak, biz bind()qo'ng'iroq qilishdan oldin qo'ng'iroq qilishimiz keraklisten(), shunda server ma'lum bir portda ishlaydi. (Siz ulanish uchun port sizning do'stlari aytib imkoniyatiga ega bo'lish uchun kerak!) Bas, siz kiruvchi ulanishlar uchun tinglash bo'ladi-ku, agar, tizim qo'ng'iroqlar ketma-ketligi siz qilish olaman:

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

Men buni namuna kodi o'rnida qoldiraman, chunki bu juda aniq. (Bo'limdagi kodaccept(), quyida, to'liqroq.) Bu butun sha-bang, albatta, qiyin qismi uchun qo'ng'iroq bo'ladi accept().

accept()- "Port qo'ng'iroq uchun rahmat 3490.”

Tayyor qiling-accept()qo'ng'iroq kinda g'alati! Nima sodir bo'ladi, bu: uzoq kimdir connect()siz ing bir port ustida mashina uchun harakat qiladilisten(). Ularning ulanish ed bo'lishi kutib navbatga accept()bo'ladi. Siz qo'ng'iroq accept()va siz u kutayotgan ulanish olish uchun aytib. Bu yagona ulanish uchun foydalanish uchun sizga yangi socket fayl identifikatorini qaytarib olaman! Bu to'g'ri, birdan siz bir bahoga ikki socket fayl identifikatorlari bor! Original bir hali yana yangi ulanishlar uchun tinglash, va yangi yaratilgan bir nihoyat tayyor send()va recv(). Biz bormiz-ku!

Qo'ng'iroq quyidagicha:

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

sockfd hisoblanadi listen()ing socket descriptor. Etarli oson. addrodatda mahalliy uchun pointer bo'ladi struct sockaddr_storage. Bu yerga kiruvchi ulanish haqida ma'lumot ketadi (va u bilan qaysi host sizni qaysi portdan chaqirayotganini aniqlashingiz mumkin). addrlenuning manzili uzatiladi oldin o'rnatilgan bo'lishi kerak, bir mahalliy integer o'zgaruvchilar sizeof(struct sockaddr_storage)accept()accept()bundan ortiq ko'p baytlarni ichiga solib bo'lmaydi addr. Agar u kamroq bo'lsa, u buni addrlenaks ettirish uchun qiymatini o'zgartiradi.

Nima taxmin? accept()-1errnoxato sodir bo'lsa qaytadi va sozlash. Betcha buni fahmlamadi.

Avvalgidek, bu bir bo'lakni yutish uchun shamlardan iborat, shuning uchun perusal uchun namuna kodi fragmenti:

#include 
#include 
#include 
#include 

#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!
    .
    .
    .

Shunga qaramay, biz new_fdbarcha send()va qo'ng'iroqlar uchun socket identifikatorini ishlatamizrecv(). Agar faqat bitta ulanish hech olish close()sockfdbo'lsangiz, agar shu portiga ko'proq kiruvchi ulanishlar oldini olish uchun tinglash mumkin, agar shunday orzu bo'lsa.

send() varecv()-men bilan gaplashing, bolam!

Bu ikki vazifalari oqim sockets yoki ulangan datagram sockets orqali muloqot uchun. Agar muntazam unconnected datagram sockets foydalanish bo'lsangiz , agar bo'limiga ko'rish uchun kerak bo'ladi sendto()va recvfrom(), quyida.

Oramazon. edusend(). qa:

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

sockfd siz ma'lumot yubormoqchi bo'lgan socket identifikatori (u qaytib kelganmi socket()yoki siz bilan accept()bo'lganmi ). msgagar yubormoqchi bo'lgan ma'lumotlarga bir pointer, va lenbayt deb ma'lumotlar uzunligi. Faqat o'rnatilgan flags0. (send()Bayroqlar haqida qo'shimcha ma'lumot olish uchun odam sahifasiga qarang.)

Ba'zi namuna kodi bo'lishi mumkin:

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

send() aslida yuborilgan bayt sonini qaytaradi-bu siz yuborish aytdim soni kamroq bo'lishi mumkin! Qarang, ba'zan ma'lumotlar butun bir gob yuborish uchun uni aytib va u faqat uni qo'yish mumkin emas. U & # 8217; ll ma'lumotlar kabi ko'p olov off mumkin, va siz qolgan keyinchalik yuborish uchun ishonch. Eslab, tomonidan qaytib qiymati send()qiymatini mos emas, agarlen, bu mag'lubiyatga qolgan yuborish uchun sizga qolgan. Yaxshi xabar bu: agar paket kichik bo'lsa (1K dan kam yoki shunga o'xshash), ehtimol, hamma narsani bir yo'la jo'natishga muvaffaq bo'ladi. Yana, -1xato qaytariladi, va errnoxato raqamiga o'rnatiladi.

recv()Qo'ng'iroq ko'p jihatdan o'xshash:

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

sockfd o'qish uchun socket deskriptor bufemasmi, ichiga ma'lumot o'qish uchun bufer emas, lenbufer maksimal uzunligi, va flagsyana o'rnatish mumkin 0. (recv()Bayroq ma'lumot uchun odam sahifasiga qarang.)

recv() aslida bufer ichiga o'qib bayt sonini qaytaradi, yoki -1xato (errnomajmui bilan, shunga ko'ra).

Kuting! recv()qaytishi mumkin0. Bu faqat bir narsani anglatishi mumkin: uzoq tomon sizga ulanishni yopdi! 0recv()Agar bu sodir bo'ldi bilaman kirgizib bir qaytish qiymati " s yo'li.

U erda, bu oson edi, u emas edi? Siz hozir oqim sockets oldinga va orqaga ma'lumotlarni o'tishi mumkin! Whee! Unix tarmoq dasturchisi ekansiz!

sendto() va recvfrom()—Gapirish uchun menga DGRAM-style

"Bu barcha yaxshi va dandy emas, "men sizga deb eshitish," lekin qaerda, bu unconnected datagram sockets bilan meni tark qilmaydi?"Hech problemo, amigo. Bizda faqat narsa bor.

Datagram sockets uzoq xost ulangan emas, chunki, biz bir paketini yuborish oldin biz berish kerak axborot qaysi parcha taxmin? Bu to'g'ri! Manzil manzili! Mana köpchilik:

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

Ko'rib turganingizdek, bu qo'ng'iroq asosan send()axborot ikki boshqa dona qo'shilishi bilan qo'ng'iroq bir xil bo'ladi. toa uchun pointer bo'ladi struct sockaddr(ehtimol, boshqa struct sockaddr_instruct sockaddr_in6bo'ladi yoki struct sockaddr_storageoxirgi daqiqada tashlab, deb) qaysi manzil IP-manzil va port o'z ichiga oladi. tolenintchuqur-pastga, shunchaki sizeof *toyoki o'rnatish mumkin sizeof(struct sockaddr_storage).

Manzil manzil tuzilishi haqida qo'llarini olish uchun, ehtimol, yo uni getaddrinfo()olasiz, yoki, danrecvfrom(), quyida, yoki qo'l bilan uni to'ldiring olaman.

Faqat bilan kabisend()sendto()aslida yuborilgan bayt sonini qaytaradi (qaysi, yana, agar yuborish aytdim bayt soni kamroq bo'lishi mumkin!), yoki -1xato haqida.

Teng o'xshash recv()varecvfrom(). Referat isrecvfrom():

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

Shunga qaramay, bu xuddi recv()er-xotin maydonlarining qo'shilishi kabi. fromstruct sockaddr_storagekelib mashina IP-manzil va port bilan to'ldiriladi mahalliy uchun ko'rsatkich hisoblanadi. fromlenuchun intboshlangan bo'lishi kerak mahalliy uchun pointer sizeof *fromyokisizeof(struct sockaddr_storage). Qachon vazifasi qaytadi, fromlenaslida saqlanadi manzili uzunligini o'z ichiga fromoladi .

recvfrom() qabul bayt sonini qaytaradi, yoki -1xato haqida (errnoshunga ko'ra majmui bilan).

Shunday qilib, bu erda bir savol: nima uchun biz struct sockaddr_storagesocket turi sifatida foydalanasiz? Nima uchun emas, balki struct sockaddr_in? Chunki, ko'rib turibsizki, o'zimizni IPv4 yoki IPv6 ga bog'lab qo'ymaslikni istaymiz. Shunday struct sockaddr_storageqilib, biz ham etarlicha katta bo'ladi bilaman umumiy foydalanish.

(Shunday qilib... bu erda yana bir savol: nima struct sockaddruchun har qanday manzil uchun etarlicha katta emas? Umumiy-maqsadni ham umumiy-maqsadga tashlaymizstruct sockaddr_storagestruct sockaddr! Tashqi va ortiqcha ko'rinadi, huh. Javob: bu etarli darajada katta emas va bu nuqtada uni o'zgartirish muammoli bo'lishini taxmin qilaman. Shunday qilib, ular yangisini yaratdilar.)

Eslab, agar connect()datagram socket bo'lsa, agar shunchaki foydalanish send()va recv()barcha operatsiyalar uchun mumkin. Rozetkaning o'zi hali ham datagram rozetkasi va paketlar hali UDPDAN foydalanadi, ammo socket interfeysi siz uchun maqsad va manba ma'lumotlarini avtomatik ravishda qo'shadi.

close() vashutdown()-outta yuzimni oling!

Kishnagan! Siz send()recv()kun bo'yi ing va ing ma'lumotlarini oldingiz va uni oldingiz. Siz socket identifikatoriga ulanishni yopishga tayyormiz. Bu oson. Siz faqat muntazam Unix fayl deskriptor close()vazifasini foydalanishingiz mumkin:

    close(sockfd); 

Bu har qanday qo'shimcha o'qiydi va rozetkaga yozadi oldini oladi. Masofadan turib rozetkani o'qish yoki yozishga harakat qilgan har bir kishi xato qiladi.

Faqat holda siz socket yopiladi qanday ustidan bir oz ko'proq nazorat istayman, agar shutdown()vazifasini foydalanishingiz mumkin. Bu siz ma'lum bir yo'nalishda muloqot kesib imkonini beradi, yoki har ikki yo'l (faqat kabiclose()). Synopsis:

    int shutdown(int sockfd, int how); 

sockfd socket fayl deskriptor siz o'chirish uchun kerakli bo'lgan, va howquyidagilardan biri hisoblanadi:

how Ta'siri
0 Disallowed yanada o'tdi.
1 Disallowed yanada yuboradi
2 Yanada yuboradi va qabul disallowed etiladi (kabi close())

shutdown() 0muvaffaqiyat qaytadi, va -1xato haqida (errnoshunga ko'ra majmui bilan).

Agar shutdown()unconnected datagram sockets foydalanish deign bo'lsa, u shunchaki yanada send()va qo'ng'iroqlar uchun soket mavjud recv()emas qiladi (agar siz datagram soket, agar siz bu foydalanishingiz mumkin, deb eslaymanconnect()).

Bu shutdown()aslida fayl identifikatorini yopish emas ta'kidlash muhim—u faqat uning mavjudligini o'zgartiradi. Soket identifikatorini ozod qilish uchun siz foydalanishingiz kerak close().

Unga hech narsa.

(Tashqari siz o'rniga qo'ng'iroq kerak Windows va Winsock yordamida bo'lsangiz, deb eslash closesocket()close().)

getpeername()—Siz kimsiz?

Ushbu funktsiya juda oson.

Bu juda oson, men deyarli o'z qismini bermadim. Lekin bu erda baribir.

Funktsiya getpeername()ulangan oqim rozetkaning boshqa uchida kim sizga aytaman. Bu synopsis:

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

sockfd ulangan oqim rozetkaga deskriptor bo'ladi, addra uchun pointer struct sockaddr(yoki a struct sockaddr_in) bu ulanish boshqa tomoni haqida ma'lumot o'tkazadi , va addrlenbir ko'rsatgich hisoblanadiint, deb sizeof *addryoki boshlash kerak sizeof(struct sockaddr).

Funktsiya -1xato qaytadi va errnoshunga ko'ra belgilab.

Ularning manzili bor bir marta, foydalanish mumkininet_ntop(),getnameinfo(), yoki gethostbyaddr()bosma yoki ko'proq ma'lumot olish uchun. Yo'q, siz ularning login nomini olish mumkin emas. (Ok, ok. Boshqa kompyuter bir ident daemon ishlayotgan bo'lsa, bu mumkin. Biroq, bu hujjat doirasidan tashqarida. RFC tekshiring 1413 qo'shimcha ma'lumot olish uchun.)

gethostname()—Men kimman?

Vazifasi ham osongetpeername()gethostname(). Dasturingiz ishlayotgan kompyuter nomini qaytaradi. Nomi keyin tomonidan foydalanish gethostbyname()mumkin , quyida, mahalliy mashina IP manzilini aniqlash uchun.

Nima ko'proq qiziqarli bo'lishi mumkin? Men bir necha narsalarni o'ylab mumkin, lekin ular socket dasturlash uchun tegishli emas. Baribir mana borini:

    #include 
    
    int gethostname(char *hostname, size_t size); 

Vajlari oddiy: hostnamefunktsiya qaytib ustiga hostname o'z ichiga oladi chars bir qator uchun pointer bo'ladi, va sizehostnameqator bayt uzunligi.

Funktsiya 0muvaffaqiyatli tugagandan qaytadi, va -1xato haqida, errnoodatdagidek belgilash.

Mijoz-Server Fon

Bu mijoz-server dunyo, chaqaloq. Faqat tarmoq haqida hamma narsa server jarayonlar va aksincha bilan gaplashib mijoz jarayonlari bilan shug'ullanadi. telnetMisol uchun, oling. Agar port ustida uzoq xost ulanish qachon 23 telnet bilan (mijoz), deb xost ustida bir dastur (chaqirditelnetd, server) hayotga springs. Kiruvchi telnet ulanishini boshqaradi, sizni kirish xohishi bilan o'rnatadi va hokazo.

Mijoz-Server O'zaro Ta'siri.

Mijoz va server o'rtasida axborot almashinuvi yuqoridagi diagrammada umumlashtiriladi.

Mijoz-server juft gapirish mumkin unutmangSOCK_STREAM,SOCK_DGRAM, yoki boshqa hech narsa (modomiki ular bir xil narsa gapirgan qilyapmiz, deb). Mijoz-server juft ba'zi yaxshi misollar telnetbor/telnetd,ftp/ftpd, yokiFirefox/Apache. Agar foydalanish har safarftp, uzoq dastur bor,ftpd, bu sizga xizmat.

Ko'pincha, faqat bir mashina ustida bir server mavjud bo'ladi, va bu server yordamida bir necha mijozlar band bo'ladi fork(). Asosiy muntazam: server ulanishni kutadi, accept()u va fork()bola jarayoni uni boshqaradi. Bu bizning namuna server keyingi bo'limda nima qiladi.

Oddiy Oqim Serveri

Barcha bu server qilsa Hello, world!bir oqim ulanish orqali string "" chiqib yuborish. Bu serverni sinash uchun barcha qilish kerak bir oynada uni ishga tushirish, va boshqa unga telnet bilan:

    $ telnet remotehostname 3490

remotehostnamemashinani nomi qani uni ustida ishlayapsizmi.

Server kodeksining:

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;
}

Agar qiziquvchan odamsiz, men uchun bir katta main()vazifaga kodni bor (men his) sintaktik aniqlik. Agar yaxshi his qiladi, agar kichik vazifalari uni bo'linib uchun bilishadi.

(Bundan tashqari, bu butun sigaction()narsa siz uchun yangi bo'lishi mumkin—bu yaxshi. Bor ekan kodi Ed bola chiqish jarayonlari sifatida paydo zombi jarayonlari o'rib uchun mas'ul fork()bo'lgan. Agar zombi ko'p qilish va ularni o'rib bo'lmasa, tizim administratori hayajonlangan bo'ladi.)

Ma'lumotlarni keyingi bo'limda keltirilgan mijoz yordamida ushbu serverdan olishingiz mumkin.

Oddiy Oqim Mijozi

Bu odam serverdan ham oson ekan. Barcha bu mijoz qilsa siz buyruq qatorni belgilash xost ulanadi, port 3490. Bu server yuboradi mag'lubiyatga oladi.

Mijoz manba:

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#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;
}

Agar mijoz ishlatish oldin server ishlatish bo'lmasa e'tibor bering, connect()qaytadi "aloqa rad". Juda foydali.

Datagram Soket

Biz allaqachon bizning muhokama bilan UDP datagram sockets asoslarini qoplangan sendto()ayting varecvfrom(), yuqorida, bas, men faqat namuna dasturlari bir necha taqdim olaman: talker.cva listener.c.

listener portiga kiruvchi paketi kutib bir mashina ustida o'tiradi 4950. talkerbu portiga bir dasta yuboradi, belgilangan mashina ustida, foydalanuvchi buyruq qatorni kiradi baribir o'z ichiga oladi.

25 uchun manba listener.c:

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;
}

E'tibor bering, bizning da'vatimizda getaddrinfo()biz nihoyat foydalanmoqdamiz SOCK_DGRAM. Bundan tashqari, hech qanday ehtiyoj bor, deb qayd listen()yoki accept(). Bu unconnected datagram sockets foydalanish bonusi biridir!

Keyingi 26 uchun manba keladi talker.c:

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;
}

Va buning hammasi bor! listenerBa'zi mashina ustida ishga tushirish, keyin boshqa ishlatishtalker. Ularni muloqot tomosha qiling! Butun yadroviy oila uchun qiziqarli g-ball hayajon!

Bu safar ham serverni ishlatish shart emas! Siz talkero'zi tomonidan ishlatish mumkin, va u faqat baxtli hech kim boshqa tomonida bir bilan tayyor bo'lsa, ular yo'qoladi efir ichiga paketlarini off olovrecvfrom(). Eslab: UDP datagram sockets yordamida yuborilgan ma'lumotlar kelishi kafolatlanmagan!

Men o'tmishda ko'p marta zikr ayting yana bir kichik batafsil tashqari: ulangan datagram sockets. Men bu erda bu haqida gapirish kerak, biz hujjatning datagram bo'limida odamsiz, chunki. Ning talkerqo'ng'iroqlar connect()va listener" s manzilini bildiradi, deb aytaylik. Shu nuqtadan boshlab, talkerfaqat yuborilgan va belgilangan manzildan olish mumkin connect(). Shuning uchun, siz foydalanish shart emas sendto()va recvfrom(); siz shunchaki foydalanishingiz mumkin send()va recv().

Biroz Ilg'or Texnikalar

Bu, albatta, rivojlangan emas, lekin ular biz allaqachon qoplangan ayting ko'proq asosiy darajada chiqib qolibsan. Aslida, agar siz & # 8217; ve bu uzoq kazanılmış bo'lsa, Unix tarmoq dasturlash asoslari o'zingizni juda amalga tushunmoq kerak! Tabriklayman!

Shunday qilib, bu erda biz siz sockets haqida bilib olishingiz mumkin ko'proq ezoterik narsalar ba'zi Jasur yangi dunyoga borib. Unda bor!

To'sish

To'sish. Siz bu haqda eshitgansiz - endi heck nima? Mohiyatiga ko'ra, " blok ""uyqu" uchun techie jargon hisoblanadi. Siz , ehtimol, siz paytida payqadimlistener, yuqorida, bir paketi keldi qadar u faqat u erda o'tiradi. Nima bo'ldi , deb ataladirecvfrom(), ma'lumotlar yo'q edi va recvfrom()ba'zi ma'lumotlar kelgunga qadar "blok" (ya'ni u erda uxlash) deb aytiladi.

Ko'p funksiyalar bloki. accept() bloklari. Barcha recv()funksiyalar bloki. Ular ruxsat qilyapmiz, chunki ular bu, albatta, mumkin sababi. Agar birinchi bilan socket identifikatorini yaratish socket()qachon, kernel bloklash uchun uni sozlash. Agar siz rozetkani bloklashni istamasangiz, siz qo'ng'iroq qilishingiz kerak fcntl():

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

Blokirovkalash uchun rozetkani o'rnatib, ma'lumot uchun rozetkani samarali "so'rov" qilishingiz mumkin. Agar siz blokirovka qilmaydigan rozetkadan o'qishga harakat qilsangiz va u erda hech qanday ma'lumot bo'lmasa, bloklash mumkin emas-u qaytadi -1va errnoor—ga o'rnatiladi EAGAINEWOULDBLOCK.

(Kuting-u qaytib kelishi mumkinmi EAGAIN yoki EWOULDBLOCK? Qaysi nazorat qilasiz? Spetsifikatsiyada sizning tizimingiz qaytib kelishi aniqlanmagan, shuning uchun portativlik uchun ularni tekshiring.)

Umuman olganda, ammo, saylov bu turi yomon fikr emas. Agar band dastur qo'yish bo'lsa-rozetkaga ma'lumotlarni izlab kutib turing, bu uslub chiqib ketayotganini kabi CPU vaqt so'rish olaman. O'qish uchun kutayotgan ma'lumotlar mavjudligini tekshirish uchun yanada oqlangan echim quyidagi bo'limda keltirilgan poll().

poll()—Synchronous I/O Multiplexing

Nima, albatta, siz, albatta, ega bo'lish istayman negadir bir vaqtning o'zida soket bir guruh kuzatib borish va keyin ma'lumotlar tayyor bor kishilar band bo'ladi. Bu yo'l siz don & # 8217; t doimiy ravishda o'qish uchun tayyor bo'lgan ko'rish uchun barcha kishilar sockets poll kerak.

Ogohlantirish bir so'z: poll()bu ulanishlar ulkan sonlar kelganda dahshatli sekin. Bunday holatlarda siz libevent 27 kabi voqealar kutubxonasidan yaxshiroq ishlashga erishasiz, bu sizning tizimingizda eng tezkor usuldan foydalanishga urinadi.

Xo'sh, qanday qilib saylov oldini olish mumkin? Emas, balki bir oz kinoya, agar poll()tizim qo'ng'iroq yordamida saylov oldini olish mumkin. Qisqacha aytganda, biz operatsion tizimdan biz uchun barcha iflos ishlarni qilishni so'raymiz va ba'zi ma'lumotlar qaysi soketlarda o'qishga tayyor bo'lsa, bizga xabar bering. Shu bilan birga, bizning jarayon tizimi resurslarini tejash, uyqu borish mumkin.

Umumiy gameplan struct pollfdbiz kuzatib istayman qaysi socket identifikatorlari haqida ma " lumot bilan s bir qator ushlab qolish uchun emas, va biz uchun kuzatish uchun kerakli voqealar qanday. poll()O'sha voqealar biri sodir qadar OS qo'ng'iroq blokirovka qiladi (masalan, " o'qish uchun tayyor socket!") yoki foydalanuvchi belgilangan vaqt o'tishi sodir bo'lguncha.

Foydali, a listen()ing socket qaytadi" o'qish uchun tayyor " yangi kiruvchi aloqa ed bo'lishi tayyor bo'lsaaccept().

Bu etarli hazil. Buni qanday ishlatamiz?

    #include 
    
    int poll(struct pollfd fds[], nfds_t nfds, int timeout);

fds axborot bizning array emasmi (qaysi sockets nima uchun kuzatib borish uchun), nfdsqator elementlar soni, va timeoutmillisekundlarda bir vaqt. Bu voqea sodir bo'lgan qator elementlar sonini qaytaradi.

Shunga bir qaraylik struct:

 

    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
    };

Shunday qilib, biz bir qator bor fiz, va biz fdmonitoring manfaatdor odamsiz bir socket identifikatori uchun har bir element uchun maydon ko'rasiz. Va keyin biz eventsqiziqqan voqealar turini ko'rsatish uchun maydonni o'rnatamiz.

eventsMaydon bitwise-yoki quyidagilardan iborat:

Ibrokhimov Dilshodbek Yuldashev
POLLIN Ma'lumotlar ushbu rozetkaga tayyor bo'lganda meni ogohlantiringrecv().
POLLOUT send()Men blokirovka holda bu rozetkaga ma'lumotlarni mumkin, meni ogohlantirish.

Agar tartibda s sizning qator bor bir marta struct pollfd, keyin siz uni o'tishi mumkinpoll(), bundan tashqari, qator hajmini o'tib, shuningdek millisekundlarda bir vaqt qiymati sifatida. (Abadiy kutish uchun salbiy vaqtni belgilashingiz mumkin.)

poll()Qaytib keyin, agar reventsPOLLINyoki o'rnatilgan bo'lsa, ko'rish uchun maydon tekshirish mumkinPOLLOUT, bu voqea sodir ko'rsatgan.

(Agar qo'ng'iroq bilan, albatta, mumkin, deb aslida ko'proq borpoll()poll()Man sahifasiga qarang, quyida, batafsil ma'lumot uchun.)

Mana misol ma'lumotlarni standart kiritishdan o'qishga tayyor bo'lishi uchun 2.5 sekund kutib qolamiz yani urganingizda RETURN:

#include 
#include 

int main(void)
{
    struct pollfd pfds[1]; // More if you want to monitor more

    pfds[0].fd = 0;          // Standard input
    pfds[0].events = POLLIN; // Tell me when ready to read

    // If you needed to monitor other things, as well:
    //pfds[1].fd = some_socket; // Some socket descriptor
    //pfds[1].events = POLLIN;  // Tell me when ready to read

    printf("Hit RETURN or wait 2.5 seconds for timeout\n");

    int num_events = poll(pfds, 1, 2500); // 2.5 second timeout

    if (num_events == 0) {
        printf("Poll timed out!\n");
    } else {
        int pollin_happened = pfds[0].revents & POLLIN;

        if (pollin_happened) {
            printf("File descriptor %d is ready to read\n", pfds[0].fd);
        } else {
            printf("Unexpected event occurred: %d\n", pfds[0].revents);
        }
    }

    return 0;
}

poll()pfdsVoqealar sodir bo'lgan qator elementlar sonini qaytaradi yana e'tibor bering. Bu qator qaysi elementlar sizga aytmaydi (agar siz hali ham buning uchun harakatlanish kerak), lekin u bir non-nol maydon bor qancha yozuvlari sizga aytib reventsbermaydi (bas, siz bu ko'p topish keyin skanerlash to'xtatish mumkin).

Bu erda bir nechta savol tug'ilishi mumkin :men qanday qilib yangi fayl identifikatorlarini qo'shaman?poll() Buning uchun, shunchaki siz kerak bo'lgan barcha uchun qator etarli joy bor ishonch hosil qiling,yoki realloc()ko'proq joy kerak.

To'plamdagi narsalarni o'chirish haqida nima deyish mumkin? Buning uchun siz massivdagi oxirgi elementni o'chirib tashlayotganingizdan nusxa olishingiz mumkin. Va keyin soni bir kam o'tishi poll(). Yana bir variant siz fdsalbiy raqamiga har qanday maydon o'rnatishingiz mumkin va poll()uni e'tiborsizlik qiladi, deb.

Qanday qilib biz siz uchun mumkin chat serverga barcha birga qo'yish mumkintelnet?

Nima, biz, albatta, olaman bir tinglovchi soket boshlash, va fayl identifikatorlari majmui uni kiritish uchun poll(). (Bu kirish aloqasi bor qachon tayyor-to-o'qib ko'rsatadi.)

Keyin qatorimizga yangi ulanishlar qo'shamizstruct pollfd. Va agar biz kosmosdan chiqib ketsak, uni jadal rivojlantiramiz.

Agar ulanish yopiq bo'lsa, biz qator uni olib tashlash olaman.

Va ulanish tayyor bo'lganda, biz undan ma'lumotlarni o'qiymiz va boshqa barcha ulanishlarga ma'lumotlarni yuboramiz, shuning uchun ular boshqa foydalanuvchilarning yozganlarini ko'rishlari mumkin.

Shunday qilib, bu so'rov server berish bir harakat qilib ko'ring. Bir oynada uni ishga tushirish,keyin telnet localhost 9034boshqa terminal windows bir qator. Siz boshqa birida bir oynada kiriting nima ko'rish imkoniyatiga ega bo'lishi kerak (agar qaytib urdi keyin).

Faqat bu emas, lekin siz urdi CTRL-]va quitchiqish uchun yozing telnet, agar, server uzilishini aniqlash va fayl identifikatorlari qator sizni olib tashlash kerak.

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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);
}

// Return a listening socket
int get_listener_socket(void)
{
    int listener;     // Listening socket descriptor
    int yes=1;        // For setsockopt() SO_REUSEADDR, below
    int rv;

    struct addrinfo hints, *ai, *p;

    // 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) {
        return -1;
    }

    freeaddrinfo(ai); // All done with this

    // Listen
    if (listen(listener, 10) == -1) {
        return -1;
    }

    return listener;
}

// Add a new file descriptor to the set
void add_to_pfds(struct pollfd *pfds[], int newfd, int *fd_count, int *fd_size)
{
    // If we don't have room, add more space in the pfds array
    if (*fd_count == *fd_size) {
        *fd_size *= 2; // Double it

        *pfds = realloc(*pfds, sizeof(**pfds) * (*fd_size));
    }

    (*pfds)[*fd_count].fd = newfd;
    (*pfds)[*fd_count].events = POLLIN; // Check ready-to-read

    (*fd_count)++;
}

// Remove an index from the set
void del_from_pfds(struct pollfd pfds[], int i, int *fd_count)
{
    // Copy the one from the end over this one
    pfds[i] = pfds[*fd_count-1];

    (*fd_count)--;
}

// Main
int main(void)
{
    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

    char remoteIP[INET6_ADDRSTRLEN];

    // Start off with room for 5 connections
    // (We'll realloc as necessary)
    int fd_count = 0;
    int fd_size = 5;
    struct pollfd *pfds = malloc(sizeof *pfds * fd_size);

    // Set up and get a listening socket
    listener = get_listener_socket();

    if (listener == -1) {
        fprintf(stderr, "error getting listening socket\n");
        exit(1);
    }

    // Add the listener to set
    pfds[0].fd = listener;
    pfds[0].events = POLLIN; // Report ready to read on incoming connection

    fd_count = 1; // For the listener

    // Main loop
    for(;;) {
        int poll_count = poll(pfds, fd_count, -1);

        if (poll_count == -1) {
            perror("poll");
            exit(1);
        }

        // Run through the existing connections looking for data to read
        for(int i = 0; i < fd_count; i++) {

            // Check if someone's ready to read
            if (pfds[i].revents & POLLIN) { // We got one!!

                if (pfds[i].fd == listener) {
                    // If listener is ready to read, handle new connection

                    addrlen = sizeof remoteaddr;
                    newfd = accept(listener,
                        (struct sockaddr *)&remoteaddr,
                        &addrlen);

                    if (newfd == -1) {
                        perror("accept");
                    } else {
                        add_to_pfds(&pfds, newfd, &fd_count, &fd_size);

                        printf("pollserver: new connection from %s on "
                            "socket %d\n",
                            inet_ntop(remoteaddr.ss_family,
                                get_in_addr((struct sockaddr*)&remoteaddr),
                                remoteIP, INET6_ADDRSTRLEN),
                            newfd);
                    }
                } else {
                    // If not the listener, we're just a regular client
                    int nbytes = recv(pfds[i].fd, buf, sizeof buf, 0);

                    int sender_fd = pfds[i].fd;

                    if (nbytes <= 0) {
                        // Got error or connection closed by client
                        if (nbytes == 0) {
                            // Connection closed
                            printf("pollserver: socket %d hung up\n", sender_fd);
                        } else {
                            perror("recv");
                        }

                        close(pfds[i].fd); // Bye!

                        del_from_pfds(pfds, i, &fd_count);

                    } else {
                        // We got some good data from a client

                        for(int j = 0; j < fd_count; j++) {
                            // Send to everyone!
                            int dest_fd = pfds[j].fd;

                            // Except the listener and ourselves
                            if (dest_fd != listener && dest_fd != sender_fd) {
                                if (send(dest_fd, buf, nbytes, 0) == -1) {
                                    perror("send");
                                }
                            }
                        }
                    }
                } // END handle data from client
            } // END got ready-to-read from poll()
        } // END looping through file descriptors
    } // END for(;;)--and you thought it would never end!
    
    return 0;
}

Keyingi bo'limda, biz bir shunga o'xshash qarash olaman, deb nomlangan katta vazifasi select(). Har ikki select()va poll()shunga o'xshash funktsional va ishlash taklif, va faqat, albatta, ular ishlatiladi-ku qanday farq. select()bir oz ko'proq ko'chma bo'lishi mumkin, lekin, ehtimol, foydalanishda bir oz clunkier emas. Agar eng yaxshi ko'raman birini tanlang, modomiki u sizning tizimiga qo'llab-quvvatlanadi yotipti, deb.

select()—Synchronous I/O Multiplexing, Eski Maktab

Ushbu funktsiya biroz g'alati, lekin u juda foydali bo'ldi. Quyidagi vaziyatni oling: agar server va siz kiruvchi ulanishlar uchun tinglash, shuningdek, siz allaqachon bor ulanishlar o'qish tutish istayman.

Yo'q, muammo, siz aytish, faqat bir accept()va recv()s bir er-xotin. juda tez emas, buster! Qo'ng'iroqqa to'siq bo'layotgan bo'lsangiz - chiaccept()? Qanday recv()bir vaqtning o'zida ma'lumotlar uchun boryapmiz? "Non-blokirovka soket foydalaning!"Yo'l yo'q! CPU to'griligini hohlamaysiz. Nima, keyin?

select() sizga bir vaqtning o'zida bir nechta rozetkalarni kuzatish uchun kuch beradi. Bu sizga o'qish uchun tayyor bo'lganlarni aytib beraman, yozish uchun tayyor bo'lgan, va soketlari istisnolar ko'tardi qaysi, agar chindan ham, deb bilish bo'lsangiz.

Ogohlantirish bir so'z:select(), juda ko'chma bo'lsa-da,, u ulanishlar ulkan raqamlar kelganda dahshatli sekin. Bunday holatlarda siz libevent 30 kabi voqealar kutubxonasidan yaxshiroq ishlashga erishasiz, bu sizning tizimingizda eng tezkor usuldan foydalanishga harakat qiladi.

Bundan keyin ham konspektni taklif qilaman select():

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

Funktsiya monitorlar fayl deskriptorlari "silsilasini"; xususan readfdswritefds, va exceptfds. Agar standart kiritish va ba'zi socket deskriptor dan o'qish mumkin, agar ko'rishni istasangiz,sockfd, faqat fayl deskriptorlari qo'shish 0va sockfdmajmui readfdsuchun . Parametr eng numfdsyuqori fayl deskriptori plus One qiymatlariga o'rnatilishi kerak. Bu misolda, u o'rnatilgan bo'lishi kerak sockfd+1, bu standart kiritish ko'ra albatta yuqori, chunki ( 0).

Qachon select()qaytadi, readfdso'qish uchun tayyor bo'lgan tanlangan fayl identifikatorlari qaysi aks ettirish uchun tahrirlangan bo'ladi. Siz quyida, so'l bilan ularni sinab mumkinFD_ISSET().

Ancha oldinga borishdan oldin, men bu silsilasini manipulyatsiya qilish haqida gaplashamiz. Har bir to'plam turlichadir fd_set. Quyidagi macros bu turi bo'yicha faoliyat:

 

Funktsiya Dilshodbek Yuldashev
FD_SET(int fd, fd_set *set); Qo'shish fdset.
FD_CLR(int fd, fd_set *set); Olib tashlang fdset.
FD_ISSET(int fd, fd_set *set); Bo'lgan fdbo'lsa rost qaytaringset.
FD_ZERO(fd_set *set); Barcha yozuvlari tozalash set.

Nihoyat, bu amalga weirded nimastruct timeval? Xo'sh, ba'zan kimdir sizga ba'zi ma'lumotlarni yuborish uchun abadiy kutish istamayman. Ehtimol, har bir 96 soniya siz hech narsa sodir bo'lmagan bo'lsa-da, terminali uchun "hali ketadi..." chop istayman. Bu safar tuzilishi bir vaqt davri belgilash imkonini beradi. Agar vaqt oshib ketgan bo'lsa va select()hali tayyor fayl identifikatorlarini topmagan bo'lsa, u qaytib keladi, shuning uchun qayta ishlashni davom ettirishingiz mumkin.

struct timevalErgashgan joylari bor:

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

Faqat tv_seckutish uchun soniya soniga o'rnating va tv_useckutish uchun mikrosekundlar soniga o'rnating. Ha, bu _micro_seconds ekan, milliseconds emas. Millisekundda 1,000 mikrosekund va bir soniyada 1,000 millisekund mavjud. Shunday qilib, bir soniyada 1,000,000 mikrosekund bor. Nima uchun uni ""? usec "U "biz"micro" uchun foydalanish yunon harf μ (Mu) kabi qarash kerak bo'ladi. Shuningdek, qachon vazifasi qaytadi, timeout hali qolgan vaqt ko'rsatish uchun yangilangan bo'lishi mumkin. Bu siz ishlayotgan ku Unix nima lazzat bog'liq.

Yo ' - o'q! Biz microsecond qaror taymerni bor! Xo'sh, unga tayanmang. Ehtimol, qat'i nazar, siz belgilangan qanday kichik standart Unix timeslice ba'zi qismini kutish struct timevalkerak bo'ladi .

Qiziqtirgan boshqa narsalar: siz struct timevaluchun maydonlarni 0belgilangan bo'lsa , select()darhol timeout qiladi, samarali sizning fotoalbomlarda barcha fayl identifikatorlari saylov. Agar NULL parametr belgilangan timeoutbo'lsa, u vaqt hech qachon, va birinchi fayl identifikatori tayyor qadar kutadi. Nihoyat, agar ma'lum bir majmui uchun kutish haqida g'amxo'rlik bo'lmasa, agar faqat qo'ng'iroq NULL uni o'rnatishingiz select()mumkin .

Quyidagi kodi snippet standart kiritish paydo narsa uchun 2.5 soniya kutadi:

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

#include 
#include 
#include 
#include 

#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;
} 

Agar chiziq buffered terminali bo'lsangiz, agar urdi asosiy qaytish bo'lishi kerak yoki u baribir vaqt bo'ladi.

Endi, sizlardan ba'zi bu datagram rozetkaga ma'lumotlarni kutish uchun ajoyib bir yo'l, deb o'ylayman mumkin—va siz haqsiz: bu bo'lishi mumkin. Agar mahalliy odam sahifa siz uni harakat bo'lsangiz masalada deydi nima ko'rish kerak.

Ba'zi Unices struct timevalvaqt miqdorini aks ettirish uchun vaqt yangilash hali bir vaqt oldin qolgan. Lekin boshqalar yo'q. Agar ko'chma bo'lishni istasangiz, bu yuzaga tayanib qilmang. (gettimeofday()Agar o'tgan vaqt kuzatish uchun kerak bo'lsa foydalaning. Bu bamaylixotir, bilaman, lekin bu shunday.)

Agar o'qish to'plamidagi rozetka ulanishni yopsa nima bo'ladi? Xo'sh, bu holda, select()"o'qishga tayyor"deb nomlangan Socket identifikatori bilan qaytadi. Agar aslida recv()undan, albatta, qachon, recv()qaytadi 0. Bu siz mijoz aloqani yopdi bilaman qilib.

Haqida yana bir eslatmaselect(): agar sizda ing bo'lgan soket bo'lsa, listen()setda o'sha socket ning fayl deskriptorini qo'yib, yangi ulanish bor-yo'qligini tekshirib ko'rishingiz mumkinreadfds.

Va bu, do'stlarim, qudratli select()funktsiyaning tezkor ko'rinishi.

Lekin, mashhur talab bilan, bu erda chuqur misol. Afsuski, axloqsizlik o'rtasidagi farq-oddiy misol, yuqorida va bu erda muhim ahamiyatga ega. Lekin bir ko'z bor,keyin uni quyidagicha tavsifi o'qib.

Bu dastur oddiy ko'p foydalanuvchi chat server kabi harakat. Bir oynada ishlaydigan boshlang, keyin telnetunga ( "telnet hostname 9034" ) bir nechta boshqa oynalardan. Bir sessiyada biror narsa yozsangiztelnet, u boshqalarda paydo bo'lishi kerak.

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;
}

E'tibor bering, men kodi ikki fayl identifikatori silsilasini bor: masterva read_fds. Birinchi,master, hozirda ulangan barcha socket deskriptorlarini hamda yangi ulanishlar uchun tinglayotgan socket deskriptorini ushlab turadi.

Men mastermajmuini bor sababi select()aslida siz soketlari o'qishga tayyor bo'lgan aks ettirish uchun unga o'tib majmuini o'zgartiradi, deb. Men keyingi bir qo'ng'iroq dan ulanishlar kuzatib kerak beriselect(), men bir joyda xavfsiz yuz bu saqlash kerak. Oxirgi daqiqada, men masterichiga nusxaread_fds, va keyin qo'ng'iroq select().

Lekin bu degani, har safar yangi ulanishni olishim kerak, uni masterto'plamga qo'shishim kerakmi? Yup! Va har safar aloqa yopilganda, uni masterto'plamdan olib tashlashim kerakmi? Ha, shunday qiladi.

E'tibor bering, men listenersoket o'qishga tayyor bo'lganda ko'rishni tekshiraman. Qachon u, men kutayotgan yangi ulanish bor, degan ma'noni anglatadi, va men accept()uni va mastermajmui uni kiritish. Xuddi shunday, agar mijoz ulanish o'qish uchun tayyor bo'lsa , va recv()qaytadi0, men mijoz ulanish yopiq bilaman, va men majmui uni olib tashlash kerakmaster.

Agar mijoz recv()nol bo'lmagan qaytaradi, agar, garchi, ba'zi ma'lumotlar qabul qilingan bilaman. Shunday qilib, men uni olaman va keyin masterro'yxatdan o'ting va ushbu ma'lumotlarni qolgan barcha mijozlarga yuboring.

Va bu, do'stlarim, qudratli select()funktsiyaning oddiy ko'rinishi.

U erda barcha siz Linux muxlislari uchun tezkor eslatma: ba'zan, kamdan-kam hollarda, Linux ning select()"tayyor-to-o'qish" qaytib, keyin aslida o'qishga tayyor bo'lishi mumkin emas! Bu deganiread()select(), bu aytilmaganidan keyin bloklanadi! Nima uchun siz oz—! Yaxshiyamki, ishchi yechim O_NONBLOCKqabul qiluvchi rozetkaga bayroqni o'rnatishdir, shuning uchun u xatolar EWOULDBLOCK(agar u sodir bo'lsa, siz xavfsiz tarzda e'tiborsiz qoldirishingiz mumkin). fcntl()Non-to'sib uchun soket o'rnatish haqida qo'shimcha ma'lumot olish uchun mos yozuvlar sahifasiga qarang.

Bundan tashqari, bu erda bir bonus keyinchalik hisoblanadi: poll()ko'p bir xil yo'l qilsa muomala deb nomlangan yana bir vazifasi borselect(), lekin fayl identifikatori silsilasini boshqarish uchun bir xil tizimi bilan. Uni tekshiring!

Muomala Qisman send()s

Haqida bo'limida qaytarib eslab send(), yuqorida, men aytgan bo'lsasend(), siz uni so'radi barcha bayt yuborish mumkin emas, deb? Ya'ni 512 bayt yuborishni istaysiz, lekin 412 ni qaytaradi. Qolgan 100 baytga nima bo'ldi?

Yaxshi, ular & # 8217; qayta sizning oz bufer hali yuboriladi kutib. Sizning nazoratingizdan tashqari holatlar tufayli, yadro barcha ma'lumotlarni bir bo'lakka yubormaslikka qaror qildi va endi do'stim, u erda ma'lumotlarni olish uchun sizga tegishli.

Buni qilish uchun siz ham shunday funksiya yozishingiz mumkin:

#include 
#include 

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
} 

Bu misolda, sagar ma'lumotlarni yubormoqchi socket emas, bufbufer ma'lumotlarni o'z ichiga olgan, va lenintbufer bayt sonini o'z ichiga olgan bir markerni.

Funktsiya -1xato qaytadi (va errnohali qo'ng'iroq dan o'rnatiladi send()). Shuningdek, aslida yuborilgan baytlar soni ham qaytariladi len. Xato bo'lmagan ekan, shu siz yuborgan baytlar soni bo'ladi. sendall()bu eng yaxshi, albatta, bo'ladi, huffing va puffing, ma'lumotlarni jo'natish uchun, lekin xato bor bo'lsa, u darhol sizga qaytarib oladi.

To'liqligi uchun, bu erda vazifaga bir misol qo'ng'iroq bo'ldi:

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);
} 

Paketning bir qismi kelganda qabul qiluvchining oxirida nima bo'ladi? Paketlar o'zgaruvchan uzunligi bo'lsa, bir paketi tugaydi va boshqa boshlanadi qachon qabul qilish qanday biladi? Ha, haqiqiy dunyo stsenariylari eshaklardagi shohona og'riqdir. Ehtimol, encapsulate kerak (ma'lumotlar encapsulation bo'lim yo'l boshida qaytib bor, deb eslayman?) Batafsil ma'lumot uchun o'qing!

Serialization-ma'lumotlarni to'plami qanday

Bu tarmoq bo'ylab matn ma'lumotlarni yuborish uchun etarli oson, siz topish-ku, lekin siz s yoki s kabi ba'zi "ikkilik" ma'lumotlarni yuborish bo'lsangiz, nima bo'ladi?intfloat Sizda bir nechta variant bor.

  1. Kabi funksiya bilan sonni matnga aylantirishsprintf(), so'ngra matnni yuborish. Qabul qilish kabi bir vazifani foydalanib, bir qator qaytib matnni ajratmoq strtol()bo'ladi .

  2. Faqat ma'lumotlarni xom yuborish, uchun ma'lumotlarga bir namoyishchi o'tib send().

  3. Sonni ko'chma binar shaklga kodlash. Qabul qiluvchi uni dekodlaydi.

Sneak oldindan ko'rish! Bu kecha faqat!

[Parda ko'taradi]

Beej deydi, " men usuli uch afzal, yuqorida!”

[OXIRI]

(Men jiddiy bu bo'limni boshlash oldin, men buni uchun u erda kutubxonalar bor, deb sizlarga aytib kerak, va o'z dumalab va ko'chma va xatosiz qolgan juda qiyin. Shunday qilib, ov atrofida va bu narsalar o'zingiz amalga oshirish uchun qaror qabul qilishdan oldin, uy vazifasini, albatta,. Men bu ish kabi narsalar haqida qiziquvchan kishilar uchun bu erda ma'lumotlarni o'z ichiga oladi.)

Aslida barcha usullari, yuqorida, ularning kamchiliklari va afzalliklari bor, lekin, men aytgan kabi, umuman, men uchinchi usuli afzal. Birinchi, garchi, ning boshqa ikki kamchiliklari va afzalliklari ba'zi haqida gapiraylik.

Birinchi usul, yuborish oldin matn sifatida raqamlarni kodlash, osongina chop va sim orqali kelayotgan ma'lumotlarni o'qib mumkin afzalligi bor. Ba'zan bir inson-o'qib protokol bo'lmagan tarmoqli kengligi-intensiv vaziyatda foydalanish uchun ajoyib, bunday Internet o'rni Chat bilan sifatida (ARM). Biroq, u aylantirish uchun sekin ekanligini ahvolga tushgan bor, va natijalar deyarli har doim original soni ko'proq joy egallaydi!

Ikkinchi usul: xom ma'lumotlarni o'tib. Bu juda oson (lekin xavfli!): faqat yuborish ma'lumotlarga bir namoyishchi olib, va u bilan yuborish qo'ng'iroq.

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

Qabul qiluvchi, bu kabi uni oladi:

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

Tez, oddiy-nima kabi emas? Xo'sh, barcha arxitektorlar doubleintbir xil bit vakillik yoki hatto bir xil bayt buyurtma bilan (yoki bu borada) vakili emas ekan! Kodi qat'iy bo'lmagan ko'chma bo'ladi. (Salom - ehtimol, portativlikka muhtoj emassiz, bu holda bu yaxshi va tez.)

Integer turlarini qadoqlashda biz allaqachon htons()funktsiyalar sinfining raqamlarni tarmoq bayt tartibiga aylantirish orqali ko'chma narsalarni saqlashga qanday yordam berishini ko'rdik va bu qanday qilish kerak. Afsuski, turlari uchun hech qanday o'xshash vazifalari borfloat. Hamma umid yo'qoldimi?

Qo'rqmang! (U yerda bir soniya qo'rqdingizmi? Yo'q? Ozgina bo'lsa ham emasmi?) Biz, albatta, mumkin, bir narsa bor: biz to'plami mumkin (yoki "marshal", yoki "serialize", yoki ming million boshqa nomlari biri) qabul qiluvchi uzoq tomonida ochish mumkin ma'lum ikkilik formatda ichiga ma'lumotlar.

Men "ma'lum ikkilik format" bilan nima demoqchisiz? Xo'sh, biz allaqachon htons()misolni ko'rdik, to'g'rimi? Bu o'zgartiradi (yoki "kodlar", agar shunday deb o'ylayman bo'lsangiz) mezbon format tarmoq bayt tartibda ichiga baribir bir qator. Raqamni teskari (unencode) qilish uchun qabul qiluvchi chaqiradi ntohs().

Lekin men faqat boshqa non-integer turlari uchun har qanday bunday funktsiya yo'q edi, deb tayyor olish bermadi? Darhamvarhami.bi Men qildim. Va buning uchun C hech qanday standart yo'l bor, chunki, bu namakob bir oz ekan (siz Python muxlislar uchun u erda bir bepul so'z o'yini, deb).

Albatta, narsa ma'lum bir formatga ma'lumotlarni to'plami va kod hal qilish uchun sim orqali yuborish uchun. Misol uchun, floats to'plami, bu erda takomillashtirish uchun xona mo'l bilan tez va iflos narsa:

#include 

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;
}

Yuqoridagi kod float32-bit raqamida saqlaydigan sodda dastur hisoblanadi. Yuqori bit (31) soni belgisini saqlash uchun ishlatiladi ("1" salbiy anglatadi), va keyingi etti bit (30-16) butun soni qismini saqlash uchun ishlatiladi float. Nihoyat, qolgan bitlar (15-0) sonning kasr qismini saqlash uchun ishlatiladi.

Foydalanish juda sodda:

#include 

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;
}

Ortiqcha tomonda, u kichik, oddiy va tez. Minus tomonda, bu kosmosdan samarali foydalanish emas va bu oraliq juda cheklangan—u erda 32767-dan kattaroq raqamni saqlashga harakat qiling va u juda baxtli bo'lmaydi! Yuqoridagi misolda ham oxirgi juft o'nli kasrlarning to'g'ri saqlanmagan ekanligini ko'rishingiz mumkin.

Buning o'rniga nima qilishimiz mumkin? Xo'sh, suzuvchi nuqta raqamlarini saqlash uchun standart IEEE-754. Eng kompyuterlar suzuvchi nuqta matematika yuritish uchun ichki bu format foydalanish,shunday hollarda, qat'iy aytganda, aylantirish amalga oshirilishi kerak emas edi. Agar manba kodingiz portativ bo'lishini istasangiz, bu sizning taxmin qilishingiz mumkin emas. (Boshqa tomondan, agar narsalar tez bo'lishini istasangiz, buni qilish kerak bo'lmagan platformalarda optimallashtirishingiz kerak! Bu htons()va uning birinchi nima.)

Bu erda kodlaydi ba'zi kodi suzadi va IEEE-754 format  ekan . (Asosan-u NaN yoki cheksizlikni kodlamaydi, lekin buni o'zgartirish mumkin.)

#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)&((1LL< 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;
}

Men qadoqlash va 32-bit ochish uchun yuqori erda ba'zi qulay so'l qo'yish (ehtimol, bir float) va 64-bit (ehtimol, bir double) raqamlari, lekin pack754()vazifasi to'g'ridan-to'g'ri deb ataladi va ma'lumotlar kodlashbits-qiymat aytgan bo'lishi mumkin ( expbitsqaysi normalangan raqam ko'rsatkich uchun himoyalangan).

Mana namuna ishlating:


#include 
#include  // defines uintN_t types
#include  // 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;
}

Yuqoridagi kod, bu chiqish ishlab chiqaradi:

    float before : 3.1415925
    float encoded: 0x40490FDA
    float after  : 3.1415925
    
    double before : 3.14159265358979311600
    double encoded: 0x400921FB54442D18
    double after  : 3.14159265358979311600

Agar bo'lishi mumkin yana bir savol s to'plami qanday?struct Afsuski, siz uchun, tuzuvchi bir joyda butun plomba qo'yish bepulstruct, va siz portably bir bo'lak tel orqali butun narsani yuborish mumkin emas, degan ma'noni anglatadi. ("Bunday qilolmaydi", "bunday qilolmaydi"degan gaplarni eshitib kasal bo'lib qolmaysizmi? Kechirasiz! Bir do'stim iqtibos uchun, " hech narsa noto'g'ri ketadi qachon, men har doim Microsoft aybdor."Bu Microsoftning xatosi bo'lmasligi mumkin, ammo do'stimning bayonoti butunlay to'g'ri.)

Orqaga unga: sim orqali yuborish uchun eng yaxshi yo'l structmustaqil ravishda har bir maydon to'plami va keyin structular boshqa tomonda kelganda ularni ochish uchun emas.

Bu juda ko'p ish, siz o'ylayotgan narsadir. Ha, bo'ladi. Agar, albatta, mumkin, bir narsa siz uchun ma'lumotlarni to'plami yordam berish uchun yordamchi vazifasini yozish hisoblanadi. Bu qiziqarli bo'ladi! Albatta!

Kitobida Kernighan va Pike tomonidan dasturlash 37 amaliyoti, ular amalga printf()oshirish-kabi vazifalari deb nomlangan pack()va unpack()bu aniq buni. Men ularga bog'langan edim, lekin aftidan o'sha vazifalari kitobdan manbai qolganlari bilan onlayn emas.

(Dasturlash amaliyoti mukammal o'qiladi. Zevs men uni tavsiya bir mushukcha har doim saqlaydi.)

Ushbu mavzuda, men C bir protokol tamponlar amalga oshirish uchun bir namoyishchi tomchi ketyapman ishlatiladigan hech qachon ayting, lekin butunlay hurmatli ko'rinadi. Python va Perl dasturchilar pack()unpack()shu narsani amalga oshirish uchun o'z tilining va vazifalarini nazorat istagi paydo bo'ladi. Java esa xuddi shunday tarzda ishlatilishi mumkin bo'lgan katta-ol' Serializable interfeysga ega.

Lekin siz C o'z qadoqlash yordam dasturini yozish bo'lsangiz, K&P ning hiyla printf()paketlarini qurish qilish-kabi vazifalari o'zgaruvchan argument ro'yxatlarini foydalanish hisoblanadi. Bu erda men pishirilgan bir versiyasi umid qilamanki, sizga bunday narsa ishlashi mumkin, qanday qilib bir fikr berish uchun etarli bo'ladi, deb asoslangan o'z ustida.

(Ushbu kod pack754()vazifalari zikr, yuqorida. packi*()Vazifalari tanish htons()oila kabi faoliyat, ular charo'rniga boshqa tamsayı bir qator ichiga to'plami tashqari.)

#include 
#include 
#include 
#include 

/*
** 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);
}

Va bu erda bir namoyish dasturi ichiga ba'zi ma'lumotlarni paketlarni bufva keyin o'zgaruvchilar uni ochish yuqoridagi kod. unpack()Bir string argument bilan qo'ng'iroq qachon unutmang (format specifier"s"), bu bufer overrun oldini olish uchun uning oldida maksimal uzunligi sonini qo'yish dono ekan, masalan,"96s". Tarmoq orqali olish ma'lumotlarni ochish qachon ehtiyot bo'ling-bir yomon foydalanuvchi tizimi hujum qilish maqsadida yomon-qurilgan paketlar yuborish mumkin!

#include 

// 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;
}

Agar siz o'z kodni ketmoq yoki boshqa birovning foydalanish yo'qmi, bu tekshirish xatolar tutib uchun ma'lumotlar qadoqlash muolajalarni umumiy majmuini bor yaxshi fikr, o'rniga qo'l har bir oz har safar qadoqlash ortiq.

Ma'lumotlarni qadoqlash paytida, foydalanish uchun yaxshi format nima? Ajoyib savol. Yaxshiyamki, RFC 4506, tashqi ma'lumotlar vakillik standart, allaqachon hokazo suzuvchi nuqta turlari, integer turlari, tillo, xom ma'lumotlar, kabi, turli turdagi bir guruh uchun ikkilik formatlarini belgilaydi. Agar ma'lumotlarni o'zingiz ketmoq uchun harakat qilyapmiz, agar men bunga muvofiq taklif. Lekin shart emas-ku. Paketli politsiya sizning eshigingizdan tashqarida emas. Hech bo'lmasa, ular deb o'ylamayman.

Har qanday holatda ham, siz yuborishdan oldin ma'lumotlarni qandaydir yoki boshqa kodlash u ishlarni to'g'ri yo'lidir!

Ma'lumotlar Encapsulation o'g'li

U, albatta, ma'lumotlarni kapsüllemek nimani anglatadi, baribir? Eng oddiy holatda, demak, siz u erda ba'zi identifikatsion ma'lumotlar yoki paket uzunligi yoki har ikkalasi bilan sarlavhani yopasiz.

Sizning header kabi qarash kerak, nima? Xo'sh, bu sizning loyihangizni bajarish uchun zarur bo'lgan narsalarni anglatadigan ba'zi ikkilik ma'lumotlar.

Qoyil. Bu noaniq.

Okay. Misol uchun, siz s foydalanadi ko'p foydalanuvchi chat dasturi bor aytaylikSOCK_STREAM. qachon bir foydalanuvchi turlari ("deydi") narsa, axborot ikki dona serverga uzatiladi kerak: nima dedi va kim uni dedi.

Hozircha shunday yaxshi? "Muammo nimada?"siz so'rayapsiz.

Muammo shundaki, xabarlar turli uzunliklarda bo'lishi mumkin. "Tom" ismli bir kishi, "Salom" deb aytishi mumkin va "Benjamin" ismli yana bir kishi "Hey guys nima?”

Shunday send()qilib, bu sizning mijozlaringizga barcha narsalar kiradi. Sizning chiquvchi ma'lumotlar oqimi bu kabi ko'rinadi:

    t o m H i B e n j a m i n H e y g u y s w h a t i s u p ?

Va hokazo. Bir xabar boshlanganda va boshqa to'xtaganda mijoz qanday biladi? Siz mumkin, agar istasa, barcha xabarlarni bir xil uzunligi qilish va faqat sendall()biz amalga qo'ng'iroq, yuqorida. Lekin bu chiqindilar tarmoqli kengligi! Biz send()"Tom" "Hi" deyish mumkin, shuning uchun faqat 1024 bayt istamayman.

Shunday qilib, ma'lumotlarni mayda sarlavha va paketli strukturada kapsulalaymiz. Har ikki mijoz va server to'plami va ochish uchun qanday bilaman (ba'zan "marshal" va "unmarshal" deb ataladi) bu ma'lumotlar. Endi qaramang, lekin biz mijoz va server muloqot qanday ta'riflaydi bir protokol aniqlash uchun harakat qilyapmiz!

Bu holda, ning foydalanuvchi nomi bilan padded 8 belgilar sobit uzunligi, deb faraz '\0'qilaylik . Va keyin ma'lumotlar 128 belgilar maksimal qadar, o'zgaruvchan uzunligi faraz qilaylik. Keling, biz bu vaziyatda foydalanishingiz mumkin bir namuna paketi tuzilishini ko'rib chiqaylik:

  1. len (1 bayt, unsigned)-paketning umumiy uzunligi, 8-bayt foydalanuvchi nomi va chat ma'lumotlarini hisoblash.

  2. name (8 bayt)-foydalanuvchi nomi, kerak bo'lsa nul-padded.

  3. chatdata ( n-bayt) - ma'lumotlarning o'zi, ko'pi bilan 128 bayt. Paketning uzunligi ushbu ma'lumotlarning uzunligi va 8 (yuqorida joylashgan nom maydonining uzunligi) sifatida hisoblanishi kerak.

Nima uchun maydonlar uchun 8-bayt va 128-bayt limitlarni tanladim? Men ularni havodan tortib oldim. Balki, garchi, 8 bayt ehtiyojlari uchun juda cheklovchi bo'ladi, va siz 30-bayt nomi maydonini bo'lishi mumkin, yoki baribir. Tanlov sizga havola.

Yuqoridagi paket ta'rifidan foydalanib, birinchi paket quyidagi ma'lumotlardan iborat bo'ladi (hex va ASCII da):

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

Ikkinchisi esa o'xshash:

       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  ...

(Uzunligi tarmoq bayt tartibda saqlanadi, albatta. Bu holda, u muhim emas, shuning uchun faqat bir bayt ekan,lekin umuman olganda siz barcha ikkilik butun sonlarning paketlar tarmoq bayt tartibda saqlanadi istayman olaman.)

Agar bu ma'lumotlarni yuborish qolinishini, agar xavfsiz bo'lishi va shunga o'xshash bir buyruq foydalanish kerak sendall(), yuqorida, bas, siz barcha ma'lumotlar yuboriladi bilamansend(), u barcha olish uchun bir necha qo'ng'iroqlar oladi bo'lsa ham,.

Xuddi shu tarzda, siz ushbu ma'lumotni olganingizda, biroz qo'shimcha ish qilishingiz kerak. Xavfsiz bo'lishi uchun, agar qisman paketini olish mumkin, deb taxmin qilish kerak(ehtimol, biz 18 42 65 6E 6ABenjamin "" qabul kabi, yuqorida, lekin bu biz bu qo'ng'iroq olish recv()hammasi). recv()Paket to'liq qabul qilinmaguncha qayta-qayta qo'ng'iroq qilishimiz kerak.

Lekin qanday qilib? Xo'sh, biz paketning to'liq bo'lishi uchun jami olishimiz kerak bo'lgan baytlar sonini bilamiz, chunki bu raqam paketning old tomonida taked. Biz, shuningdek, maksimal paketi hajmi bilaman 1+8+128, yoki 137 bayt(chunki biz paketni aniqladik).

Agar bu erda, albatta, mumkin, bir er-xotin narsalar aslida bor. Har bir paket uzunligi bilan boshlanadi bilasizmi beri, recv()faqat paketi uzunligi olish uchun qo'ng'iroq qilishingiz mumkin. Keyin siz bor bir marta, u yana aniq paketi qolgan uzunligi ko'rsatilgan qo'ng'iroq qilishingiz mumkin (ehtimol, qayta-qayta barcha ma'lumotlarni olish uchun) to'liq paketi bor qadar. Bu usulning afzalligi shundaki, faqat bitta paket uchun yetarli darajada katta bufer kerak, kamchiligi esa recv()barcha ma'lumotlarni olish uchun kamida ikki marta qo'ng'iroq qilish kerak.

Yana bir variant faqat qo'ng'iroq recv()va qabul qilish uchun tayyor ekansiz miqdorini aytish paketi bayt maksimal soni. Agar bo'lsin baribir keyin, bufer orqa ustiga uni tayoq,va nihoyat paketi to'liq bo'lsa, ko'rish uchun tekshirish. Albatta, keyingi paket ba'zi olish mumkin, shuning uchun siz & # 8217; ll bu uchun xona bo'lishi kerak.

Nima qila ikki paketlar uchun etarli katta bir qator e'lon hisoblanadi. Bu ular yetib sifatida siz paketlarini rekonstruksiya qiladi ish array bo'ladi.

Agar recv()ma'lumotlar har safar, agar ish bufer uni append va paketi to'liq bo'lsa, ko'rish uchun tekshirish olaman. Ya'ni buferdagi baytlar soni sarlavhada ko'rsatilgan uzunlikdan katta yoki teng (+1, chunki sarlavhadagi uzunlik uzunlikning o'zi uchun baytni o'z ichiga olmaydi). Agar buferdagi baytlar soni 1 dan kam bo'lsa, paket to'liq emas, shubhasiz. Buning uchun maxsus ishni qilishingiz kerak, garchi birinchi bayt axlat bo'lsa va siz to'g'ri paket uzunligi uchun unga tayanolmaysiz.

Paket to'liq bir marta, siz nima u bilan, albatta, mumkin. Foydalaning, va ish bufer uni olib tashlash.

Kishnagan! Boshingga shuni tiqyapsanmi hali? Xo'sh, bu erda bir-ikki musht ikkinchi bo'ldi: agar bitta qo'ng'iroq keyingi ustiga bir paketi oxirida o'tgan va o'qib bo'lishi mumkinrecv(). Anavi, agar bir to'liq paketi bilan ish bufer bor, va keyingi paketi to'liq qismi! Qonli heck. (Agar ikki paketlarini ushlab etarlicha katta ish bufer qildi nima uchun, lekin bu—holda, bu sodir!)

Birinchi paketning uzunligini sarlavhadan bilganingiz va ish buferidagi baytlar sonini kuzatib borganingiz uchun ish buferidagi baytlarning nechtasini ikkinchi (chala) paketga tegishli ekanligini chiqarib, hisoblab chiqishingiz mumkin. Agar birinchi ishlov ayting qachon, agar ish bufer chiqib, uni tozalash va bufer oldida pastga qisman ikkinchi paketini ko'chirish mumkin, shuning uchun u keyingi uchun borish uchun barcha tayyor recv().

(Sizlardan ba'zi o'quvchi aslida ish bufer boshiga qisman ikkinchi paketini harakat vaqt talab etadi, deb qayd etadi, va dastur bir dumaloq bufer yordamida bu talab qilmaslik kodlangan mumkin. Afsuski sizlardan qolganlari uchun, circular tamponlar bo'yicha muhokama ushbu maqolaning doirasi emas. Agar siz hali ham qiziquvchan bo'lsangiz, ma'lumotlar tuzilmalari kitob qatnashdi va u erdan borish.)

Men u oson edi hech qachon. Ok, men u oson edi aytish qildim. Va u; agar faqat amaliyotini kerak va tez orada u tabiiy sizga kelaman. Excalibur tomonidan men uni qasam!

Adabiyot Paketlari-Salom, Dunyo!

Hozircha ushbu qo'llanma bir xostdan boshqa xostga ma'lumot yuborish haqida gapirib o'tdi. Lekin bu mumkin, men turib, agar mumkin, deb, to'g'ri organi bilan, bir vaqtning o'zida bir necha xostlar ma'lumotlarni yuborish !

UDP (faqat UDP, TCP emas) va standart IPv4 bilan bu radioeshittirish deb ataladigan mexanizm orqali amalga oshiriladi . IPv6 bilan radioeshittirish qo'llab-quvvatlanmaydi va siz multicastingning ko'pincha ustun texnikasiga murojaat qilishingiz kerak , afsuski, men bu vaqtda muhokama qilmayman. Lekin yulduzli ko'zli kelajak etarli-biz 32-bit hozirgi xususda qilyapmiz.

Lekin kuting! Siz faqat off ishlatish va willy-nilly radioeshittirish boshlash mumkin emas; agar SO_BROADCASTtarmoq ustida bir adabiyot paketini amalga yuborishingiz mumkin oldin socket variantni o'rnatish kerak. Bu ular raketa uchirish kaliti ustidan qo'yish o'sha oz plastik panellari bir biriga o'xshaydi! Bu sizning qo'lingizda qanchalik kuch ushlab turibdi!

Lekin jiddiy bo'lsa-da, efir paketlarini ishlatish xavfi mavjud va ya'ni: efir paketini olgan har bir tizim ma'lumotlarni qamrab olishning barcha piyoz-teri qatlamlarini olib tashlashi kerak. Va keyin u ma'lumotlarni ustidan qo'llari yoki uni tashlamoq. Har ikki holda ham, bu efir paketini olgan har bir mashina uchun juda ko'p ish va ularning barchasi mahalliy tarmoqda bo'lgani uchun, bu juda ko'p keraksiz ishlarni bajaradigan mashinalar bo'lishi mumkin. O'yin Doom birinchi chiqqanida, bu uning tarmoq kodi haqida shikoyat edi.

Endi, bir mushuk teri uchun bir necha yo'l bor... bir daqiqa kutib turing. Mushukni terishning birdan ortiq usuli bormi? Bu qanday ifoda? Uh va shunga o'xshash, adabiyot paketini yuborishning birdan ortiq usuli bor. Shunday qilib, barcha narsaning go'sht va kartoshka olish uchun:agar adabiyot xabar uchun manzil manzilini belgilash qanday? Ikki umumiy yo'l bor:

  1. Ma'lumotlarni ma'lum bir subnetning translyatsiya manziliga yuboring. Bu manzil mezbon qismi uchun belgilangan barcha bir-bit bilan subnet ning tarmoq soni. Masalan; misol uchun, uyda mening tarmoq hisoblanadi 192.168.1.0, mening netmask emas 255.255.255.0, bas, manzil oxirgi bayt mening mezbon soni (birinchi uch bayt, chunki, netmask ko'ra, tarmoq soni). Demak, translyatsiya manzilim 192.168.1.255. Unix ostida, ifconfigbuyruq aslida sizga barcha bu ma'lumotlarni beradi. (Agar qiziquvchan bo'lsangiz, bitwise mantiq sizning adabiyot manzilini network_numberolish yoki (emas netmask).) Ushbu turdagi translyatsiya paketini uzoq tarmoqlarga va mahalliy tarmoqqa yuborishingiz mumkin, ammo paketning marshrutizator tomonidan tushib ketish xavfini ishlatasiz. (Ular uni tomchi bermadi, agar, keyin ba'zi tasodifiy smurf translyatsiya trafik bilan LAN sel boshlash mumkin.)

  2. Ma'lumotlarni "global" eshittirish manziliga yuboring. Bu 255.255.255.255, aka INADDR_BROADCAST. Ko'p mashinalari avtomatik ravishda bitwise va tarmoq raqami bilan bu tarmoq adabiyot manziliga aylantirish uchun, lekin ba'zi bo'lmaydi. u o'zgaradi. Routerlar mahalliy tarmoq off adabiyot paketi bu turini oldinga emas, kinoya etarli.

Xo'sh, avval socket opsiyasini o'rnatmasdan efir manzili bo'yicha ma'lumotlarni jo'natishga harakat qilsangiz nima bo'ladi?SO_BROADCAST Xo'sh, yaxshi eski olov talkerlistenerva nima sodir ko'rib chiqaylik.

    $ 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

Ha, bu juda baxtli emas...chunki biz SO_BROADCASTsoket variantini o'rnatmadik. Buni, va endi siz sendto()istagan joyda mumkin!

Aslida, bu efirga uzatilishi mumkin talkerbo'lgan UDP ilovasi va mumkin bo'lmagan yagona farq. SO_BROADCAST Bu dasturni broadcaster.c:

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;
}

Bu va "oddiy" UDP mijozi / server holati o'rtasida qanday farq bor? Hech narsa! (Mijoz bundan mustasno, bu holda adabiyot paketlarini yuborishga ruxsat etiladi.) Kabi, oldinga borish va listenerbir oynada eski UDP dasturini ishlatish, va broadcasterboshqa. Siz hozir yuqorida, muvaffaqiyatsiz barcha o'sha yuboradi, albatta, ega bo'lishi kerak.

    $ 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

Va listenerpaketlar bor, deb javob ko'rish kerak. (Agar listenerjavob bermasa, bu IPv6 manziliga bog'liq bo'lgani uchun bo'lishi mumkin. AF_UNSPEClistener.cAF_INETIpv4ni majbur qilish uchun o'zgarishga harakat qiling.)

Xo'sh, bu hayajonli. Lekin hozir listenersiz ikki nusxalarini yer bor, shunday qilib, bir xil tarmoq ustida sizga keyingi boshqa mashina ustida olov, har bir mashina ustida bir, va broadcasteradabiyot manzili bilan yana ishlatish... Salom! listenerAgar faqat bir marta chaqirdi-da, har ikki s paketini olishsendto()! Salqin!

Agar listenerunga to'g'ridan-to'g'ri yuborish ma'lumotlarni oladi, lekin adabiyot manzili haqida emas, balki ma'lumotlar, agar paketlar to'sib, deb mahalliy mashina ustida xavfsizlik devori bor, deb bo'lishi mumkin. (Ha, Pat va Bapper, men bu mening namuna kodi ish emas edi, nima uchun, deb qildim oldin amalga oshirish uchun rahmat. Men qo'llanma sizni zikr edim sizga, va bu erda siz. Shunday qilib, nyah.)

Shunga qaramay, adabiyot paketlari bilan ehtiyot bo'ling. LAN har bir mashina u s yoki yo'qligini paketi bilan shug'ullanish majbur bo'ladi, chunkirecvfrom(), u butun hisoblash tarmog'iga juda yuk taqdim mumkin. Ular, albatta, tejamli va tegishli foydalanish uchun.

Umumiy Savollar

O'sha header fayllarni qayerdan olsam bo'ladi?

Agar siz allaqachon tizimiga ularni yo'q bo'lsa, agar ehtimol, ularni kerak emas. Sizning alohida platforma uchun qo'llanmasini tekshiring. Agar Windows uchun qurish bo'lsangiz, agar faqat kerak #include .

bind()Hisobotlar "foydalanish allaqachon manzil" qachon men nima qilasiz?

Siz tinglash setsockopt()SO_REUSEADDRrozetkaga variant bilan foydalanish kerak. bind()Bo'yicha bo'limni va select()masalan bo'yicha bo'limni tekshiring.

Tizimdagi ochiq rozetkalar ro'yxatini qanday olaman?

Foydalanish netstatmanTo'liq ma'lumot olish uchun sahifani tekshiring, lekin siz faqat yozib, ba'zi yaxshi chiqish olish kerak:

    $ netstat

Faqat hiyla qaysi socket qaysi dastur bilan bog'liq aniqlash hisoblanadi. :-)

Marshrut stolini qanday ko'rish mumkin?

routeBuyruq ishga tushirish (/sbineng Linuxes haqida) yoki buyruq netstat -r.

Men faqat bitta kompyuter bo'lsa, qanday qilib men mijoz va server dasturlarini ishlatish mumkin? Tarmoq dasturlarini yozish uchun tarmoq kerak emasmi?

Yaxshiyamki, siz uchun, deyarli barcha mashinalari yadro o'tirgan va tarmoq kartasi bo'lishi uchun da'vo bir loopback tarmoq "qurilma" amalga oshirish. (Bu loyo'nalish jadvalda "" deb sanab interfeysi.)

Agar nomli mashina kirgan qilyapmiz da'vo"goat". Bir oynada mijoz va boshqa server ishga tushirish. Yoki serverni fonda (" server &") ishga tushiring va mijozni shu oynada ishga tushiring. Loopback qurilmasining surati siz client goatyoki client localhost("localhost"/etc/hostsfaylingizda aniqlanganligi sababli) va siz mijozni tarmoqsiz server bilan gaplashishingiz mumkin!

Qisqasi, hech qanday o'zgarishlar u bitta nodavlat tarmoq mashina ustida ishlaydigan qilish kodi har qanday zarur! Huzzah!

Qanday uzoq tomoni ulanish yopiq bo'lsa, men aytishim mumkin?

Qaytadi, chunki aytish mumkin recv()0.

Qanday qilib, bir "ping" kommunal amalga oshirish, albatta, men? ICMP nima? Qaerda xom soketlari haqida ko'proq bilib olishingiz mumkin va ?SOCK_RAW

Richard Stivens " UNIX tarmoq dasturlash kitoblar . Shuningdek, ping/Stevens " UNIX tarmoq dasturlash manba kodi subdirectory qarash, mavjud onlayn.

Men o'zgartirish yoki qo'ng'iroq haqida vaqt qisqartirishi qanday ?connect()

Richard Stivens sizga berishini, men faqat lib/connect_nonb.c UNIX tarmoq dasturlash manba kodi sizga murojaat.

Uning gist siz bilan socket identifikatorini qilish socket(), deb, non-blokirovka uni o'rnatish, qo'ng'iroqconnect(), va barcha yaxshi ketadi, agar connect()darhol qaytadi -1va errnoo'rnatiladi EINPROGRESS. Keyin siz select()istagan vaqt bilan qo'ng'iroq, o'qish va yozish fotoalbomlarda ham socket descriptor o'tib. Bu timeout bo'lmasa, bu connect()yakunlandi qo'ng'iroq anglatadi. Bu nuqtada, siz getsockopt()SO_ERRORqo'ng'iroq qaytish qiymatini olish uchun variant bilan foydalanish connect()kerak bo'ladi, qaysi xato yo'q edi, agar nol bo'lishi kerak.

Nihoyat, siz, ehtimol, siz u orqali ma'lumotlarni uzatish boshlash oldin yana to'sib bo'lishi soket qaytib o'rnatish istayman olaman.

Bu sizning dastur & # 8217; s u bog'lovchi esa boshqa bir narsa, albatta, ruxsat qo'shilgan foyda bor, deb e'tibor bering, juda. Siz, masalan, past narsa uchun timeout o'rnatish mumkin, kabi 500 ms, va ekran har timeout bir ko'rsatkichni yangilash, keyin select()yana qo'ng'iroq. Agar chaqirdi select()va vaqt-out ayting qachon, demoq, 20 marta, agar u munosabati voz vaqti keldi bilaman olaman.

Men aytgan kabi, mukammal ajoyib misol uchun Stivens " manbasini nazorat.

Men Windows uchun qurish qanday?

Birinchidan, Windows o'chirish va Linux yoki BSD o'rnatish. };-). Yo'q, aslida, faqat joriy Windows uchun qurish bo'limiga qarang.

Solaris/SunOS uchun qanday quraman? Kompilyatsiya qilishga harakat qilsam linker xatolarini olishda davom etaman!

Sun qutilari avtomatik ravishda socket kutubxonalar kompilyatsiya emas, chunki majburiy xatolar sodir. Buning uchun qanday bir misol uchun joriy yilda Solaris/Sunnat uchun bino bo'limiga qarang.

Nima select()uchun signalga tushib qoladi?

Signallar bloklangan tizim chaqiruvlariga -1to'siq bilan qaytishga olib keladierrnoEINTR. Agar signal işleyicisi tashkil qachon bilan sigaction(), agar bayrog'ini o'rnatishingiz mumkinSA_RESTART, u so'zini keyin tizimi qo'ng'iroq qayta ishga tushirish kerak bo'lgan.

Tabiiyki, bu har doim ishlamaydi.

Bu mening sevimli hal gotobayonot o'z ichiga oladi. Siz bu hech oxirigacha sizning professor g'ashim bilaman, bas, buning uchun borish!

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");
} 

Ishonch hosil, agar bu holda foydalanish shart emasgoto; agar uni nazorat qilish uchun boshqa tuzilmalarni foydalanishingiz mumkin. Lekin men gotobayonot aslida toza, deb o'ylayman.

Qanday qilib, bir qo'ng'iroq bo'yicha timeout amalga recv()oshirish mumkin ?

Foydalanish select()! Bu siz o'qish uchun izlamoqdamiz socket identifikatorlari uchun timeout parametr belgilash imkonini beradi. Yoki butun funksiyani bitta funksiyaga o'rashingiz mumkin, shunga o'xshash:

#include 
#include 
#include 
#include 

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
}
.
.
. 

recvtimeout()Agar vaqt taqdirda qaytadi e'tibor bering-2. Nega qaytmaysan 0? Xo'sh, agar eslasangiz, qo'ng'iroqning qaytish qiymati 0recv()uzoq tomon aloqani yopganligini anglatadi. Bas, bu qaytish qiymati allaqachon uchun aytilgan, va -1"xato" degan ma'noni anglatadi, bas-2, men timeout ko'rsatkich sifatida tanladi.

Ma'lumotlarni rozetkadan yuborishdan oldin qanday shifrlayman yoki siqaman?

Shifrlashning oson usullaridan biri SSL (secure sockets layer) dan foydalanishdir,ammo bu ushbu qo'llanma doirasidan tashqarida. (OpenSSL loyihasini tekshiring qo'shimcha ma'lumot olish uchun.)

Lekin siz ulang yoki o'z kompressor yoki shifrlash tizimi amalga oshirish uchun kerakli faraz, bu har ikki uchi o'rtasida qadamlar ketma-ketlikda orqali ishlayotgan sifatida ma'lumotlar fikr faqat bir masala. Har bir qadam ba'zi tarzda ma'lumotlarni o'zgartiradi.

  1. server fayl ma'lumotlarni o'qiydi (yoki qaerda)
  2. server ma'lumotlarni shifrlaydi / siqadi (ushbu qismni qo'shasiz)
  3. server send()s shifrlangan ma'lumotlar

Endi boshqa yo'l:

  1. mijoz recv()s shifrlangan ma'lumotlar
  2. mijoz ma'lumotlarni parolini / dekompressiyalaydi (bu qismini qo'shasiz)
  3. mijoz faylga ma'lumotlarni yozadi (yoki qaerda)

Agar kompres va shifrlash uchun harakat qilyapmiz bo'lsa, faqat birinchi kompres eslayman. :-)

Mijoz to'g'ri server nima undoes kabi uzoq faqat, ma'lumotlar oxirida yaxshi bo'ladi qat'i nazar, siz qo'shish qancha oraliq qadamlar.

Bas, siz mening kodni ishlatish uchun nima qilish kerak, barcha ma'lumotlar o'qish va ma'lumotlar yuborilgan qaerda o'rtasida joy topish (foydalanish send()) tarmoq orqali, va shifrlash qilsa u erda ba'zi kodni yopishib.

Bu nima "PF_INET" men ko'rib turaman? Bilan bog'liqmi AF_INET?

 

Ha, ha shunday. socket()Batafsil ma'lumot uchun bo'limiga qarang.

Mijozdan qobiq buyruqlarini qabul qiluvchi va ularni bajaruvchi serverni qanday yozish mumkin?

Soddaligi uchun, mijoz s aytish connect()imkonini beradi, send()s, va close()ulanish s (anavi, mijoz yana ulanish holda hech keyingi tizimi qo'ng'iroqlar bor).

Mijoz quyidagicha jarayon bu:

  1. connect() uchun server
  2. send("/sbin/ls > /tmp/client.out")
  3. close() aloqa

Ayni paytda, server ma'lumotlarni tashish va uni ijro etiladi:

  1. accept() mijozdan ulanish
  2. recv(str) buyruq string
  3. close() aloqa
  4. system(str) buyruqni ishga tushirish uchun

Ogoh bo'ling! Server mijoz deydi nima amalga ega uzoq qobiq kirish berib kabi va odamlar serverga ulanish, ular qachon hisob uchun narsalar, albatta, mumkin. Masalan; misol uchun, yuqoridagi misolda, nima mijoz yuboradi, agar " rm -rf ~"? Bu sizning hisobingizdagi hamma narsani o'chiradi, bu nima!

Bas, siz dono olish, va siz xavfsiz bilaman bir er-xotin kommunal tashqari har qanday foydalanish mijoz oldini olish, foobarkommunal kabi:

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

Lekin siz hali ham xavfli odamsiz, afsuski: mijoz "foobar; rm -rf ~" kirsa nima? Eng xavfsiz narsa\, buyruq uchun argumentlarda barcha nomaqbul belgilar (shu jumladan bo'shliqlar, agar mos bo'lsa) oldida qochish ( "" ) belgisini qo'yadigan bir oz muntazam yozishdir.

Ko'rib turganingizdek, server mijoz yuboradi narsalarni ijro boshlanadi qachon xavfsizlik juda katta masala.

Men ma'lumotlarni bir qator yuborish qilyapman , lekin qachon menrecv(), u faqat qabul 536 bayt yoki 1460 bir vaqtning o'zida bayt. Lekin men mahalliy mashina uni ishlatish, agar, u bir vaqtning o'zida barcha ma'lumotlarni qabul. Nima bo'lyapti o'zi?

Siz MTU topsa qilyapmiz-jismoniy o'rta qo'yish mumkin maksimal hajmi. Mahalliy mashinada siz 8k yoki undan ko'p muammo tug'diradigan loopback qurilmasidan foydalanasiz. Lekin Ethernet, qaysi faqat qo'yish mumkin 1500 bir boshi bilan bayt, agar bu chegarasi bosing. Modem orqali, bilan 576 MTU (yana, boshi bilan), agar hatto pastki chegarasi bosing.

Siz barcha ma'lumotlar, birinchi navbatda, yuborilgan ishonch hosil qilish kerak. (sendall()Batafsil ma'lumot uchun funktsiya bajarilishini qarang.) Siz & # 8217; qayta ishonch hosil bir marta, keyin recv()barcha ma'lumotlar o'qish qadar bir ko'chadan qo'ng'iroq qilish kerak.

Uchun bir necha qo'ng'iroqlar yordamida ma'lumotlar to'liq paketlarini olish haqida batafsil ma'lumot olish uchun ma'lumotlar kapsülleme bo'lim o'g'li o'qing recv().

Men Windows qutisidaman va fork()tizim qo'ng'irog'i yoki har qanday turi yo'q struct sigaction. Nima qilish kerak?

Ular har bir joyda bo'lsangiz, ular sizning derleyici bilan jo'natildi bo'lishi mumkin POSIX kutubxonalarda bo'laman. Men bir Windows qutisi yo'q beri, men, albatta, sizga javob aytib mumkin emas, lekin men Microsoft POSIX moslashuv qatlamini ega ekanligini eslash ko'rinadi va qaerda bo'ladi ekanfork(). (Va, ehtimol, hatto sigaction.)

VC bilan kelgan yordam qidiruv++ " fork "yoki" POSIX " va u sizga har qanday maslahatlar beradi, agar ko'rish.

Agar bu umuman ishlamasa, fork()sigactionnarsalarni tozalang va uni Win32 ekvivalenti bilan almashtiring: CreateProcess(). Men qanday foydalanishni bilmaymanCreateProcess()-bu bazillion dalillarni oladi, lekin u VC++bilan kelgan docs qoplangan bo'lishi kerak.

Men bir xavfsizlik devori ortida emasman-ular ulanish mumkin, shuning uchun men xavfsizlik devori tashqarida odamlar mening IP manzilini bilish qilaylik qanday mening mashina?

Afsuski, xavfsizlik devori maqsadi xavfsizlik devori ichida mashinalari ulanish dan xavfsizlik devori tashqarida odamlar oldini olish uchun emas, bas, ularni shunday qilish imkonini beruvchi asosan xavfsizlik buzilishi hisoblanadi.

Bu barcha yo'qoladi, deb aytish uchun emas. Bir narsa uchun, agar siz hali ham tezconnect()-tez xavfsizlik devori orqali maskarading yoki NAT yoki shunga o'xshash biror narsa qilsangiz. Faqat siz har doim bir ulanishni boshlash odamsiz, shunday qilib, dasturlarni loyihalashtirish,va siz yaxshi bo'ladi.

Agar bu qoniqarli bo'lmasa, odamlar sizga ulanishi uchun sysadmins-dan xavfsizlik devorida teshik ochishni so'rashingiz mumkin. Xavfsizlik devori sizni NAT dasturi orqali yoki proksi yoki shunga o'xshash biror narsa orqali oldinga surishi mumkin.

Xavfsizlik devori bir teshik engil olinishi uchun hech narsa emas, deb xabardor bo'ling. Siz yomon odamlar ichki tarmoqqa kirish bermaydi ishonch hosil qilish kerak; agar Ajam bo'lsangiz, u siz tasavvur mumkin ko'ra dasturiy ta'minot xavfsiz qilish uchun juda qiyin.

Sysadminni mendan qizg'anma. ;-)

Paketli snifferni qanday yozaman? Ethernet interfeysini turli xil rejimga qanday qo'yaman?

Agar tarmoq kartasi "turli xil rejimda" bo'lsa, u barcha paketlarni operatsion tizimga, bu mashinaga murojaat qilganlarga emas, balki bilishda bo'lganlar uchun. (Biz bu erda Ethernet-qatlam manzillar gaplashib turibmiz, emas, balki IP–manzillar-lekin ethernet IP ancha past-qatlam, chunki,barcha IP-manzillar samarali shuningdek yuboriladi. Qo'shimcha ma'lumot olish uchun bo'lim past darajadagi bema'nilik va tarmoq nazariyasi qarang.)

Bu paketli snifferning qanday ishlashi uchun asosdir. Interfeysni turli xil rejimga qo'yadi, keyin OS telda ketadigan har bir paketni oladi. Siz bu ma'lumotlarni o'qishingiz mumkin, ayrim turdagi soket olaman.

Afsuski, savolga javob platformaga qarab o'zgaradi, lekin Agar siz Google uchun, masalan, "windows promiscuous ioctl" siz, ehtimol, bir joyga borasiz. Linux uchun, foydali Stack Overflow mavzu kabi ko'rinadi nima bor, shuningdek.

Qanday qilib, bir TCP yoki UDP soket uchun maxsus timeout qiymatini o'rnatishingiz mumkin?

Bu sizning tizimiga bog'liq. Siz uchun aniq qo'ng'iroq mumkin SO_RCVTIMEOva SO_SNDTIMEO(bilan foydalanish uchun setsockopt()) tizim bunday funksiyalarini qo'llab-quvvatlaydi, agar ko'rish uchun.

Linux odam sahifa yordamida alarm()yoki o'rniga taklifsetitimer().

Qaysi portlardan foydalanish mumkinligini qanday aytishim mumkin? "Rasmiy" port raqamlari ro'yxati bormi?

Odatda bu muammo emas. Agar yozish bo'lsangiz, aytaylik, veb-server, keyin u sizning dasturiy uchun taniqli port 80 foydalanish yaxshi fikr. Agar siz o'zingizning maxsus serveringizni yozsangiz, tasodifiy portni tanlang (lekin 1023 dan katta) va uni sinab ko'ring.

Port foydalanish allaqachon bo'lsa, agar harakat qachon siz" foydalanish allaqachon manzili " xato bind()olasiz . Yana bir port tanlang. (Bu sizning dasturiy foydalanuvchi config fayl yoki buyruq qatorni kaliti bilan ham muqobil port belgilash uchun ruxsat berish uchun yaxshi fikr.)

Rasmiy port raqamlari ro'yxati bor Internet tayinlangan raqamlari organi tomonidan saqlab (IANA). Faqat bir narsa, chunki (ustida 1023) bu ro'yxatda siz port foydalanish mumkin emas, degani emas. Misol uchun, Id dasturiy DOOM "mdqs" bir xil port foydalanadi, deb baribir. Barcha muhim bir xil mashina boshqa hech kim siz uni ishlatish uchun kerakli paytda bu port yordamida, deb.

Man Sahifalari

Unix dunyosida qo'llanmalar ko'p. Ular sizning ixtiyorida bor individual vazifalarni tasvirlab oz bo'limlari bor.

Albatta, manualyozish uchun juda ko'p narsa bo'ladi. Demoqchimanki, UNIX olamida hech kim, shu jumladan o'zim ham bunday yozishni yoqtirmaydi. Albatta, men terse bo "lishi afzal qancha haqida va katta uzunligi borish mumkin, lekin o" rniga, men qisqa bo "lishi va men butunlay deyarli barcha sharoitda bo" lishi afzal qanday butunlay hayratlanarli qisqa haqida uzoq shamolli diatribes bilan sizni tug'ib emas.

[Qarsaklar]

Tashakur.com. ki Nima, men olish qilaman, bu sahifalar Unix dunyoda "man sahifalar" deb ataladi, deb, va men sizning o'qish lazzatlanish uchun bu erda o'z shaxsiy kesilgan variant kiritilgan bo'lishi. Narsa, bu vazifalar ko'p men kirgizib qilyapman ortiq yo'l ko'proq umumiy maqsadi bor, lekin men faqat Internet Sockets dasturlash uchun tegishli qismlar taqdim ketyapman.

Lekin kuting! Bu mani sahifalarimda hammasi noto'g'ri emas:

Agar real ma'lumot bo'lsangiz, yozib mahalliy Unix odam sahifalarni tekshirishman whatever, qaerda "baribir" siz nihoyatda manfaatdor odamsiz narsa, " accept"kabi. (Men Microsoft Visual Studio ularning yordam bo'limda o'xshash narsa bor ishonamanki. Lekin "odam ""yordam" dan ko'ra bir bayt lo'nda bo'lgani ma'qul. Unix yana g'olib!)

Shunday qilib, bu shunday nomukammal bo'lsa, nima uchun hatto qo'llanma barcha ularni o'z ichiga oladi? Yaxshi, bir necha sabablari bor, lekin eng yaxshi, deb (a) bu versiyalari tarmoq dasturlash tomon maxsus qaratilgan va Real qaraganda hazm qilish oson, va (b) bu versiyalari misollar o'z ichiga oladi!

Oh! Va misollar haqida gapirganda, men barcha xatolarni tekshirishga moyil emasman, chunki u kodning uzunligini oshiradi. Lekin, albatta, juda ko'p siz butunlay ekansiz ekan tizimi qo'ng'iroqlar har qanday qilish har qanday vaqt tekshirish xato qilish kerak 100% ishonch u muvaffaqiyatsiz qilmoqchi emas, va, ehtimol, keyin ham buni qilish kerak!

accept()

Agar tinglash rozetkaga kiruvchi ulanishni qabul

Synopsis

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

Dilshodbek Yuldashev

Agar SOCK_STREAMsoket olish va kiruvchi ulanishlar uchun uni tashkil etish muammo orqali ketdi ayting listen()accept()bir marta, keyin aslida yangi ulangan mijoz bilan keyingi aloqa uchun foydalanish uchun o'zingizni yangi socket identifikatorini olish uchun qo'ng'iroq.

Tinglash uchun foydalanayotgan eski socket hali bor, va accept()ular kelib sifatida yanada qo'ng'iroqlar uchun ishlatiladi.

Parametr Dilshodbek Yuldashev
s Bu listen()ing socket descriptor.
addr Bu sizga ulanayotgan sayt manzili bilan to'ldiriladi.
addrlen Bu sizeof()parametr qaytarilgan struktura bilan to'ldiriladiaddr. Agar siz orqaga qaytayotganingizni taxmin qilsangiz, uni xavfsiz tarzda e'tiborsiz qoldirishingiz mumkinstruct sockaddr_in, chunki siz buni bilasiz, chunki bu siz uchun o'tgan turaddr.

accept() odatda bloklanadi va siz select()"o'qishga tayyor"ekanligini ko'rish uchun vaqtidan oldin tinglash soket identifikatorini ko'rib chiqishingiz mumkin. Agar shunday bo'lsa, unda edni kutayotgan yangi aloqa boraccept()! Yo ' - o'q! Shu bilan bir qatorda, siz O_NONBLOCKyordamida tinglash rozetkaga bayrog'ini o'rnatish mumkinfcntl(), va keyin u blokirovka hech qachon, o'rniga tanlash -1errnomajmui bilan qaytish uchun EWOULDBLOCK.

Tomonidan qaytib socket identifikatori accept()ochiq va uzoq xost ulangan, bir halol socket identifikatori hisoblanadi. Agar close()u bilan bajarib qachon siz unga kerak.

Qaytish Qiymati

accept() yangi ulangan soket identifikatorini yoki -1xatolikni errnomos ravishda o'rnatishni qaytaradi.

Mirshod Fayziyev

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!

Korishi. Com. Ve

socket()getaddrinfo()listen()struct sockaddr_in

bind()

Ip-manzil va port raqami bilan rozetkani bog'lash

Synopsis

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

Dilshodbek Yuldashev

Agar uzoq mashina server dasturiga ulanishni istasa, unda ikkita ma'lumot kerak: IP-manzil va port raqami. bind()Qo'ng'iroq faqat buni qilish imkonini beradi.

Birinchidan, agar getaddrinfo()struct sockaddrmanzil manzili va port ma'lumotlar bilan yuklashingiz qo'ng'iroq. Keyin siz socket()socket identifikatorini olish uchun qo'ng'iroq, va keyin soket va ichiga manzilini o'tibbind(), va IP-manzil va port sehrli bo'ladi (haqiqiy sehr yordamida) rozetkaga bog'langan!

Agar IP manzilini bilmasangiz, yoki siz faqat mashina ustida bir IP manzilini bor bilaman, yoki siz mashinaning IP manzillar ishlatiladi qaysi farqi yo'q, agar shunchaki AI_PASSIVEhintsparametr bayroqni o'tishi getaddrinfo()mumkin . Nima, bu qilsa struct sockaddrbind()u avtomatik ravishda bu xost ning IP-manzilini to'ldirish kerak, deb aytadi maxsus qiymati bilan IP-manzil qismi to'ldiring.

Nima nima? struct sockaddrJoriy xost bilan manzilni avtomatik to'ldirishga sabab bo'lish uchun "S IP adresi" ga qanday maxsus qiymat yuklanadi? Men sizga aytaman,lekin yodda tutish, bu siz qo'lidan to'ldirish bo'lsangiz faqatstruct sockaddr; agar emas , dan natijalarini foydalanishgetaddrinfo(), yuqoridagi kabi. IPv4 da struktura sin_addr.s_addrmaydoni struct sockaddr_ino'rnatiladi INADDR_ANY. Ipv6da strukturaning maydoni sin6_addrstruct sockaddr_in6global o'zgaruvchidan beriladi in6addr_any. Yoki, agar yangi e'lon struct in6_addrbo'lsangiz, agar uni boshlash mumkin IN6ADDR_ANY_INIT.

Nihoyat, addrlenparametr uchun o'rnatilgan bo'lishi kerak sizeof my_addr.

Qaytish Qiymati

Muvaffaqiyat nol qaytadi, yoki -1xato haqida (va errnoshunga ko'ra o'rnatiladi).

Mirshod Fayziyev

// 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);

Korishi. Com. Ve

getaddrinfo()socket()struct sockaddr_instruct in_addr

connect()

Serverga rozetka ulash

Synopsis

    #include 
    #include 
    
    int connect(int sockfd, const struct sockaddr *serv_addr,
                socklen_t addrlen);

Dilshodbek Yuldashev

Agar qo'ng'iroq bilan socket identifikatorini qurilgan ayting bir martasocket(), agar connect()yaxshi nomli tizimi qo'ng'iroq yordamida uzoq serverga bu socket mumkinconnect(). Agar barcha qilish kerak, uni socket identifikatorini va yaxshi bilish uchun manfaatdor odamsiz server manzilini o'tishi hisoblanadi. (Oh, va manzil uzunligi, tez-tez bu kabi vazifalarga uzatiladi qaysi.)

Odatda, bu ma'lumotlar bir qo'ng'iroq natijasida birga keladigetaddrinfo(), agar istasangiz, lekin siz o'z to'ldiring mumkinstruct sockaddr.

Agar siz hali soket identifikatorini chaqirmagan bo'lsangizbind(), u avtomatik ravishda IP-manzilingizga va tasodifiy mahalliy portga bog'langan. Agar server emasmiz, agar bu siz bilan, odatda, faqat yaxshi, agar, albatta, mahalliy port nima farqi yo'q, chunki; agar parametr uni qo'yish mumkin, shuning uchun siz faqat uzoq port nima g'amxo'rlikserv_addrbind()Siz, albatta, mijoz soket ma'lum bir IP-manzil va port bo'lishi bo'lsangiz qo'ng'iroq qilishingiz mumkin, lekin bu juda kam.

Socket connect()ed bir marta, agar bepul send()odamsiz va recv()yurak mundarijaga unga ma'lumotlar.

Maxsus eslatma: agar connect()SOCK_DGRAMuzoq xost uchun UDP socket bo'lsa, siz foydalanishingiz mumkin send()va recv()shuningdek sendto()va recvfrom(). Xohlasangiz.

Qaytish Qiymati

Muvaffaqiyat nol qaytadi, yoki -1xato haqida (va errnoshunga ko'ra o'rnatiladi).

Mirshod Fayziyev

// 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);

Korishi. Com. Ve

socket()bind()

close()

Soket deskriptorini yoping

Synopsis

    #include 
    
    int close(int s);

Dilshodbek Yuldashev

Siz & # 8217; ve concocted bor baribir jinni sxemasi uchun soket yordamida tugagan va siz don & # 8217; t istayman send()recv()yoki, albatta, rozetkaga bilan barcha boshqa hech narsa, albatta,,, mumkinclose(), va u & # 8217; ll ozod bo'lishi, hech qachon yana foydalanish uchun.

Bu ikki yo'l biri sodir bo'ladi, agar uzoq tomoni aytishim mumkin. Biri: uzoqdan yon chaqirsarecv(), qaytadi 0. Ikki: uzoq tomoni qo'ng'iroqlar send()bo'lsa, u bir signal olish SIGPIPEva yuborish olaman() qaytadi -1va errnoo'rnatiladi EPIPE.

Windows foydalanuvchilari: foydalanish kerak funksiyasi deyiladi closesocket(), emas close(). Agar siz soket identifikatorida foydalanishga harakat qilsangizclose(), Windows g'azablanishi mumkin... va g'azablanganda buni yoqtirmaysiz.

Qaytish Qiymati

Muvaffaqiyat nol qaytadi, yoki -1xato haqida (va errnoshunga ko'ra o'rnatiladi).

Example

s = socket(PF_INET, SOCK_DGRAM, 0);
.
.
.
// a whole lotta stuff...*BRRRONNNN!*
.
.
.
close(s);  // not much to it, really.

Korishi. Com. Ve

socket()shutdown()

getaddrinfo()freeaddrinfo()gai_strerror()

Bir mezbon nomi haqida ma'lumot olish va / yoki xizmat va struct sockaddrnatija bilan yuklashingiz.

Synopsis

    #include 
    #include 
    #include 
    
    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
    };

Dilshodbek Yuldashev

getaddrinfo() (masalan, uning IP-manzili kabi) muayyan mezbon nomi haqida ma'lumot qaytadi va struct sockaddr(u IPv4 yoki IPv6 bo'lsa kabi) qo'pol batafsil g'amxo'rlik, siz uchun bir yuk bo'ladi ajoyib funksiyasi. Bu eski vazifalari o'rnini gethostbyname()vagetservbyname().Tavsif, quyida, bir oz qiyin bo'lishi mumkin ma'lumotlar ko'p o'z ichiga oladi, lekin haqiqiy foydalanish juda oddiy. Bu birinchi misollar nazorat qilish bunga loyiq bo'lishi mumkin.

Agar manfaatdor odamsiz mezbon nomi nodenameparametr ketadi. Manzil kabi, bir mezbon nomi ham bo'lishi mumkin "www.example.com", yoki IPv4 yoki IPv6 manzili (mag'lubiyatga sifatida o'tgan). NULLAgar bayroq yordamida bo'lsangiz, bu parametr ham bo'lishi mumkin AI_PASSIVE(pastroqqa qarang).

servnameParametr asosan port raqami. Bu port raqami bo'lishi mumkin ("80" kabi, bir mag'lubiyatga sifatida o'tgan), yoki "http" yoki "tftp" yoki "smtp" yoki "pop", va hokazo kabi, bir xizmat nomi bo'lishi mumkin. Taniqli xizmat nomlari IANA Port Ro'yxatida topish mumkin yoki /etc/servicesfayl.

Nihoyat, kirish parametrlari uchun bizda bor hints. Agar getaddrinfo()funktsiya nima qilmoqchi aniqlash uchun bo'lsin, bu, albatta, bo'ladi. Bilan foydalanishdan oldin butun tuzilishi nol memset(). Keling, foydalanishdan oldin o'rnatishingiz kerak bo'lgan maydonlarni ko'rib chiqaylik.

ai_flagsTurli xil narsalarga o'rnatilishi mumkin, ammo bu erda bir nechta muhim narsalar mavjud. (Bir nechta bayroqlarni bitwise-oring operatori bilan birgalikda belgilash mumkin|). Bayroqlar to'liq ro'yxati uchun odam sahifa tekshiring.

AI_CANONNAME ai_canonnamehostning kanonik (haqiqiy) nomi bilan to'ldirilishiga natija sabab bo'ladi. AI_PASSIVEnatijaning IP adresi INADDR_ANY(IPv4) yoki in6addr_any(IPv6) bilan to'ldirilishiga sabab bo'ladi; bu keyingi chaqiruvni bind()struct sockaddrjoriy xost adresi bilan ip adresini avtomatik to'ldirishga sabab bo'ladi. Bu siz manzilini hardcode istamayman qachon bir server tashkil etish uchun yaxshi.

Agar foydalanishAI_PASSIVE, albatta, agar, bayroq, keyin siz o'tishi mumkin NULLnodename(chunki bind()keyinchalik siz uchun uni to'ldiradi).

Kirish paramterlari bilan davom etib, siz ai_familyAF_UNSPECgetaddrinfo()IPv4 va IPv6 manzillarini qidirishni istagan bo'lishingiz mumkin. Bundan tashqari, yoki bilan bir yoki boshqa o'zingizni cheklash AF_INETAF_INET6mumkin .

Keyingi, socktypemaydon uchun belgilangan bo'lishi kerak SOCK_STREAMyoki SOCK_DGRAM, kerakli qaysi qarab soket turi.

Nihoyat, ai_protocol0protokol turini avtomatik ravishda tanlash uchun qoldiring.

Endi, u erda hamma narsalar bo'lsin keyin, nihoyat qo'ng'iroq qilish mumkin getaddrinfo()!

Albatta, bu erda qiziqarli boshlanadi. resEndi s bog'liq ro'yxatiga ishora qiladistruct addrinfo, va siz maslahatlar bilan o'tdi nima mos barcha manzillar olish uchun bu ro'yxatda orqali borish mumkin.

Endi, u bir sabab yoki boshqa uchun ishlamaydi ba'zi manzillar olish mumkin, bas, nima Linux odam sahifa qilsa ro'yxati orqali ko'chadan uchun qo'ng'iroq qilib socket()va connect()(yoki bind()siz bayrog'i bilan server tashkil AI_PASSIVEetish bo'lsangiz) u muvaffaqiyatli qadar.

Nihoyat, agar bog'liq ro'yxati bilan bajarib qachon, agar freeaddrinfo()xotira bo'shatish uchun qo'ng'iroq qilish kerak (yoki notog'ri bo'ladi, va ba'zi odamlar xafa bo'ladi).

Qaytish Qiymati

Muvaffaqiyat nol qaytadi, yoki xato haqida nonzero. Bu nonzero qaytib bo'lsa, agar qaytish gai_strerror()qiymati xato kodi bosma versiyasini olish vazifasini foydalanishingiz mumkin.

Mirshod Fayziyev

// 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

Korishi. Com. Ve

gethostbyname()getnameinfo()

gethostname()

Tizim nomini qaytaradi

Synopsis

    #include 
    
    int gethostname(char *name, size_t len);

Dilshodbek Yuldashev

Sizning tizim nomi bor. Hammalari qilishadi. Bu biz haqida gapirgan ayting networky narsalar qolgan bir oz ko'proq Unixy narsa, lekin u hali ham uning foydalanish bor.

Misol uchun, siz mezbon nomini olishingiz mumkin, va keyin gethostbyname()IP manzilini topish uchun qo'ng'iroq.

Parametr namemezbon nomini o'tkazadi bufer ishora kerak, va lenbayt deb bufer hajmi. gethostname()buferning oxirini almashtirmaydi (u xatoni qaytarishi mumkin yoki u faqat yozishni to'xtatishi mumkin) va NULbuferda buning uchun joy bo'lsa, u satrni bekor qiladi.

Qaytish Qiymati

Muvaffaqiyat nol qaytadi, yoki -1xato haqida (va errnoshunga ko'ra o'rnatiladi).

Mirshod Fayziyev

char hostname[128];

gethostname(hostname, sizeof hostname);
printf("My hostname: %s\n", hostname);

Korishi. Com. Ve

gethostbyname()

gethostbyname()gethostbyaddr()

Hostname uchun IP-manzil oling, yoki aksincha

Synopsis

    #include 
    #include 
    
    struct hostent *gethostbyname(const char *name); // DEPRECATED!
    struct hostent *gethostbyaddr(const char *addr, int len, int type);

Dilshodbek Yuldashev

Esda tuting,: bu ikki vazifalari tomonidan superseded etiladi getaddrinfo()va getnameinfo()Xususan, gethostbyname()IPv6 bilan yaxshi ishlamaydi.

Ushbu vazifalar mezbon nomlari va IP-manzillar o'rtasida oldinga va orqaga xaritasi. Masalan; misol uchun, agar "www.example.com", siz gethostbyname()uning IP manzilini olish va uni saqlash uchun foydalanishingiz mumkin struct in_addr.

Aksincha, agar bor bo'lsa, a struct in_addryokistruct in6_addr, agar gethostbyaddr()orqaga hostname olish uchun foydalanishingiz mumkin. gethostbyaddr() IPv6 mos keladi, lekin siz o'rniga yangi shinier foydalanish kerakgetnameinfo().

(Agar siz hostname qarash istayman nuqta-va-raqamlar formatida bir IP manzilini o'z ichiga olgan mag'lubiyatga bo'lsa, agar bayrog'i bilan foydalanib off yaxshiroq bo'lar edimgetaddrinfo()AI_CANONNAME.)

gethostbyname() kabi mag'lubiyatga oladi "www.yahoo.com", va struct hostentaxborot tonna o'z ichiga olgan bir qaytadi, IP-manzil, shu jumladan,. (Boshqa ma'lumotlar rasmiy mezbon nomi, taxalluslar ro'yxati, manzil turi, manzillar uzunligi, va manzillar ro'yxati-bu siz ko'rib bir marta bizning maxsus maqsadlar uchun foydalanish juda oson umumiy-maqsadi tuzilishi ekan qanday.)

gethostbyaddr() oladi struct in_addryoki struct in6_addrva sizga tegishli mezbon nomini olib keladi (bir bor bo'lsa,), bas, u xil ning teskari gethostbyname()ekan . Parametrlarni kelsak, bo'lsa-daaddr, bir char*, agar aslida bir markerni o'tishi istayman struct in_addrlenbo'lishi kerak sizeof(struct in_addr)va typebo'lishi kerak AF_INET.

Shunday struct hostentqilib, qaytib oladi, bu nima? Bu savol xost haqida ma'lumot o'z ichiga olgan sohalarda bir qator bor.

Maydonez Dilshodbek Yuldashev
char *h_name Haqiqiy kanonik xost nomi.
char **h_aliases Massivlar bilan o'tish mumkin bo'lgan taxrirlar ro'yxati-oxirgi element NULL
int h_addrtype Natijaning manzili turi, albattaAF_INET, bizning maqsadlarimiz uchun bo'lishi kerak.
int length Ip (4-versiya) adreslar uchun 4 bo'lgan baytlardagi adreslar uzunligi.
char **h_addr_list Bu xost uchun IP-manzillar ro'yxati. Bu bo'lsa-da , buchar**, albattastruct in_addr*, yashirish s bir qator bo'ldi. Oxirgi qator element hisoblanadi NULL.
h_addr Uchun keng tarqalgan belgilangan boshqacha h_addr_list[0]. Agar faqat bu uy egasi uchun har qanday eski IP-manzil bo'lsangiz (ha, ular bir necha bo'lishi mumkin) faqat bu maydon foydalanish.

Qaytish Qiymati

Muvaffaqiyat bo'yicha natijasida bir namoyishchi qaytadistruct hostent, yoki NULLxato haqida.

Buning o'rniga perror()siz odatda xato hisobot uchun foydalanish edim normal va barcha narsalar, bu vazifalar o'zgaruvchilar parallel natijalarga egah_errno, qaysi vazifalari yordamida chop mumkin herror()yoki hstrerror(). Bu ish faqat klassik kabierrno,perror(), va strerror()vazifalari siz uchun ishlatiladi-ku.

Mirshod Fayziyev

// THIS IS A DEPRECATED METHOD OF GETTING HOST NAMES
// use getaddrinfo() instead!

#include 
#include 
#include 
#include 
#include 
#include 
#include 

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);

Korishi. Com. Ve

getaddrinfo()getnameinfo()gethostname()errnoperror()strerror()struct in_addr

getnameinfo()

Berilgan uchun mezbon nomi va xizmat nomi ma'lumot qarashstruct sockaddr.

Synopsis

    #include 
    #include 
    
    int getnameinfo(const struct sockaddr *sa, socklen_t salen,
                    char *host, size_t hostlen,
                    char *serv, size_t servlen, int flags);

Dilshodbek Yuldashev

Bu funksiya teskarisidirgetaddrinfo(), ya'ni bu funksiya allaqachon Yuklangan struct sockaddroladi va unga nom va xizmat nomini izlaydi. Bu eski gethostbyaddr()va getservbyport()vazifalari o'rnini.

Siz bir markerni o'tishi kerak struct sockaddr(aslida qaysi ehtimol bir struct sockaddr_inyoki struct sockaddr_in6tashlab ayting, deb) saparametr, va bu uzunligi structsalen.

Natijada mezbon nomi va xizmat nomi va parametrlari tomonidan ishora maydoni yozilgan bo'ladihostserv. Albatta, siz bu tamponlar max uzunligi belgilash hostlenkerak va servlen.

Nihoyat, agar o'tishi mumkin bir necha bayroqlar bor, lekin bu erda bir er-xotin yaxshi bo'lganlar. NI_NOFQDNfaqatgina hostbutun domen nomini emas, balki mezbon nomini o'z ichiga oladi. NI_NAMEREQDagar nomi DNS qidirish bilan topish mumkin emas, agar funktsiya muvaffaqiyatsiz sabab bo'ladi(agar bu bayroqni belgilash bo'lmasa va nomi topish mumkin emas, getnameinfo()o'rniga IP-manzil bir string versiyasini qo'yadihost).

Har doimgidek, to'liq cho'mich uchun mahalliy odam sahifalarni tekshirish.

Qaytish Qiymati

Muvaffaqiyat nol qaytadi, xato yoki nooziq-nol. Qaytish qiymati nol bo'lmagan bo'lsa, u gai_strerror()inson-o'qib mag'lubiyatga olish uchun o'tgan bo'lishi mumkin. getaddrinfoQo'shimcha ma'lumot olish uchun qarang.

Mirshod Fayziyev

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"

Korishi. Com. Ve

getaddrinfo()gethostbyaddr()

getpeername()

Ulanish uzoq tomoni haqida manzil ma'lumot qaytish

Synopsis

    #include 
    
    int getpeername(int s, struct sockaddr *addr, socklen_t *len);

Dilshodbek Yuldashev

accept()Agar uzoq ulanish ed ham bor bir marta, yoki connect()serverga ed, agar hozir bir peer sifatida tanilgan nima bor . Sizning peer shunchaki siz ulangan kompyuter, bir IP-manzil va port bilan belgilangan. Shundavisa. So…

getpeername() sodda struct sockaddr_insiz ulangan odamsiz mashina haqida ma'lumot bilan to'lgan qaytadi.

Nima uchun u "ism"deb ataladi? Xo'sh, rozetkalar turli xil juda ko'p bor, biz ushbu qo'llanmada yordamida qilyapmiz kabi emas, balki faqat Internet Sockets, va shuning uchun "nomi" barcha ishlarni qoplangan bir chiroyli umumiy muddatli edi. Bizning holatimizda, Peerning" nomi " bu IP-manzil va port.

Funktsiya natijasida manzili hajmini qaytadi-dalen, agar lenhajmi bilan preload kerak addr.

Qaytish Qiymati

Muvaffaqiyat nol qaytadi, yoki -1xato haqida (va errnoshunga ko'ra o'rnatiladi).

Mirshod Fayziyev

// 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);

Korishi. Com. Ve

gethostname()gethostbyname()gethostbyaddr()

errno

Oxirgi tizim qo'ng'irog'i uchun xato kodini ushlab turadi

Synopsis

    #include 
    
    int errno;

Dilshodbek Yuldashev

Bu tizim qo'ng'iroqlar bir poda uchun xato ma'lumotlarni ushlab o'zgaruvchidir. Agar eslash olaman bo'lsa, kabi narsalar socket()va listen()-1xato qaytish, va ular errnosiz xato sodir bo'lgan maxsus xabar berish uchun aniq qiymatini belgilash.

Header fayl errno.hxatolar uchun doimiy ramziy nomlari bir guruh ro'yxati, kabiEADDRINUSE,EPIPE,ECONNREFUSED, va hokazo. Mahalliy odam sahifalar kodlari xato sifatida qaytib mumkin, nima sizga aytaman, va siz turli xil yo'llar bilan turli xatolarni band qilish uchun ish vaqtida bu foydalanishingiz mumkin.

Yoki, tez-tez, qo'ng'iroq perror()yoki strerror()xato inson-o'qib versiyasini olish mumkin.

Bir narsa ta'kidlash, agar ko'p meraklıları uchun, eng tizimlarida errnobir threadsafe tarzda belgilangan, deb. (Anavi, bu aslida bir global o'zgaruvchilar emas, lekin u faqat bir-tishli muhitda bir global o'zgaruvchilar kabi muomala.)

Qaytish Qiymati

O'zgaruvchining qiymati transpired bo'lishi uchun eng so'nggi xato bo'ladi, qaysi oxirgi harakat muvaffaqiyatli bo'lsa "muvaffaqiyat" uchun kod bo'lishi mumkin.

Mirshod Fayziyev

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);
}

Korishi. Com. Ve

perror()strerror()

fcntl()

Nazorat socket descriptors

Synopsis

    #include 
    #include 
    
    int fcntl(int s, int cmd, long arg);

Dilshodbek Yuldashev

Ushbu funktsiya, odatda, fayl qulflash va boshqa fayl yo'naltirilgan narsalar qilish uchun ishlatiladi, lekin u ham siz ko'rish yoki vaqti-vaqti bilan foydalanish mumkin, bir er-xotin socket bog'liq vazifalari bor.

Parametr ssiz faoliyat qilmoqchi socket identifikatori cmdhisoblanadi, o'rnatilgan bo'lishi kerakF_SETFL, va argquyidagi buyruqlar biri bo'lishi mumkin. (Men aytgan kabi, fcntl()men bu erda kirgizib qilyapman ko'proq bor, lekin men socket yo'naltirilgan qolish uchun harakat qilyapman.)

 

cmd Dilshodbek Yuldashev
O_NONBLOCK Rozetkani blokirovka qilmaslik uchun o'rnating. Batafsil ma'lumot uchun bloklash bo'limiga qarang.
O_ASYNC Asinxron i / o qilish uchun rozetkani o'rnatingrecv()SIGIO. Bu ko'rish uchun kam, va qo'llanma doirasi. Va menimcha, bu faqat ma'lum tizimlarda mavjud.

Qaytish Qiymati

Muvaffaqiyat nol qaytadi, yoki -1xato haqida (va errnoshunga ko'ra o'rnatiladi).

fcntl()Tizim qo'ng'iroq turli foydalanish aslida turli qaytish qadriyatlarga ega, lekin ular socket bog'liq emas ekansiz, chunki men bu erda ularni qoplangan yo'q. fcntl()Qo'shimcha ma'lumot olish uchun mahalliy odam sahifasiga qarang.

Mirshod Fayziyev

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

Korishi. Com. Ve

To'sish, send()

htons()htonl()ntohs()ntohl()

Tarmoq bayt tartibda xost bayt tartibda bir nechta bayt integer turlarini aylantirish

Synopsis

    #include 
    
    uint32_t htonl(uint32_t hostlong);
    uint16_t htons(uint16_t hostshort);
    uint32_t ntohl(uint32_t netlong);
    uint16_t ntohs(uint16_t netshort);

Dilshodbek Yuldashev

Faqat siz, albatta, baxtsiz qilish uchun, turli kompyuterlar o'z multibyte butun sonlar uchun ichki turli bayt orderings foydalanish (ya'nichar. Buning surati, Agar siz send()short intIntel qutisidan Mac-ga ikki bayt bo'lsa (ular Intel qutilariga aylanishidan oldin, men ham shuni nazarda tutyapman), bitta kompyuterning soni1, ikkinchisi esa raqam deb o'ylaydi 256va aksincha.

Bu muammo atrofida olish uchun yo'l chetga ularning farqlarni qo'yish va Motorola va IBM to'g'ri edi, deb qabul qilish uchun har bir kishi uchun, va Intel g'alati yo'l qildim, va shuning uchun biz barcha ularni yuborishdan oldin "katta-endian" bizning bayt orderings aylantirish. Intel "oz-endian" mashina bo'lgani uchun, u bizning afzal bayt buyurtma qo'ng'iroq qilish ancha siyosiy to'g'ri "tarmoq bayt tartibi". Shunday qilib, bu vazifalar tarmoq bayt tartibi va yana qaytib uchun ona bayt tartibda aylantirish.

(Bu Intel bu vazifalari atrofida barcha bayt almashtirish anglatadi, va PowerPC ular bayt tarmoq bayt tartibda allaqachon, chunki hech narsa. Lekin siz har doim baribir sizning kodi ularni ishlatish kerak, kimdir Intel mashina uni qurish mumkin va hali ham narsalar to'g'ri ish bor, chunki.)

Eslatma bu turdagi jalb etiladi 32-bit (4 bayt, ehtimol int) va 16-bit (2 bayt, juda mos short) sonlar. 64-bit mashinalari htonll()64-bit ints uchun bo'lishi mumkin, lekin men uni ko'rmaganman. Siz faqat o'z yozish kerak.

Yaxshiyamki, bu funktsiyalarning ishlashi siz avval host (sizning mashinangiz) bayt tartibi yoki tarmoq bayt tartibidan aylantirsangiz, qaror qabul qilasiz. Agar " xost "bo'lsa, siz qo'ng'iroq qilmoqchi bo'lgan funktsiyaning birinchi harfi"h" dir. Aks holda"tarmoq" uchun " n " ekan. Funktsiya nomi o'rta har doim "uchun" siz & # 8217; qayta biri dan aylantirish, chunki "uchun" boshqa, va oldingi xat siz & # 8217; qayta aylantirish nima ko'rsatadi . Oxirgi harf-ma'lumotlarning hajmi, qisqa uchun "s", yoki uzoq uchun "l". Shunday qilib:

Funktsiya Dilshodbek Yuldashev
htons() host to network short
htonl() host to network long
ntohs() network to host short
ntohl() network to host long

Qaytish Qiymati

Har bir funksiya aylantirilgan qiymatni qaytaradi.

Mirshod Fayziyev

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

inet_ntoa()inet_aton()inet_addr

A va orqa bir nuqta-va-soni mag'lubiyatga ip manzillar aylantirishstruct in_addr

Synopsis

    #include 
    #include 
    #include 
    
    // 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);

Dilshodbek Yuldashev

Bu funksiyalar deprecated chunki ular IPv6 ni band qilmaydi! Foydalanish inet_ntop()yoki inet_pton()o'rniga! Ular hali ham tabiatda topish mumkin, chunki ular bu erda kiritilgan.

Bu vazifalar barcha bir aylantirish struct in_addr(sizning qismi struct sockaddr_in, eng munosib) nuqta-va-raqamlar formatida bir mag'lubiyatga (masalan, "192.168.5.10") va aksincha. Agar ip-manzil buyruq qatorni yoki biror narsa o'tib bo'lsa, bu uchun olish uchun eng oson yo'lidirstruct in_addrconnect(), yoki baribir. Agar ko'proq kuch kerak bo'lsa, kabi DNS vazifalari ba'zi harakat gethostbyname()yoki mahalliy mamlakatda to'ntarish d'État harakat.

Funksiya inet_ntoa()a dagi tarmoq adresini struct in_addrnuqta-va-sonlar formatidagi satrga aylantiradi. "Ntoa "" n "tarmoq uchun turadi, va" a " tarixiy sabablarga ko'ra ASCII uchun turadi (bas, u "ASCII uchun tarmoq"ekan—"toa" qo'shimchasi bir butun son uchun ASCII mag'lubiyatga aylantiradi deb nomlangan C kutubxonasida o'xshash do'st atoi()bor).

Funktsiya inet_aton()qarama-qarshi bo'lgan, a nuqta-va-raqamlari mag'lubiyatga aylantirish in_addr_t(maydon turi s_addrbo'lgan sizning struct in_addr).

Nihoyat, funktsiya inet_addr()asosan bir xil narsa qilsa bir katta funksiyasi inet_aton(). Bu nazariy deprecated bo'lyapdi, lekin siz uni ko'p ko'rasiz va politsiya siz uni ishlatish, agar sizni olish kelmaydi.

Qaytish Qiymati

inet_aton() manzil bir amal biri bo'lsa non-nol qaytadi, manzil yaroqsiz bo'lsa va u nol qaytadi.

inet_ntoa() funksiyaga har bir qo'ng'iroq bilan yoziladi statik buferda nuqta-va-sonlar satrini qaytaradi.

inet_addr() sifatida manzilini qaytaradiin_addr_t, yoki -1xato bor bo'lsa. (Agar mag'lubiyatga aylantirish uchun harakat go'yo bir xil natija emas " 255.255.255.255", qaysi bir amal IP-manzil. Bu nima inet_aton()uchun yaxshidir.)

Mirshod Fayziyev

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");

Korishi. Com. Ve

inet_ntop()inet_pton()gethostbyname()gethostbyaddr()

inet_ntop()inet_pton()

Inson-ukish shaklida va orqa IP-manzillar aylantirish.

Synopsis

    #include 
    
    const char *inet_ntop(int af, const void *src,
                          char *dst, socklen_t size);
    
    int inet_pton(int af, const char *src, void *dst);

Dilshodbek Yuldashev

Bu vazifalar inson-o'qib IP-manzillar bilan muomala va turli vazifalari va tizim qo'ng'iroqlar bilan foydalanish uchun ularning ikkilik vakillik ularni aylantirish uchun. "N" uchun turadi "tarmoq", va"""". taqdimot uchun p Yoki "matn taqdimoti". Lekin siz "bosma" deb o'ylashingiz mumkin. """tarmoq printable uchun"hisoblanadi.ntop Qarang?

Ba'zan bir IP-manzil qarab qachon ikkilik raqamlar bir dasta qarash istamayman. Siz yaxshi bosma shaklida uni istayman , kabi192.0.2.180, yoki 2001:db8:8714:3a90::12. Bunday holda, inet_ntop()siz uchun.

inet_ntop() parametr manzil oilasini oladi af(AF_INETyoki AF_INET6). srcParametr struct in_addrstruct in6_addrbir mag'lubiyatga aylantirish uchun kerakli manzilini ham bir yoki o'z ichiga olgan bir ko'rsatkich bo'lishi kerak. Nihoyat dstva sizemaqsad mag'lubiyatga pointer va bu mag'lubiyatga maksimal uzunligi.

Stringning maksimal uzunligi qanday bo'lishi kerakdst? IPv4 va IPv6 manzillari uchun maksimal uzunlik nima? Yaxshiyamki sizga yordam berish uchun bir necha so'l bor. Maksimal uzunligi bor: INET_ADDRSTRLENva INET6_ADDRSTRLEN.

Boshqa marta, agar ukish shaklida bir IP manzilini o'z ichiga olgan mag'lubiyatga bo'lishi mumkin, va siz bir struct sockaddr_inyoki bir uni to'plami istayman struct sockaddr_in6. Bunday holda, qarama-qarshi funcion inet_pton()keyin odamsiz nima.

inet_pton() bundan tashqari, bir manzil oila AF_INEToladi (yoki AF_INET6afparametr. srcParametr bosma shaklida IP manzilini o'z ichiga olgan mag'lubiyatga bir ko'rsatkich hisoblanadi. Nihoyat dstparametr natija saqlanishi kerak qaerda ishora, qaysi ehtimol, bir struct in_addryoki struct in6_addr.

Bu vazifalar DNS lookups qilmang-getaddrinfo()buning uchun kerak bo'ladi.

Qaytish Qiymati

inet_ntop() dstmuvaffaqiyat parametr qaytaradi, yoki NULLqobiliyatsiz (va errnoo'rnatiladi).

inet_pton() 1muvaffaqiyat qaytadi. -1Agar xato bo'lsa, u qaytadi (errnoo'rnatiladi), yoki 0kirish haqiqiy IP-manzil bo'lmasa.

Mirshod Fayziyev

// 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;
}

Korishi. Com. Ve

getaddrinfo()

listen()

Kiruvchi ulanishlarni tinglash uchun rozetkani ayting

Synopsis

    #include 
    
    int listen(int s, int backlog);

Dilshodbek Yuldashev

Siz socket deskriptorini (socket()tizim qo'ng'irog'i bilan qilingan) olishingiz va unga kiruvchi ulanishlarni tinglashni aytishingiz mumkin. Bu mijozlardan serverlarni ajratib turadi, bolalar.

backlogParametr tizimi siz qarab, bir er-xotin, turli narsalarni anglatishi mumkin, lekin loosely kernel yangi rad boshlanadi oldin bo'lishi mumkin, qanday qilib bo'ladi. Yangi ulanishlar kelib, shunday qilibaccept(),, backlog & # 8217; t to'ldirish emas, shunday qilib, ularga tez bo'lishi kerak. Uni belgilash harakat qilib ko'ring 10 yoki shunday, va mijozlar olish boshlash bo'lsa "ulanish rad" og'ir yuk ostida, u oliy o'rnatish.

Qo'ng'iroq oldinlisten(), sizning server bind()ma'lum bir port raqamiga o'zini qo'shishingiz qo'ng'iroq kerak. Ushbu port raqami (serverning IP-manzilida) mijozlarga ulanadigan bo'ladi.

Qaytish Qiymati

Muvaffaqiyat nol qaytadi, yoki -1xato haqida (va errnoshunga ko'ra o'rnatiladi).

Mirshod Fayziyev

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

Korishi. Com. Ve

accept()bind()socket()

perror()strerror()

Inson-o'qib mag'lubiyatga sifatida xato chop

Synopsis

    #include 
    #include    // for strerror()
    
    void perror(const char *s);
    char *strerror(int errnum);

Dilshodbek Yuldashev

Juda ko'p vazifalari -1xato qaytish va errnoba'zi bir raqam bo'lishi uchun o'zgaruvchining qiymatini belgilash beri, osongina sizga mantiqiy bir shaklda, deb chop mumkin, agar ishonch hosil yaxshi bo'lardi.

Rahmdil, perror()shunday qiladi. Agar ko'proq tavsif xato oldin bosilgan bo'lsangiz, siz sunga parametr ishora mumkin (yoki ssifatida tark mumkin NULLva qo'shimcha hech narsa chop etiladi).

Mohiyatiga ko'ra, bu funktsiya errnoqadriyatlarni oladi , kabiECONNRESET, va chiroyli tarzda ularni tazyiqlar, Peer tomonidan ulanish reset "kabi.”

Funktsiya strerror()juda o'xshaydiperror(), bu berilgan qiymati uchun xato xabari mag'lubiyatga bir namoyishchi qaytadi tashqari (agar odatda o'zgaruvchining o'tishierrno).

Qaytish Qiymati

strerror() xato xabari mag'lubiyatga bir namoyishchi qaytadi.

Mirshod Fayziyev

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));
}

Korishi. Com. Ve

errno

poll()

Bir vaqtning o'zida bir nechta rozetkalarda tadbirlar uchun Test

Synopsis

    #include 
    
    int poll(struct pollfd *ufds, unsigned int nfds, int timeout);

Dilshodbek Yuldashev

Ushbu funktsiyaselect(), ular har ikki voqealar uchun fayl identifikatorlari silsilasini tomosha deb juda o "xshash, bunday kirish ma" lumotlar tayyor recv()sifatida , socket send()uchun ma "lumotlarga tayyor, tashqari tarmoqli ma" lumotlar tayyor recv(), xatolar, va boshqalar.

Asosiy fikr s bir qator o'tishinfds struct pollfd, deb ufds, millisekundlarda bir vaqt bilan birga (1000 bir soniyada millisekundlarda). timeoutAgar abadiy kutish bo'lsangiz salbiy bo'lishi mumkin. Hech voqea vaqt tomonidan socket identifikatorlari har qanday sodir bo'lsa, poll()qaytadi.

S massividagi har bir element struct pollfdbitta socket deskriptorini ifodalaydi va quyidagi maydonlarni o'z ichiga oladi:

 

    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
    };

Chaqirishdan oldin poll()fdsocket deskriptori bilan yuklang (agar fdmanfiy songa o'rnatsangiz, bu struct pollfde'tiborga olinmaydi va uning reventsmaydoni nolga o'rnatiladi) va keyin eventsmaydonni bitwise-ORing quyidagi makros orqali quring:

Ibrokhimov Dilshodbek Yuldashev
POLLIN Ma'lumotlar ushbu rozetkaga tayyor bo'lganda meni ogohlantiringrecv().
POLLOUT send()Men blokirovka holda bu rozetkaga ma'lumotlarni mumkin, meni ogohlantirish.
POLLPRI Banddan tashqari ma'lumotlar ushbu rozetkaga tayyor bo'lganda meni ogohlantiringrecv().

poll()Qo'ng'iroq qaytib bir marta, reventsmaydon bir bitwise sifatida qurilgan bo'ladi-yoki yuqorida sohalarda, deskriptors aslida bu voqea sodir bo'ldi, qaysi aytib. Bundan tashqari, bu boshqa sohalar mavjud bo'lishi mumkin:

Ibrokhimov Dilshodbek Yuldashev
POLLERR Ushbu rozetkada xatolik yuz berdi.
POLLHUP Aloqaning olis tomoni ilib ketdi.
POLLNVAL Soket identifikatori bilan biror narsa noto'g'ri fdedi-ehtimol bu uninitializedmi?

Qaytish Qiymati

ufdsVoqea ularga sodir bo'lgan qator elementlar sonini qaytaradi; timeout sodir bo'lsa, bu nol bo'lishi mumkin. Shuningdek -1xato qaytadi (va errnoshunga ko'ra o'rnatiladi).

Mirshod Fayziyev

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);
    }
}

Korishi. Com. Ve

select()

recv()recvfrom()

Ma'lumotlarni rozetkaga olish

Synopsis

    #include 
    #include 
    
    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);

Dilshodbek Yuldashev

Agar socket yuqoriga va ulangan bir marta, agar yordamida uzoq tomondan kiruvchi ma'lumotlarni o'qishingiz mumkin recv()(TCP SOCK_STREAMsockets uchun) va recvfrom()(UDP SOCK_DGRAMsockets uchun).

Ikkala funksiya ham socket deskriptorini s, buferga ko'rsatgich buf, buferning o'lchami (baytlarda) lenva flagsfunksiyalar qanday ishlashini nazorat qiluvchi to'plamni oladi.

Bundan tashqari , recvfrom()bir oladistruct sockaddr*fromma'lumotlar kelgan qaerda sizga aytaman, va fromlenhajmi bilan to'ldiradi struct sockaddr. (Bundan tashqari, hajmi bo'lishi boshlash kerak fromlenfromyoki struct sockaddr.)

Xo'sh, bu funktsiyaga qanday ajoyib bayroqlar o'tishi mumkin? Bu erda ulardan ba'zilari, lekin siz ko'proq ma'lumot olish uchun mahalliy odam sahifalarni tekshirish kerak va aslida sizning tizimiga qo'llab-quvvatlanadi nima. Siz bitwise-yoki bu birga, yoki faqat flags0siz u muntazam vanil bo'lish bo'lsangiz o'rnatilgan recv().

 

Ibrokhimov Dilshodbek Yuldashev
MSG_OOB Tarmoqli ma'lumotlar chiqib olish. Bu bayroq bilan sizga yuborilgan ma'lumotlarni olish uchun qanday MSG_OOBsend()bo'ladi . Qabul qiluvchi tomon sifatida siz SIGURGshoshilinch ma'lumotlar borligini aytib, signal ko'targansiz. Bu signal uchun sizning işleyicisinde, agar recv()bu bayroq bilan qo'ng'iroq mumkinMSG_OOB.
MSG_PEEK Agar recv()"faqat da'vo uchun" qo'ng'iroq bo'lsangiz, bu bayroq bilan qo'ng'iroq qilishingiz mumkin. Bu sizga recv()"real uchun" (ya'ni, bayroqsiz) qo'ng'iroq qilganingizda buferda nima kutayotganini aytadiMSG_PEEK. Bu keyingi qo'ng'iroq ichiga yashirincha ko'rib chiqish o'xshaydirecv().
MSG_WAITALL recv()Agar parametr belgilangan barcha ma'lumotlar qadar qaytib emas, balki aytlen. Biroq, bu sizning istaklaringizni haddan tashqari holatlarda e'tiborsiz qoldiradi, ammo signal qo'ng'iroqni to'xtatsa yoki biror xatolik yuz bersa yoki uzoq tomon aloqani yopsa va hokazo. Unaqa jinnilik qilma.

Qo'ng'iroq qilganingizdarecv(), o'qish uchun ba'zi ma'lumotlar bor ekan, bloklab qo'yadi. Agar blokirovka emas bo'lsangiz, non-blokirovka uchun socket o'rnatish yoki bilan tekshirish select()poll()yoki qo'ng'iroq oldin kiruvchi ma'lumotlar bor recv()yoki yo'qligini ko'rish uchun yoki recvfrom().

Qaytish Qiymati

Aslida qabul bayt sonini qaytaradi (agar parametr talab kamroq bo'lishi mumkinlen), yoki -1xato (va errnoshunga ko'ra o'rnatiladi).

Uzoq tomoni ulanish yopiq bo'lsa, recv()qaytadi 0. Bu uzoq tomoni ulanishni yopiq bo'lsa aniqlash uchun normal usuli hisoblanadi. Normallik yaxshi, osiy!

Mirshod Fayziyev

// 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);

Korishi. Com. Ve

send()sendto()select()poll(), To'sib

select()

Sockets deskriptorlari o'qish/yozishga tayyorligini tekshiring

Synopsis

    #include 
    
    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);

Dilshodbek Yuldashev

select()Funktsiya sizga bir vaqtning o'zida bir necha soket tekshirish uchun bir yo'l beradi, ular ma'lumotlar D bo'lishi uchun kutish bo'lsa, ko'rish recv()send()uchun, yoki siz to'sib holda ularga ma'lumotlarni mumkin, agar, yoki ba'zi istisno sodir bo'ldi, agar.

Siz so'l yordamida socket identifikatorlari sizning silsilasini to'ldirish, kabiFD_SET(), yuqorida. Agar majmuini bor bir marta, agar quyidagi parametrlardan biri sifatida vazifaga o'tib: readfdsagar to'plamdagi soketlari har qanday ma'lumotlar tayyor bo'lsa bilish bo'lsangizrecv()writefds, soketlari har qanday send()ma'lumotlar tayyor bo'lsa, va/yoki exceptfdssiz bilish kerak bo'lsa, bir istisno (xato) soketlari har qanday sodir. NULLAgar voqealar o'sha turdagi manfaatdor emasmiz, agar bu parametrlar har qanday yoki barcha bo'lishi mumkin. select()Qaytib so'ng, fotoalbomlarda qadriyatlar o'qish yoki yozish uchun tayyor bo'lgan ko'rsatish uchun o'zgaradi, va istisnolar bor qaysi.

Birinchi parametr, neng yuqori-sonli socket identifikatori hisoblanadi (ular faqat ints odamsiz, eslab?) plus biri.

Nihoyat, thestruct timeval,timeout, oxirida-bu siz select()uchun bu silsilasini tekshirish uchun qancha vaqt aytib beradi. Bu vaqt keyin qaytib olaman, yoki voqea sodir bo'lsa, qaysi biri birinchi bo'lib. Bu struct timevalikki maydon bor: tv_secsoniya soni, qaysi qo'shiladi tv_usec, mikrosekundlarda soni (bir soniyada 1,000,000 mikrosekundlarda).

Yordamchi makroslar quyidagilarni bajaradi:

Ibrokhimov Dilshodbek Yuldashev
FD_SET(int fd, fd_set *set); Qo'shish fdset.
FD_CLR(int fd, fd_set *set); Olib tashlang fdset.
FD_ISSET(int fd, fd_set *set); Bo'lgan fdbo'lsa rost qaytaringset.
FD_ZERO(fd_set *set); Barcha yozuvlari tozalash set.

Linux foydalanuvchilar uchun eslatma: Linux ning select()"tayyor-to-o'qish" qaytish va keyin aslida shunday blokirovka keyingi qo'ng'iroq sabab, o'qishga tayyor bo'lmasligi mumkinread(). Qabul qiluvchi rozetkaga bayroqni o'rnatib, bu xato atrofida ishlashingiz mumkinO_NONBLOCK, shuning uchun u xatolar bo'lsaEWOULDBLOCK, bu xatolikni e'tiborsiz qoldiradi. fcntl()Non-to'sib uchun soket o'rnatish haqida qo'shimcha ma'lumot olish uchun mos yozuvlar sahifasiga qarang.

Qaytish Qiymati

Muvaffaqiyat majmui deskriptorlari sonini qaytaradi0, vaqt yetdi bo'lsa, yoki -1xato haqida (va errnoshunga ko'ra o'rnatiladi). Shuningdek, silsilasini tayyor bo'lgan soketlari ko'rsatish uchun tahrirlangan.

Mirshod Fayziyev

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);
    }
}

Korishi. Com. Ve

poll()

setsockopt()getsockopt()

Rozetkaga turli xil variantlarni o'rnating

Synopsis

    #include 
    #include 
    
    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);

Dilshodbek Yuldashev

Sockets juda yapılandırılabilir hayvonlar bor. Aslini olib qaraganda, ular shunday yapılandırılabilir, men ham bu erda uni hamma qamrab qilmoqchi emasman. Ehtimol, tizimga bog'liq. Lekin men asoslari haqida suhbatlashamiz.

Shubhasiz, bu funktsiyalar rozetkaga ma'lum variantlarni oladi va o'rnatadi. Linux qutisida barcha socket ma'lumoti 7-bo'limda socket uchun man sahifasida joylashgan. (Turi: "man 7 socket" bu barcha sovg'alar olish uchun.)

Parametrlarga kelsak, ssiz gapirayotgan soket, daraja o'rnatilishi kerak SOL_SOCKET. Keyin siz optnameqiziqqan ismga o'rnatasiz. Shunga qaramay, barcha variantlar uchun odam sahifasiga qarang, lekin bu erda eng qiziqarli narsalar bor:

optname Dilshodbek Yuldashev
SO_BINDTODEVICE Ushbu rozetkani eth0bind()ip-manzilga bog'lash uchun foydalanish o'rniga ramziy qurilma nomiga bog'lang. ifconfigQurilma nomlarini ko'rish uchun UNIX ostidagi buyruqni yozing.
SO_REUSEADDR Bu portga boshqa soketlari beradibind(), allaqachon portiga bog'langan faol tinglash socket bor ekan. Bu sizga o'sha atrofida olish imkonini beradi "foydalanish allaqachon manzil" agar halokatga so'ng server qayta harakat xato xabarlar.
SOCK_DGRAM UDP datagram beradi ( SOCK_DGRAM) sockets va adabiyot manzili yuborilgan paketlarni yuborish va qabul qilish. Hech narsa qilmaydi-hech narsa!!- TCP oqim rozetkalari uchun! Hahaha!

Parametr kelsakoptval, bu, odatda, bir intsavol qiymatini ko'rsatib uchun pointer bo'ldi. Booleans uchun nol yolg'on va nol bo'lmagan haqiqat. Va bu mutlaq haqiqat, agar u sizning tizimingizda boshqacha bo'lmasa. O'tiladigan parametr bo'lmasa, bo'lishi optvalmumkin NULL.

Yakuniy parametr,optlen, uzunligi o'rnatilgan bo'lishi kerakoptval, ehtimolsizeof(int), lekin variant qarab o'zgaradi. Holda unutmanggetsockopt(), bu bir pointer bo'ladisocklen_t, va u saqlanadi maksimal hajmi ob'ekt belgilaydi optval(bufer toshib oldini olish uchun). Va getsockopt()optlenaslida belgilangan bayt sonini aks ettirish uchun qiymatini o'zgartirish qiladi.

Ogohlantirish: ba'zi tizimlarida (xususan Quyosh va Windows), variant charo'rniga bir bo'lishi mumkin int, va o'rnatiladi, masalan, '1'o'rniga bir qiymati bir belgi intqiymati 1. Yana, "" va " "bilan qo'shimcha ma'lumot olish uchun o'z odam sahifalarini tekshiring!man setsockoptman 7 socket

Qaytish Qiymati

Muvaffaqiyat nol qaytadi, yoki -1xato haqida (va errnoshunga ko'ra o'rnatiladi).

Mirshod Fayziyev

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");
}

Korishi. Com. Ve

fcntl()

send()sendto()

Ma'lumotlarni rozetkadan chiqarib yuborish

Synopsis

    #include 
    #include 
    
    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);

Dilshodbek Yuldashev

Bu funksiyalar ma'lumotlarni rozetkaga jo'natadi. Umuman olganda, send()TCP SOCK_STREAMulangan sockets uchun ishlatiladi, va sendto()UDP SOCK_DGRAMunconnected datagram sockets uchun ishlatiladi. Unconnected sockets bilan, agar siz bir yuborish har safar bir paketi manzilini belgilash kerak, va oxirgi parametrlari sendto()paketi qaerga aniqlash nima uchun o'sha.

Har ikki bilan send()vasendto(), parametr ssocket emas, bufagar yubormoqchi bo'lgan ma'lumotlarga bir pointer lenbo'ladi, agar yubormoqchi baytlar soni, va flagssiz ma'lumotlar yuborilishi qanday haqida ko'proq ma'lumot belgilash imkonini beradi. flagsAgar u "normal" ma'lumotlar bo'lishi bo'lsangiz nolga o'rnating. Bu erda tez-tez ishlatiladigan bayroqlar ayrim, lekin send()batafsil ma'lumot olish uchun mahalliy odam sahifalarni tekshirish:

Ibrokhimov Dilshodbek Yuldashev
MSG_OOB "Band chiqib" ma'lumotlar sifatida yuborish. TCP bu qo'llab-quvvatlaydi, va bu ma'lumotlar normal ma'lumotlarga nisbatan yuqori ustuvor ega, deb qabul tizimi aytish uchun bir yo'ldir. Qabul qiluvchi signalni qabul SIGURGqiladi va u bu ma'lumotlarni birinchi navbatda qolgan barcha normal ma'lumotlarni qabul qilmasdan qabul qilishi mumkin.
MSG_DONTROUTE Ushbu ma'lumotlarni yo'riqnoma orqali yubormang, uni mahalliy saqlang.
MSG_DONTWAIT send()Ketgan trafik tiqilib qoladi, chunki blokirovka edi, agar, u qaytib EAGAINbor . Bu faqat bu yuborish uchun non-to'sib yoqish "o'xshaydi."Batafsil ma'lumot uchun bloklash bo'limiga qarang.
MSG_NOSIGNAL send()Endi ing bo'lgan uzoq xost uchun recv()bo'lsa, agar odatda signal olasiz SIGPIPE. Ushbu bayroqni qo'shish bu signalning ko'tarilishiga to'sqinlik qiladi.

Qaytish Qiymati

Aslida yuborilgan bayt sonini qaytaradi, yoki -1xato (va errnoshunga ko'ra o'rnatiladi). Aslida yuborilgan bayt soni siz uni yuborish so'radi soni kamroq bo'lishi mumkin unutmang! send()Bu atrofida olish uchun yordamchi funktsiyasi uchun qisman s tashish bo'limiga qarang.

Bundan tashqari, agar rozetka ikki tomonidan yopilgan bo'lsa, jarayonni chaqirish send()signalni SIGPIPEoladi . (send()Bayroq bilan atalgan ekanMSG_NOSIGNAL.)

Mirshod Fayziyev

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);

Korishi. Com. Ve

recv()recvfrom()

shutdown()

Stop yanada yuboradi va rozetkaga qabul

Synopsis

    #include 
    
    int shutdown(int s, int how);

Dilshodbek Yuldashev

Ana shunaqa! Men uni bor ayting! Yo'q, yana send()s bu rozetkaga ruxsat etiladi, lekin men hali recv()ham unga ma'lumotlarni istayman! Yoki aksincha! Buni qanday qilishim mumkin?

Agar close()socket identifikatori bo'lsa, u o'qish va yozish uchun rozetkaning ikkala tomonini yopadi va socket identifikatorini bo'shatadi. Agar faqat bir tomoni yoki boshqa yopish bo'lsangiz, agar bu shutdown()qo'ng'iroq foydalanishingiz mumkin.

Parametrlarni kelsak, saniq siz bu amalni amalga oshirish uchun kerakli socket emas, va nima harakat parametr bilan belgilangan bo'lishi mumkin, debhow. Qanday SHUT_RDyanada s oldini olish uchun bo'lishi mumkinrecv()SHUT_WR, yanada s taqiqlashsend(), yoki SHUT_RDWRhar ikkala qilish.

shutdown()Bu socket identifikatorini ozod qilmaydi unutmang, bas, siz hali close()ham to'liq yopildi qilingan bo'lsa ham, oxir-oqibat socket kerak.

Bu kamdan-kam hollarda ishlatiladigan tizim qo'ng'iroq.

Qaytish Qiymati

Muvaffaqiyat nol qaytadi, yoki -1xato haqida (va errnoshunga ko'ra o'rnatiladi).

Mirshod Fayziyev

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);

Korishi. Com. Ve

close()

socket()

Rozetka deskriptorini ajratish

Synopsis

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

Dilshodbek Yuldashev

Agar sockety narsalarni qilish uchun foydalanishingiz mumkin, yangi socket identifikatorini qaytadi. Bu, odatda, bir socket dasturi yozish juda jarayonida birinchi qo'ng'iroq bo'ladi, va keyingi qo'ng'iroqlar uchun natija foydalanishingiz mumkinlisten(),bind(),accept(), yoki boshqa vazifalarni turli.

Odatiy foydalanish , agar qo'ng'iroq bu parametrlar uchun qadriyatlarni olishgetaddrinfo(), quyidagi misolda ko'rsatilgandek. Agar chindan ham istasangiz, lekin siz qo'l bilan ularni to'ldirish mumkin.

Ibrokhimov Dilshodbek Yuldashev
domain domain agar manfaatdor odamsiz rozetkaga qanday ta'riflaydi. Bu menga ishonishi mumkin, turli xil narsalar bo'lishi mumkin, lekin bu soket qo'llanmasi bo'lgani PF_INETuchun IPv4 va PF_INET6IPv6 uchun bo'ladi.
type Shuningdek, typeparametr narsalar bir qator bo'lishi mumkin, lekin siz ehtimol SOCK_STREAMishonchli TCP sockets (send(),recv()) yoki SOCK_DGRAMishonchsiz tez UDP sockets (sendto(),) uchun ham uni recvfrom()belgilash turaman. (Yana bir qiziqarli socket turi SOCK_RAWqo'l bilan paketlarini qurish uchun foydalanish mumkin bo'lgan. Bu juda ajoyib.)
protocol Nihoyat, protocolparametr ma'lum bir socket turi bilan foydalanish uchun qaysi protokol aytadi. Men allaqachon aytgan ayting kabi, masalan, SOCK_STREAMTCP foydalanadi. Yaxshiyamki siz uchun, foydalanish paytida SOCK_STREAMyokiSOCK_DGRAM, agar faqat protokol o'rnatishingiz mumkin 0, va u avtomatik ravishda to'g'ri protokol foydalanish olaman. Aks holda, getprotobyname()to'g'ri protokol raqamini ko'rish uchun foydalanishingiz mumkin.

Qaytish Qiymati

Yangi socket descriptor keyingi qo'ng'iroqlar foydalanish uchun, yoki -1xato (va errnoshunga ko'ra o'rnatiladi).

Mirshod Fayziyev

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);

Korishi. Com. Ve

accept()bind()getaddrinfo()listen()

struct sockaddr va pals

Internet manzillari bilan ishlash uchun tuzilmalar

Synopsis

    #include 
    
    // 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];
    };

Dilshodbek Yuldashev

Bu Internet manzillari bilan shug'ullanish, barcha syscalls va vazifalari uchun asosiy tuzilmalar bor. Ko'pincha siz & # 8217; ll getaddrinfo()bu tuzilmalar amalga to'ldirish uchun foydalanish, va keyin kerak bo'lsa, ularni o'qib beradi.

Xotirasida, The struct sockaddr_inva struct sockaddr_in6bir xil boshlanishi tuzilishini bahamstruct sockaddr, va siz erkin har qanday zarar holda boshqa bir turdagi namoyishchi tashlash mumkin, koinotning mumkin oxirida tashqari.

Agar koinot bir tashlab qachon nihoyasiga qilsastruct sockaddr_in*struct sockaddr*, men sizga bu sof tasodif va'da va siz ham bu haqda tashvish kerak emas.

Shunday qilib,, yodda bu bilan, bir funktsiya oladi, deydi qachon struct sockaddr*siz tashlab mumkinstruct sockaddr_in*, deb eslayman,struct sockaddr_in6*, yoki struct sockadd_storage*osonlik va xavfsizlik bilan turi uchun.

struct sockaddr_in tuzilishi IPv4 manzillar bilan ishlatiladi (masalan, "192.0.2.10"). Bu manzil oila tutadi (AF_INET), bir port sin_port, va IPv4 manzilsin_addr.

sin_zerostruct sockaddr_inBa'zi odamlar nolga o'rnatilishi kerak deb da'vo qiladigan bu maydon ham bor. Boshqa odamlar bu haqda hech narsa talab qilmaydilar (Linux hujjatlari hatto uni eslatib o'tmaydi) va uni nolga o'rnatish aslida zarur emas. Shunday qilib,, agar u kabi his agar, yordamida nol uni o'rnatish memset().

Endi, bu struct in_addrturli tizimlarda g'alati hayvon. Ba'zan s unionva boshqa bema'ni barcha turdagi bilan aqldan #defineekan. Lekin nima qilish kerak, faqat s_addrbu tarkibida maydonini foydalanish, ko'p tizimlari faqat bir amalga oshirish, chunki.

struct sockadd_in6 va struct in6_addrjuda o'xshash, ular IPv6 uchun ishlatiladi-ku tashqari.

struct sockaddr_storage agar accept()recvfrom()ip versiyasi-agnostik kodni yozish uchun harakat qilyapmiz yoki o'tishi mumkin struct va yangi manzil IPv4 yoki IPv6 bo'ladi, agar siz bilmayman. struct sockaddr_storageTuzilishi original kichik farqli o'laroq, har ikki turdagi ushlab etarlicha katta struct sockaddr.

Mirshod Fayziyev

// 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);

Korishi. Com. Ve

accept()bind()connect()inet_aton()inet_ntoa()

Yana Referatlar

Siz bu uzoq keldim, va endi siz ko'proq uchun baqiriq-ku! Bu narsalar haqida ko'proq ma'lumot olish uchun qaerga borishingiz mumkin?

Kitoblar

Eski-maktab haqiqiy ushlab-u-in-qo'l pulpa qog'oz kitoblar uchun, quyidagi ajoyib kitoblar ba'zi harakat qilib ko'ring. Bu mashhur bookseller bilan hamkorlik yo'nalishlarga yo'naltiruvchi, menga yaxshi kickbacks berib. Agar faqat saxiy his bo'lsangiz, agar sovg'a paypal mumkin [email protected]:-)

Unix tarmoq dasturlash, hajmi 1-2 W tomonidan. Addison-Wesley Professional va Prentice Zalida tomonidan chop etilgan. ISBNs uchun hajmi 1-2: 978-0131411555, 978-0130810816.

TCP bilan Internetworking/IP, Duglas e tomonidan volume i.Comer. Pearson tomonidan chop etilgan. ISBN 978-0136085300.

TCP / ip tasvirlangan, hajmi 1-3 W tomonidan. Addison Wesley tomonidan chop etilgan. ISBNs uchun hajmi 1, 2, va 3 (va 3-hajmi belgilangan): 978-0201633467, 978-0201633542, 978-0201634952, (978-0201776317).

TCP / Kreyg Hunt tomonidan IP tarmoq ma'muriyati. Ey'Reilly & Associates, Inc tomonidan chop etilgan. ISBN 978-0596002978.

W tomonidan UNIX muhitda ilg'or dasturlash. Addison Wesley tomonidan chop etilgan. ISBN 978-0321637734.

Web Referatlar

Veb-saytida:

 

BSD Sockets: A Quick And Dirty Primer (Unix system programming info, too!)

The Unix Socket FAQ

TCP/IP FAQ

The Winsock FAQ

 

Va bu erda ba'zi tegishli Vikipediya sahifalar:

 

Berkeley Sockets

Internet Protocol (IP)

Transmission Control Protocol (TCP)

User Datagram Protocol (UDP)

Client-Server

 

Serialization(qadoqlash va unpacking ma'lumotlar)

RFCs

 

RFCs - real axloqsizlik! Bu tayinlangan raqamlarni tasvirlab hujjatlar, dasturlash API, va internetda ishlatiladi protokollari. Men sizning lazzatlanish uchun bu erda ulardan bir necha ishoratlar kiritilgan ayting, bas, popkorn bir paqir qatnashdi va fikrlash Nizomning kiyib:

RFC 1 - birinchi RFC; bu sizga "Internet" u hayotga kelayotgan edi kabi edi, nima, bir fikr beradi,va u erdan mo'ljallangan edi qanday bir tushuncha. (Bu RFC butunlay eskirgan, shubhasiz!)

 

RFC 768 —The User Datagram Protocol (UDP)

RFC 791 —The Internet Protocol (IP)

RFC 793 —The Transmission Control Protocol (TCP)

RFC 854 —The Telnet Protocol

RFC 959 —File Transfer Protocol (FTP)

RFC 1350 —The Trivial File Transfer Protocol (TFTP)

RFC 1459 —Internet Relay Chat Protocol (IRC)

RFC 1918 —Address Allocation for Private Internets

RFC 2131 —Dynamic Host Configuration Protocol (DHCP)

RFC 2616 —Hypertext Transfer Protocol (HTTP)

RFC 2821 —Simple Mail Transfer Protocol (SMTP)

RFC 3330 —Special-Use IPv4 Addresses

RFC 3493 —Basic Socket Interface Extensions for IPv6

RFC 3542 —Advanced Sockets Application Program Interface (API) for IPv6

RFC 3849 —IPv6 Address Prefix Reserved for Documentation

RFC 3920 —Extensible Messaging and Presence Protocol (XMPP)

RFC 3977 —Network News Transfer Protocol (NNTP)

RFC 4193 —Unique Local IPv6 Unicast Addresses

RFC 4506 —External Data Representation Standard (XDR)

IETFda RFC-larni qidirish va ko'rib chiqish uchun yoqimli onlayn vosita mavjud

 

Rock Climbing Drawing

Spider Web Clipart Black And White

Bloodborne Drawing

Construction Site Clipart

Batman Symbol Outline

Daisy Flower Outline

Sideways Face Drawing

Picture Of An Anchor

Deer Antler Clipart

Voice Drawing