Socket-programmering i C
Socket programmering er en måte å koble sammen to noder på et nettverk for å kommunisere med hverandre. En socket (node) lytter på en bestemt port på en IP mens den andre socket når ut til den andre for å danne en forbindelse. Serveren danner lytterkontakten mens klienten når ut til serveren.
Socket-programmering er mye brukt i direktemeldingsapplikasjoner binær streaming og dokumentsamarbeid online streamingplattformer etc.
Eksempel
I dette C-programmet utveksler vi en hei-melding mellom server og klient for å demonstrere klient/server-modellen.
server.c
C #include #include #include #include #include #include #define PORT 8080 int main ( int argc char const * argv []) { int server_fd new_socket ; ssize_t valread ; struct sockaddr_in address ; int opt = 1 ; socklen_t addrlen = sizeof ( address ); char buffer [ 1024 ] = { 0 }; char * hello = 'Hello from server' ; // Creating socket file descriptor if (( server_fd = socket ( AF_INET SOCK_STREAM 0 )) < 0 ) { perror ( 'socket failed' ); exit ( EXIT_FAILURE ); } // Forcefully attaching socket to the port 8080 if ( setsockopt ( server_fd SOL_SOCKET SO_REUSEADDR | SO_REUSEPORT & opt sizeof ( opt ))) { perror ( 'setsockopt' ); exit ( EXIT_FAILURE ); } address . sin_family = AF_INET ; address . sin_addr . s_addr = INADDR_ANY ; address . sin_port = htons ( PORT ); // Forcefully attaching socket to the port 8080 if ( bind ( server_fd ( struct sockaddr * ) & address sizeof ( address )) < 0 ) { perror ( 'bind failed' ); exit ( EXIT_FAILURE ); } if ( listen ( server_fd 3 ) < 0 ) { perror ( 'listen' ); exit ( EXIT_FAILURE ); } if (( new_socket = accept ( server_fd ( struct sockaddr * ) & address & addrlen )) < 0 ) { perror ( 'accept' ); exit ( EXIT_FAILURE ); } // subtract 1 for the null // terminator at the end valread = read ( new_socket buffer 1024 - 1 ); printf ( '%s n ' buffer ); send ( new_socket hello strlen ( hello ) 0 ); printf ( 'Hello message sent n ' ); // closing the connected socket close ( new_socket ); // closing the listening socket close ( server_fd ); return 0 ; }
klient.c
C #include #include #include #include #include #define PORT 8080 int main ( int argc char const * argv []) { int status valread client_fd ; struct sockaddr_in serv_addr ; char * hello = 'Hello from client' ; char buffer [ 1024 ] = { 0 }; if (( client_fd = socket ( AF_INET SOCK_STREAM 0 )) < 0 ) { printf ( ' n Socket creation error n ' ); return -1 ; } serv_addr . sin_family = AF_INET ; serv_addr . sin_port = htons ( PORT ); // Convert IPv4 and IPv6 addresses from text to binary // form if ( inet_pton ( AF_INET '127.0.0.1' & serv_addr . sin_addr ) <= 0 ) { printf ( ' n Invalid address/ Address not supported n ' ); return -1 ; } if (( status = connect ( client_fd ( struct sockaddr * ) & serv_addr sizeof ( serv_addr ))) < 0 ) { printf ( ' n Connection Failed n ' ); return -1 ; } // subtract 1 for the null // terminator at the end send ( client_fd hello strlen ( hello ) 0 ); printf ( 'Hello message sent n ' ); valread = read ( client_fd buffer 1024 - 1 ); printf ( '%s n ' buffer ); // closing the connected socket close ( client_fd ); return 0 ; }
Kompilere
gcc client.c -o clientgcc server.c -o server
ProduksjonClient:Hello message sentHello from serverServer:Hello from clientHello message sentKomponenter i socket-programmering
1. Stikkontakter
Stikkontakter er en av kjernekomponentene som brukes av programmet for å få tilgang til nettverket for å kommunisere med andre prosesser/noder over nettverket. Det er ganske enkelt en kombinasjon av en IP-adresse og et portnummer som fungerer som et endepunkt for kommunikasjon.
Eksempel: 192.168.1.1:8080 der de to delene atskilt med kolon representerer IP-adresse ( 192.168.1.1 ) og den portnummer ( 8080 ).Sokkeltyper:
- TCP-kontakt (Stream-kontakt): Gir pålitelig tilkoblingsbasert kommunikasjon (dvs. TCP-protokoll ).
- UDP-kontakt (Datagram-kontakt): Gir tilkoblingsløs kommunikasjon raskere, men upålitelig (dvs. UDP-protokoll ).
2. Klient-servermodell
De klient-server-modell refererer til arkitekturen som brukes i socket-programmering der en klient og en server samhandler med hverandre for å utveksle informasjon eller tjenester. Denne arkitekturen lar klienten sende tjenesteforespørsler og serveren til å behandle og sende svar på disse tjenesteforespørslene.
Statusdiagram for server- og klientmodell
Tilstandsdiagram for server- og klientmodell av Socket Socket-programmering i C er en kraftig måte å håndtere nettverkskommunikasjon på.
Opprette en prosess på serversiden
Serveren opprettes ved å bruke følgende trinn:
1. Socketoppretting
Dette trinnet involverer opprettelsen av socket ved hjelp av socket()-funksjonen.
Parametere:
- sockfd: socket descriptor et heltall (som et filhåndtak)
- domene: heltall spesifiserer kommunikasjonsdomene. Vi bruker AF_ LOCAL som definert i POSIX-standarden for kommunikasjon mellom prosesser på samme vert. For å kommunisere mellom prosesser på forskjellige verter koblet til med IPV4 bruker vi AF_INET og AF_I NET 6 for prosesser koblet med IPV6.
- type: kommunikasjonstype
SOCK_STREAM: TCP (pålitelig tilkoblingsorientert)
SOCK_DGRAM: UDP (upålitelig tilkoblingsløs) - protokoll: Protokollverdi for Internet Protocol(IP) som er 0. Dette er det samme tallet som vises i protokollfeltet i IP-overskriften til en pakke.(man-protokoller for flere detaljer)
sockfd = socket ( domain type protocol )
2. Sett socket opt
Dette hjelper til med å manipulere alternativer for sokkelen referert av filbeskrivelsen sockfd. Dette er helt valgfritt, men det hjelper med gjenbruk av adresse og port. Forhindrer feil som: adresse som allerede er i bruk.
C setsockopt ( sockfd level optname optval socklen_t optlen );
3. Bind
Etter opprettelsen av socket binder bind()-funksjonen socket til adressen og portnummeret spesifisert i addr(egendefinert datastruktur). I eksempelkoden binder vi serveren til den lokale verten, og bruker derfor INADDR_ANY for å spesifisere IP-adressen.
C++ bind ( sockfd sockaddr * addr socklen_t addrlen );
Parametere:
- sockfd : socket-filbeskrivelse opprettet ved hjelp av socket()-funksjonen.
- adr : peker til en struct sockaddr som inneholder IP-adressen og portnummeret for å binde kontakten.
- addrlen : lengden på addr-strukturen.
4. Lytt
I dette trinnet bruker serveren listen()-funksjonen som setter serversocket i en passiv modus der den venter på at klienten skal nærme seg serveren for å opprette en tilkobling. Etterslepet definerer maksimal lengde som køen av ventende tilkoblinger for sockfd kan vokse til. Hvis en tilkoblingsforespørsel kommer når køen er full, kan klienten motta en feilmelding med en indikasjon på ECONNREFUSED.
C listen ( sockfd backlog );
Parametere :
- sockfd : socket-filbeskrivelse opprettet ved hjelp av socket()-funksjonen.
- etterslep : nummer som representerer størrelsen på køen som holder de ventende tilkoblingene mens serveren venter på å godta en tilkobling.
5. Godta
I dette trinnet trekker serveren ut den første tilkoblingsforespørselen fra køen av ventende tilkoblinger for lyttekontakten sockfd oppretter en ny tilkoblet kontakt ved å bruke akseptere() funksjon og returnerer en ny filbeskrivelse som refererer til den kontakten. På dette tidspunktet opprettes forbindelsen mellom klient og server, og de er klare til å overføre data.
C new_socket = accept ( sockfd sockaddr * addr socklen_t * addrlen );
Parametere:
- sockfd : socket-filbeskrivelse returnert av socket() og bind().
- adr : peker til en struct sockaddr som vil inneholde klientens IP-adresse og portnummer.
- addrlen : peker til en variabel som spesifiserer lengden på adressestrukturen.
6. Send/motta
I dette trinnet kan serveren sende eller motta data fra klienten.
Sende(): å sende data til klienten
C send ( sockfd * buf len flags );
Parametere:
- sockfd : socket-filbeskrivelse returnert av socket()-funksjonen.
- buff : peker til bufferen som inneholder dataene som skal sendes.
- bare : antall byte med data som skal sendes.
- flagg : heltall som spesifiserer ulike alternativer for hvordan dataene sendes, vanligvis 0 brukes for standard oppførsel.
Motta() : for å motta data fra klienten.
C recv ( sockfd * buf len flags );
Parametere:
- sockfd : socket-filbeskrivelse returnert av socket()-funksjonen.
- buff : peker til bufferen som inneholder dataene som skal lagres.
- bare : antall byte med data som skal sendes.
- flagg : heltall som spesifiserer ulike alternativer for hvordan dataene sendes, vanligvis 0 brukes for standard oppførsel.
6. Lukk
Etter at utvekslingen av informasjon er fullført, lukker serveren socket ved hjelp av close()-funksjonen og frigjør systemressursene.
C close ( fd );
Parametere:
- fd: filbeskrivelse av socket.
Opprette prosess på klientsiden
Følg trinnene nedenfor for å opprette en prosess på klientsiden:
1. Stikkontakt
Dette trinnet involverer opprettelsen av socket som gjøres på samme måte som serverens socketopprettelse
2. Koble til
Connect()-systemanropet kobler kontakten referert til av filbeskrivelsen sockfd til adressen spesifisert av addr. Serverens adresse og port er spesifisert i adr.
C++ connect ( sockfd sockaddr * addr socklen_t addrlen );
Parametere
- sockfd : socket-filbeskrivelse returnert av socket()-funksjonen.
- adr : peker til struct sockaddr som inneholder serverens IP-adresse og portnummer.
- addrlen : størrelse på adr.
3. Send/motta
I dette trinnet kan klienten sende eller motta data fra serveren som gjøres ved å bruke send() og recieve() funksjonene som ligner på hvordan serveren sender/mottar data fra klienten.
4. Lukk
Når utvekslingen av informasjon er fullført, må klienten også lukke den opprettede kontakten og frigi systemressursene ved å bruke close()-funksjonen på samme måte som serveren gjør.
Vanlige problemer og deres fikser i socket-programmering
- Tilkoblingsfeil: For å unngå tilkoblingsfeil bør vi sørge for at klienten prøver å koble til riktig IP-adresse og port .
- Portbindingsfeil: Disse feilene oppstår når en port allerede er i bruk av et annet program i dette scenariet, vil bindingen til den porten mislykkes. Prøv å bruke en annen port eller lukk det forrige programmet ved å bruke porten.
- Blokkering av stikkontakter: Som standard blokkerer stikkontakter. Dette betyr at anrop som accept() eller recv() vil vente på ubestemt tid hvis det ikke er noen klientforbindelse eller data. Du kan sette kontakten til ikke-blokkerende modus om nødvendig.