Berkeley pistorasiat

Berkeley Sockets  on sovellusohjelmointirajapinta (API), joka on C-kielen sovellusten kehittämiseen tarkoitettu kirjasto , joka tukee prosessien välistä viestintää (IPC), jota usein käytetään tietokoneverkoissa .

Berkeley- socketit (tunnetaan myös nimellä BSD socket API ) ilmestyivät ensimmäisen kerran API:na 4.1BSD Unix -käyttöjärjestelmässä (julkaistu vuonna 1982) [1] . Kuitenkin vasta vuonna 1989 UC Berkeley pystyi julkaisemaan versioita käyttöjärjestelmästä ja verkkokirjastosta ilman tekijänoikeudella suojatun Unixin AT&T -lisenssirajoituksia.

Berkeley Sockets API on muodostanut de facto abstraktiostandardin verkkovastakkeille. Useimmat muut ohjelmointikielet käyttävät samanlaista käyttöliittymää kuin C API.

STREAMS-pohjainen Transport Layer Interface (TLI) API on vaihtoehto socket API:lle. Berkeley Sockets API on kuitenkin erittäin suosittu suosio ja toteutusmäärien suhteen.

Berkeley socket interface

Berkeley socket interface  on API , joka mahdollistaa viestinnän tietokoneiden välillä tai prosessien välillä samassa tietokoneessa. Tämä tekniikka voi toimia useiden erilaisten I/O-laitteiden ja -ohjainten kanssa, vaikka niiden tuki riippuu käyttöjärjestelmän toteutuksesta . Tämä käyttöliittymän toteutus on TCP/IP :n perusta, minkä vuoksi sitä pidetään yhtenä Internetin perustana olevista perustekniikoista . Socket-tekniikka kehitettiin ensimmäisen kerran UC Berkeleyssä käytettäväksi UNIX - järjestelmissä. Kaikissa nykyaikaisissa käyttöjärjestelmissä on Berkeley socket -rajapinnan toteutus, koska siitä on tullut standardi Internet-yhteyden muodostamisen käyttöliittymä.

Ohjelmoijat voivat käyttää socket-liitäntää kolmella eri tasolla, joista tehokkain ja perustavanlaatuisin on raakapistorasio . Melko pieni määrä sovelluksia joutuu rajoittamaan toteuttamiensa lähtevien yhteyksien hallintaa, joten raaka socket -tuki oli tarkoitettu vain tietokoneille, joita käytetään Internetiin liittyviin teknologioihin perustuvaan kehitykseen. Myöhemmin useimmat käyttöjärjestelmät ovat ottaneet käyttöön tuen niille, mukaan lukien Windows .

Otsikkotiedostot

Berkeley Sockets Software Library sisältää monia aiheeseen liittyviä otsikkotiedostoja.

<sys/socket.h> BSD-socketin perustoiminnot ja tietorakenteet. <netinet/in.h> Osoite-/protokollaperheet PF_INET ja PF_INET6. Niitä käytetään laajasti Internetissä, ja ne sisältävät IP-osoitteita sekä TCP- ja UDP-porttinumeroita. <sys/un.h> PF_UNIX/PF_LOCAL osoiteperhe. Käytetään paikalliseen viestintään samassa tietokoneessa toimivien ohjelmien välillä. Ei koske tietokoneverkkoja. <arpa/inet.h> Toiminnot numeeristen IP-osoitteiden käsittelyyn. <netdb.h> Toiminnot protokollien ja isäntänimien muuntamiseen numeerisiksi osoitteiksi. Paikallisia tietoja käytetään DNS:n tapaan.

Rakenteet

struct sockaddr_in stSockAddr ; ... bind ( SocketFD ,( const struct sockaddr * ) & stSockAddr , sizeof ( struct sockaddr_in ));
  • sockaddr_in
  • sockaddr_in6
  • in_addr
  • in6_addr

Toiminnot

socket()

socket()luo yhteyden päätepisteen ja palauttaa kahvan osoitteeseen . socket()vaatii kolme argumenttia:

  • domain , joka määrittää luotavan socketin protokollaperheen. Tämä parametri määrittää nimeämiskäytännöt ja osoitemuodon. Esimerkiksi:
    • PF_INETIPv4 -verkkoprotokollalle tai
    • PF_INET6IPv6 :lle .
    • PF_UNIXpaikallisia pistokkeita varten (tiedoston avulla).
  • tyyppi (tyyppi) yksi seuraavista:
    • SOCK_STREAMluotettava stream-oriented service (TCP) (palvelu) tai stream socket
    • SOCK_DGRAMdatagrammipalvelu (UDP) tai datagrammiliitäntä
    • SOCK_SEQPACKETluotettava sarjapakettipalvelu
    • SOCK_RAW Raaka pistoke  on raakaprotokolla verkkokerroksen päällä.
  • protokolla määrittää käytettävän siirtoprotokollan. Yleisimmät ovat IPPROTO_TCP, IPPROTO_SCTP, IPPROTO_UDP, IPPROTO_DCCP. Nämä protokollat ​​on määritetty kohdassa <netinet/in.h>. Arvoa " 0" voidaan käyttää oletusprotokollan valitsemiseen määritetystä perheestä ( domain) ja tyypistä ( type).

Funktio palauttaa −1virheen. Muussa tapauksessa se palauttaa määritettyä kahvaa edustavan kokonaisluvun.

Prototyyppi #include <sys/types.h> #include <sys/socket.h> int socket ( int domain , int type , int protokolla );

gethostbyname() ja gethostbyaddr()

Funktiot gethostbyname()ja gethostbyaddr()palauttavat osoittimen struct hostent -tyyppiseen objektiin, joka kuvaa Internet-isäntää nimen tai osoitteen perusteella. Tämä rakenne sisältää joko nimipalvelimelta saatuja tietoja tai mielivaltaisia ​​kenttiä riviltä /etc/hosts. Jos paikallinen nimipalvelin ei ole käynnissä, nämä rutiinit etsivät hakemistosta /etc/hosts. Funktiot käyttävät seuraavat argumentit:

  • nimi , joka määrittää isännän nimen. Esimerkiksi: www.wikipedia.org
  • addr , joka määrittää osoittimen rakenteeseen in_addr , joka sisältää isännän osoitteen.
  • len , joka määrittää addr : n pituuden tavuina .
  • type , joka määrittää isännän osoitealueen tyypin. Esimerkiksi: PF_INET

Funktiot palauttavat virheen yhteydessä NULL-osoittimen. Tässä tapauksessa ylimääräinen kokonaisluku h_errno voidaan tarkistaa virheen tai virheellisen tai tuntemattoman isännän havaitsemiseksi. Muussa tapauksessa palautetaan kelvollinen struct hosten * .

Prototyypit struct hostent * gethostbyname ( const char * nimi ); struct hostent * gethostbyaddr ( const void * addr , int len ​​, int type );

connect()

connect() Muodostaa yhteyden palvelimeen. Palauttaa virhekoodia edustavan kokonaisluvun: 0 tarkoittaa onnistumista ja -1 virhettä.

Jotkin pistorasian tyypit ovat yhteydettömiä, varsinkin UDP-vastakkeet. Heille yhteys saa erityisen merkityksen: tiedon lähetyksen ja vastaanottamisen oletuskohde määrätään välitetylle osoitteelle, jolloin toimintoja voidaan käyttää kuten send()yhteydettömissä recv()pistorasioissa.

Varattu palvelin voi hylätä yhteysyrityksen, joten tietyntyyppiset ohjelmat on määritettävä yrittämään yhteyttä uudelleen.

Prototyyppi #include <sys/types.h> #include <sys/socket.h> int connect ( int sockfd , const struct sockaddr * serv_addr , socklen_t addrlen );

bind()

bind()sitoo socketin tiettyyn osoitteeseen. Kun pistoke luodaan :lla socket(), se liitetään johonkin osoiteperheeseen, mutta ei tiettyyn osoitteeseen. Ennen kuin pistorasia voi vastaanottaa saapuvia yhteyksiä, se on sidottava osoitteeseen. bind()vaatii kolme argumenttia:

  • sockfd - kahva, joka edustaa kantaa sidottuna
  • serv_addr on osoitin rakenteeseen sockaddr, joka edustaa osoitetta, johon sitoudumme.
  • addrlen on kenttä , joka socklen_tedustaa rakenteen pituutta sockaddr.

Palauttaa 0 onnistumisesta ja -1 virheestä.

Prototyyppi #include <sys/types.h> #include <sys/socket.h> int bind ( int sockfd , const struct sockaddr * my_addr , socklen_t addrlen );

kuuntele()

listen()valmistelee sidotun pistorasian vastaanottamaan saapuvat yhteydet (kutsutaan "kuunteluun"). Tämä toiminto koskee vain liitäntätyyppejä SOCK_STREAMja SOCK_SEQPACKET. Siinä on kaksi argumenttia:

  • sockfd on kelvollinen pistorasian kuvaaja.
  • backlog on kokonaisluku, joka ilmaisee muodostettujen yhteyksien määrän, joka voidaan käsitellä milloin tahansa. Käyttöjärjestelmä asettaa sen yleensä maksimiarvoon.

Kun yhteys on hyväksytty, se poistetaan jonosta. Onnistuessa palautetaan 0, jos virhe, −1.

Prototyyppi #include <sys/socket.h> int kuuntele ( int sockfd , int backlog );

hyväksy()

accept()käytetään hyväksymään yhteyspyyntö etäisännältä. Hyväksyy seuraavat argumentit:

  • sockfd — kuunteluliittimen kuvaus yhteyden hyväksymiseksi.
  • cliaddr — osoitin rakenteeseen sockaddr, joka vastaanottaa tietoja asiakkaan osoitteesta.
  • addrlen — osoitin kohtaan socklen_t, joka määrittää asiakasosoitteen sisältävän ja osoitteeseen välitetyn rakenteen koon accept(). Kun accept()se palauttaa jonkin arvon, socklen_tosoittaa kuinka monta tavua rakenteesta cliaddron tällä hetkellä käytössä.

Funktio palauttaa hyväksyttyyn yhteyteen liittyvän socket-kuvaajan tai -1 virheen sattuessa.

Prototyyppi #include <sys/types.h> #include <sys/socket.h> int hyväksy ( int sockfd , struct sockaddr * cliaddr , socklen_t * addrlen );

Lisäasetukset pistorasialle

Kun olet luonut pistorasian, voit asettaa sille lisäparametreja. Tässä on joitain niistä:

  • TCP_NODELAYpoistaa Naglen algoritmin käytöstä ;
  • SO_KEEPALIVEsisältää määräajoin tarkastuksia "elämänmerkkien" varalta, jos käyttöjärjestelmä tukee sitä.

Estävät ja estävät pistorasiat

Berkeley-pistorasiat voivat toimia kahdessa tilassa: estävä tai ei-esto. Estovastake ei palauta ohjausta ennen kuin se on lähettänyt (tai vastaanottanut) kaikki toiminnolle määritetyt tiedot. Tämä koskee vain Linux-järjestelmiä. Muissa järjestelmissä, kuten FreeBSD:ssä, on luonnollista, että estävä socket ei lähetä kaikkea dataa (mutta voit asettaa send()- tai recv()-lipun MSG_WAITALL). Sovelluksen tulee tarkistaa palautusarvo seuratakseen, kuinka monta tavua lähetettiin/vastaanotettiin, ja lähetettävä uudelleen tällä hetkellä käsittelemättömät tiedot [2] . Tämä voi johtaa ongelmiin, jos socket jatkaa kuuntelemista: ohjelma saattaa jumittua, koska socket odottaa tietoja, joita ei ehkä koskaan saavu.

Socket määritetään yleensä estäväksi tai ei-estoiseksi käyttämällä fcntl()tai -toimintoja ioctl().

Tiedonsiirto

Tietojen siirtämiseen voit käyttää vakiotoimintoja tiedostojen lukemiseen / kirjoittamiseen readja write, mutta on olemassa erityisiä toimintoja tiedon siirtämiseen pistorasian kautta:

  • lähettää
  • recv
  • Lähetä
  • palautus
  • lähetä viesti
  • recvmsg

On huomattava, että käytettäessä TCP-protokollaa (tyyppisiä sockets SOCK_STREAM) on mahdollisuus vastaanottaa vähemmän dataa kuin lähetettiin, koska kaikkia tietoja ei ole vielä vastaanotettu, joten sinun on joko odotettava, kunnes funktio recvpalauttaa 0 tavua, tai aseta MSG_WAITALLfunktiolle lippu recv, joka pakottaa sen odottamaan siirron loppuun asti. Muuntyyppisissä socketeissa lippu MSG_WAITALLei muuta mitään (esimerkiksi UDP:ssä koko paketti = koko viesti). Katso myös Lukitsevat ja estävät pistorasiat.

Resurssien vapauttaminen

Järjestelmä ei vapauta puhelun allokoimia resursseja socket()ennen kuin puhelu tapahtuu close(). Tämä on erityisen tärkeää, jos puhelu connect()epäonnistui ja sitä voidaan yrittää uudelleen. Jokaisella kutsulla socket()on oltava vastaava kutsu close()kaikilla mahdollisilla suorituspoluilla. <unistd.h>-otsikkotiedosto on lisättävä tukemaan sulkemistoimintoa.

Järjestelmäkutsun suorittamisen tuloksena close()on vain kutsua liitäntää pistokkeen sulkemiseksi, ei itse pistokkeen sulkemiseksi. Tämä on komento ytimelle sulkea socket. Joskus palvelinpuolella socket voi mennä lepotilaan TIME_WAITjopa 4 minuutiksi. [yksi]

Esimerkki asiakkaasta ja palvelimesta, joka käyttää TCP:tä

TCP toteuttaa yhteyden käsitteen. Prosessi luo TCP-socketin kutsumalla funktiota socket()parametreilla PF_INETtai PF_INET6sekä SOCK_STREAM(Stream socket) ja IPPROTO_TCP.

Palvelin

Yksinkertaisen TCP-palvelimen luominen koostuu seuraavista vaiheista:

  • TCP-sockettien luominen soittamalla socket().
  • Liitä pistoke kuunteluporttiin soittamalla bind(). Ennen kutsumista bind()ohjelmoijan tulee ilmoittaa rakenne sockaddr_in, tyhjentää se (näppäimellä memset()), sitten sin_family( PF_INETtai PF_INET6) ja täyttää kentät sin_port(kuunteluportti, määritä tavujonona ). Muuntaminen short intendiannessiksi voidaan tehdä funktiokutsulla htons()(lyhenne sanoista host-to-network).
  • Valmistele liitäntä kuuntelemaan yhteyksiä (luo kuunteluliitäntä) soittamalla listen().
  • Saapuvien yhteyksien hyväksyminen puhelun kautta accept(). Tämä estää pistokkeen, kunnes saapuva yhteys vastaanotetaan, minkä jälkeen se palauttaa vastaanotetun yhteyden vastakkeen kuvaajan. Alkuperäinen kahva pysyy kuunneltavana kahvana, ja accept()sitä voidaan kutsua uudelleen kyseisestä pistokkeesta milloin tahansa (niin kauan kuin se on auki).
  • Yhteys etäisäntään, joka voidaan luoda painikkeilla send()ja recv()tai write()ja read().
  • Jokaisen avoimen pistorasian lopullinen sulkeminen, jota ei enää tarvita, tehdään painikkeella close(). On huomioitava, että jos kutsuja fork()on, niin jokaisen prosessin on suljettava sen tuntemat socketit (ydin pitää kirjaa prosessien lukumäärästä, joilla on avoin kahva), ja lisäksi kaksi prosessia ei saa käyttää samaa kantaa samaan aikaan.
/* Palvelinkoodi C-kielellä */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #määritä portti 1100 int main ( void ) { struct sockaddr_in stSockAddr ; int i32SocketFD = kanta ( PF_INET , SOCK_STREAM , IPPROTO_TCP ); if ( i32SocketFD == -1 ) { perror ( "virhe pistokkeen luomisessa" ); exit ( EXIT_FAILURE ); } memset ( & stSockAddr , 0 , sizeof ( stSockAddr )); stSockAddr . syn_family = PF_INET ; stSockAddr . sin_portti = htons ( portti ); stSockAddr . sin_addr . s_addr = htonl ( INADDR_ANY ); if ( bind ( i32SocketFD , ( struct sockaddr * ) & stSockAddr , sizeof ( stSockAddr )) == -1 ) { perror ( "Virhe: sidokset" ); sulje ( i32SocketFD ); exit ( EXIT_FAILURE ); } if ( kuuntele ( i32SocketFD , 10 ) == -1 ) { perror ( "Virhe: kuunteleminen" ); sulje ( i32SocketFD ); exit ( EXIT_FAILURE ); } varten (;;) { int i32ConnectFD = hyväksy ( i32SocketFD , 0 , 0 ); if ( i32ConnectFD < 0 ) { perror ( "Virhe: hyväksy" ); sulje ( i32SocketFD ); exit ( EXIT_FAILURE ); } /* suorittaa luku- ja kirjoitustoimintoja ... */ sammutus ( i32ConnectFD , SHUT_RDWR ); sulje ( i32ConnectFD ); } paluu 0 ; }

Asiakas

TCP-asiakkaan luominen on seuraava:

  • TCP-socketin luominen soittamalla socket().
  • Muodosta yhteys palvelimeen käyttämällä connect(), ohittamalla rakenteen sockaddr_inkanssa tai sin_familymääritetyllä , määrittääksesi kuunteluportin (tavujärjestyksessä) ja määrittääksesi kuunneltavan palvelimen IPv4- tai IPv6-osoitteen (myös tavujärjestyksessä).PF_INETPF_INET6sin_portsin_addr
  • Vuorovaikutus palvelimen kanssa käyttämällä send()ja recv()tai write()ja read().
  • Katkaise yhteys ja nollaa tiedot puhelun aikana close(). Samoin, jos kutsuja oli fork(), jokaisen prosessin on suljettava ( close()) vastake.
/* C asiakaskoodi */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main ( void ) { struct sockaddr_in stSockAddr ; int i32Res ; int i32SocketFD = kanta ( PF_INET , SOCK_STREAM , IPPROTO_TCP ); if ( i32SocketFD == -1 ) { perror ( "Virhe: Socketin luominen ei onnistu" ); palauttaa EXIT_FAILURE ; } memset ( & stSockAddr , 0 , sizeof ( stSockAddr )); stSockAddr . syn_family = PF_INET ; stSockAddr . sin_portti = htons ( 1100 ); i32Res = inet_pton ( PF_INET , "192.168.1.3" , & stSockAddr . sin_addr ); if ( i32Res < 0 ) { perror ( "Virhe: ensimmäinen parametri ei ole kelvollinen osoite" ); sulje ( i32SocketFD ); palauttaa EXIT_FAILURE ; } else if ( ! i32Res ) { perror ( "Virhe: Toinen parametri ei sisällä kelvollista IP-osoitetta" ); sulje ( i32SocketFD ); palauttaa EXIT_FAILURE ; } if ( connect ( i32SocketFD , ( struct sockaddr * ) & stSockAddr , sizeof ( stSockAddr )) == -1 ) { perror ( "Virhe: yhteydet" ); sulje ( i32SocketFD ); palauttaa EXIT_FAILURE ; } /* suorittaa luku- ja kirjoitustoimintoja ... */ sammutus ( i32SocketFD , SHUT_RDWR ); sulje ( i32SocketFD ); paluu 0 ; }

Esimerkki UDP:tä käyttävästä asiakkaasta ja palvelimesta

UDP perustuu yhteydettömään protokollaan, eli protokollaan, joka ei takaa tiedon toimittamista. UDP-paketit voivat saapua epäkunnossa, olla päällekkäisiä ja saapua useammin kuin kerran tai jopa olla saapumatta määränpäähän ollenkaan. Näiden vähimmäistakuiden vuoksi UDP on huomattavasti TCP:tä huonompi. Yhteyden muodostamatta jättäminen tarkoittaa, että kahden isännän välillä ei ole virtaa tai yhteyksiä, koska tiedot saapuvat sen sijaan datagrammeissa ( Datagram Socket ).

UDP-osoiteavaruus, UDP-porttinumeroiden alue (TSAP ISO-terminologiassa), on täysin erillinen TCP-porteista.

Palvelin

Koodi voi luoda UDP-palvelimen porttiin 7654 seuraavasti:

int sock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ); struct sockaddr_insa ; _ int sidottu ; ssize_t resize ; socklen_t * osoite_len = NULL ; sa . sin_addr . s_addr = htonl ( INADDR_ANY ); sa . sin_portti = htons ( 7654 ); sidottu = sitoa ( sukka , ( struct sockaddr * ) & sa , sizeof ( struct sockaddr ) ); jos ( sidottu < 0 ) fprintf ( stderr , "bind(): virhe %s \n " , strerror ( errno ) );

bind() sitoo socketin osoite/portti-pariin.

kun ( 1 ) { printf ( "recv testi.... \n " ); resize = recvfrom ( sukka , ( void * ) Hz , 100 , 0 , ( struct sockaddr * ) & sa , osoite_len ); jos ( muuta kokoa < 0 ) fprintf ( stderr , "Virhe %s \n " , strerror ( errno ) ); printf ( "koko: %d \n " , muuta kokoa ); nukkua ( 1 ); printf ( "datagrammi: %s \n " , hz ); }

Tällainen ääretön silmukka vastaanottaa kaikki porttiin 7654 saapuvat UDP-datagrammit käyttämällä recvfrom() -funktiota . Funktio käyttää parametreja:

  • pistorasia,
  • osoitin datapuskuriin,
  • puskurin koko,
  • liput (samalla tavalla luettaessa tai muissa pistorasian vastaanottotoiminnoissa),
  • lähettäjän osoiterakenne,
  • lähettäjän osoiterakenteen pituus.

Asiakas

Yksinkertainen esitys UDP-paketin lähettämisestä, joka sisältää "Hei!" osoitteeseen 127.0.0.1, portti 7654, näyttää suunnilleen tältä:

#include <stdio.h> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <unistd.h> /* kutsua close() pistorasiassa */ int main ( tyhjä ) { int sukka ; struct sockaddr_insa ; _ int bytes_sent ; const char * buffer = "Hei!" ; int puskurin_pituus ; puskurin_pituus = strlen ( puskuri ) + 1 ; sukka = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ); jos ( sukka == -1 ) { printf ( "Virhe luotaessa pistoketta" ); paluu 0 ; } sa . syn_family = PF_INET ; sa . sin_addr . s_addr = htonl ( 0x7F000001 ); sa . sin_portti = htons ( 7654 ); tavua lähetetty = lähetä ( sukka , puskuri , strlen ( puskuri ) + 1 , 0 , ( struct sockaddr * ) & sa , sizeof ( struct sockaddr_in ) ); jos ( tavut_lähetetyt < 0 ) printf ( "Virhe lähetettäessä pakettia: %s \n " , strerror ( errno ) ); sulkea ( sukka ); paluu 0 ; }

Katso myös

Muistiinpanot

  1. Uresh Vahalia. UNIX-sisäosat: uudet rajat. - Upper Saddle River, New Jersey 07458: Prentice Hall PTR, 2003. - 844 s. — ISBN 0-13-101908-2 .
  2. Beej's Guide to Network Programming . Haettu 12. joulukuuta 2008. Arkistoitu alkuperäisestä 10. huhtikuuta 2011.

Linkit

POSIX -standardin sisältämä socket-liitännän "de jure" määritelmä , joka tunnetaan paremmin nimellä: