Berkeley Sockets är ett applikationsprogrammeringsgränssnitt (API) som är ett bibliotek för att utveckla applikationer i C-språket med stöd för inter-process communication (IPC), som ofta används i datornätverk .
Berkeley -sockets (även kända som BSD- socket API ) dök först upp som ett API i 4.1BSD Unix - operativsystemet (släppt 1982) [1] . Det var dock inte förrän 1989 som UC Berkeley kunde börja släppa versioner av operativsystemet och nätverksbiblioteket utan AT&T -licensrestriktioner för upphovsrättsskyddade Unix.
Berkeley Sockets API har bildat de facto abstraktionsstandarden för nätverkssockets. De flesta andra programmeringsspråk använder ett gränssnitt som liknar C API.
Det STREAMS-baserade Transport Layer Interface (TLI) API är ett alternativ till socket API. Berkeley Sockets API är dock starkt dominerat i popularitet och antal implementeringar.
Berkeley-socket-gränssnittet är ett API som tillåter kommunikation mellan datorer eller mellan processer på samma dator. Denna teknik kan fungera med många olika I/O-enheter och drivrutiner , även om deras stöd beror på implementeringen av operativsystemet . Denna implementering av gränssnittet är grunden för TCP/IP , på grund av vilket det anses vara en av de grundläggande teknologierna som Internet är baserat på . Socket-teknologin utvecklades först vid UC Berkeley för användning på UNIX- system. Alla moderna operativsystem har en viss implementering av Berkeley-socket-gränssnittet, eftersom detta har blivit standardgränssnittet för att ansluta till Internet.
Programmerare kan komma åt socket-gränssnittet på tre olika nivåer, varav den mest kraftfulla och grundläggande är råsockets- nivån . Ett ganska litet antal applikationer behöver begränsa kontrollen över de utgående anslutningarna de implementerar, så stöd för rå socket var tänkt att endast vara tillgängligt på datorer som används för utveckling baserade på Internet-relaterad teknik. Därefter har de flesta operativsystem implementerat stöd för dem, inklusive Windows .
Berkeley Sockets Software Library innehåller många relaterade rubrikfiler.
<sys/socket.h> Grundläggande BSD-uttagsfunktioner och datastrukturer. <netinet/in.h> Adress/protokollfamiljerna PF_INET och PF_INET6. De används ofta på Internet och inkluderar IP-adresser samt TCP- och UDP-portnummer. <sys/un.h> PF_UNIX/PF_LOCAL adressfamilj. Används för lokal kommunikation mellan program som körs på samma dator. Gäller inte datornätverk. <arpa/inet.h> Funktioner för att arbeta med numeriska IP-adresser. <netdb.h> Funktioner för att konvertera protokollnamn och värdnamn till numeriska adresser. Lokal data används på samma sätt som DNS.socket()skapar en anslutningsändpunkt och returnerar ett handtag till . socket()tar tre argument:
Funktionen återkommer −1vid fel. Annars returnerar den ett heltal som representerar det tilldelade handtaget.
Prototyp #include <sys/types.h> #include <sys/socket.h> int - socket ( int -domän , int -typ , int - protokoll );Funktionerna gethostbyname()och gethostbyaddr()returnerar en pekare till ett objekt av typen struct hostent som beskriver en internetvärd med namn respektive adress. Denna struktur innehåller antingen information mottagen från namnservern eller godtyckliga fält från en rad i /etc/hosts. Om den lokala namnservern inte körs, ser dessa rutiner i /etc/hosts. Funktionerna tar följande argument:
Funktionerna returnerar en NULL-pekare vid fel. I det här fallet kan ytterligare ett heltal h_errno kontrolleras för att upptäcka ett fel eller en ogiltig eller okänd värd. Annars returneras en giltig struct hostent * .
Prototyper struct hostent * gethostbyname ( const char * name ); struct hostent * gethostbyaddr ( const void * addr , int len , int type );connect() Upprättar en anslutning till servern. Returnerar ett heltal som representerar felkoden: 0 indikerar framgång och -1 indikerar ett fel.
Vissa typer av uttag är anslutningslösa, framför allt UDP-uttag. För dem får anslutningen en speciell betydelse: standarddestinationen för att skicka och ta emot data är tilldelad adressen som skickas, vilket gör att sådana funktioner kan användas som send()på recv()anslutningslösa uttag.
En upptagen server kan avvisa anslutningsförsöket, så vissa typer av program måste konfigureras för att försöka ansluta igen.
Prototyp #include <sys/types.h> #include <sys/socket.h> int connect ( int sockfd , const struct sockaddr * serv_addr , socklen_t addrlen );bind()binder en socket till en specifik adress. När en socket skapas med socket()associeras den med någon adressfamilj, men inte med en specifik adress. Innan ett uttag kan ta emot inkommande anslutningar måste det bindas till en adress. bind()tar tre argument:
Returnerar 0 vid framgång och -1 vid fel.
Prototyp #include <sys/types.h> #include <sys/socket.h> int bind ( int sockfd , const struct sockaddr * my_addr , socklen_t addrlen );listen()förbereder den bundna uttaget för att acceptera inkommande anslutningar (kallas "lyssna"). Denna funktion gäller endast för uttagstyper SOCK_STREAMoch SOCK_SEQPACKET. Tar två argument:
När en anslutning väl har accepterats tas den ur kö. Vid framgång returneras 0, vid fel returneras −1.
Prototyp #include <sys/socket.h> int lyssna ( int sockfd , int backlog );accept()används för att acceptera en anslutningsbegäran från en fjärrvärd. Accepterar följande argument:
Funktionen returnerar socket-beskrivningen som är associerad med den accepterade anslutningen, eller -1 vid fel.
Prototyp #include <sys/types.h> #include <sys/socket.h> int accept ( int sockfd , struct sockaddr * cliaddr , socklen_t * addrlen );Efter att ha skapat en socket kan du ställa in ytterligare parametrar för den. Här är några av dem:
Berkeley-uttag kan fungera i ett av två lägen: blockerande eller icke-blockerande. En blockerande socket returnerar inte kontroll förrän den har skickat (eller tagit emot) all data specificerad för operationen. Detta gäller bara för Linux-system. På andra system, som FreeBSD, är det naturligt att en blockerande socket inte skickar all data (men du kan ställa in flaggan send() eller recv() MSG_WAITALL). Applikationen bör kontrollera returvärdet för att hålla reda på hur många byte som skickades/mottogs och skicka om den för närvarande obearbetade informationen i enlighet därmed [2] . Detta kan leda till problem om uttaget fortsätter att lyssna: programmet kan hänga sig eftersom uttaget väntar på data som kanske aldrig kommer fram.
Ett uttag anges vanligtvis som blockerande eller icke-blockerande med hjälp av funktionerna fcntl()eller ioctl().
För att överföra data kan du använda standardfunktionerna för att läsa/skriva filer readoch write, men det finns speciella funktioner för att överföra data via uttag:
Det bör noteras att när du använder TCP-protokollet (sockets av typen SOCK_STREAM), finns det en chans att ta emot mindre data än vad som överfördes, eftersom inte all data har tagits emot ännu, så du måste antingen vänta tills funktionen recvreturnerar 0 byte, eller ställ in en flagga MSG_WAITALLför funktionen recv, vilket tvingar den att vänta till slutet av överföringen. För andra typer av sockets ändrar flaggan MSG_WAITALLingenting (till exempel i UDP, hela paketet = hela meddelandet). Se även Blockerande och icke-blockerande uttag.
Systemet frigör inte de resurser som tilldelats av samtalet socket()förrän samtalet inträffar close(). Detta är särskilt viktigt om samtalet connect()misslyckades och kan försökas igen. Varje anrop socket()måste ha ett motsvarande anrop close()i alla möjliga exekveringsvägar. <unistd.h>-huvudfilen måste läggas till för att stödja stängningsfunktionen.
Resultatet av att utföra ett systemanrop close()är bara att anropa gränssnittet för att stänga uttaget, inte att stänga själva uttaget. Detta är ett kommando för kärnan för att stänga sockeln. Ibland på serversidan kan uttaget gå i viloläge TIME_WAITi upp till 4 minuter. [ett]
TCP implementerar konceptet med en anslutning. Processen skapar en TCP-socket genom att anropa en funktion socket()med parametrarna PF_INETeller PF_INET6, samt SOCK_STREAM(Stream-socket) och IPPROTO_TCP.
Att skapa en enkel TCP-server består av följande steg:
Skapandet av en TCP-klient är som följer:
UDP bygger på ett anslutningslöst protokoll, det vill säga ett protokoll som inte garanterar leverans av information. UDP-paket kan komma ur funktion, dupliceras och anlända mer än en gång, eller till och med inte nå destinationen alls. På grund av dessa minimigarantier är UDP betydligt sämre än TCP. Ingen anslutningsetablering innebär inga strömmar eller anslutningar mellan två värdar, eftersom data anländer i datagram istället ( Datagram Socket ).
UDP-adressutrymmet, området för UDP-portnummer (TSAP i ISO-terminologi), är helt separat från TCP-portar.
Koden kan skapa en UDP-server på port 7654 så här:
int sock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ); struct sockaddr_insa ; _ int bunden ; ssize_t recsize ; socklen_t * address_len = NULL ; sa . sin_addr . s_addr = htonl ( INADDR_ANY ); sa . sin_port = htons ( 7654 ); bunden = binda ( socka , ( struktur sockaddr * ) & sa , sizeof ( struktur sockaddr ) ); if ( bundet < 0 ) fprintf ( stderr , "bind(): fel %s \n " , strerror ( errno ) );bind() binder en socket till ett adress/portpar.
medan ( 1 ) { printf ( "recv test... \n " ); recsize = recvfrom ( sock , ( void * ) Hz , 100 , 0 , ( struct sockaddr * ) & sa , adress_len ); if ( ändra storlek < 0 ) fprintf ( stderr , "Fel %s \n " , strerror ( felnr ) ); printf ( "recsize: %d \n " , recsize ); sömn ( 1 ); printf ( "datagram: %s \n " , hz ); }En sådan oändlig slinga tar emot alla UDP-datagram som anländer till port 7654 med recvfrom() . Funktionen använder parametrar:
En enkel demonstration av att skicka ett UDP-paket som innehåller "Hej!" till adressen 127.0.0.1, port 7654, ser ut ungefär så här:
#include <stdio.h> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <unistd.h> /* för att anropa close() på en socket */ int main ( void ) { int strumpa ; struct sockaddr_insa ; _ int bytes_sent ; const char * buffer = "Hej!" ; int buffer_length ; buffer_length = strlen ( buffert ) + 1 ; sock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ); if ( strumpa == -1 ) { printf ( "Fel vid skapande av socket" ); returnera 0 ; } sa . sin_familj = PF_INET ; sa . sin_addr . s_addr = htonl ( 0x7F000001 ); sa . sin_port = htons ( 7654 ); bytes skickade = skickat till ( strumpa , buffert , strlen ( buffert ) + 1 , 0 , ( struct sockaddr * ) & sa , sizeof ( struct sockaddr_in ) ); if ( bytes_sent < 0 ) printf ( "Fel vid sändning av paket: %s \n " , strerror ( errno ) ); nära ( strumpa ); returnera 0 ; }"De jure" definitionen av socket-gränssnittet i POSIX- standarden , mer känd som: