logo

Programování socketů v C

Programování zásuvek je způsob propojení dvou uzlů v síti za účelem vzájemné komunikace. Jeden soket (uzel) naslouchá na konkrétním portu na IP, zatímco druhý soket zasahuje do druhého, aby vytvořil spojení. Server tvoří soket posluchače, zatímco klient oslovuje server.
Programování soketů je široce používáno v aplikacích pro rychlé zasílání zpráv, binární streamování a spolupráce na dokumentech, online streamovací platformy atd.

Příklad

V tomto programu C si vyměňujeme jednu pozdravnou zprávu mezi serverem a klientem, abychom demonstrovali model klient/server.

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('%sn' buffer);  send(new_socket hello strlen(hello) 0);  printf('Hello message sentn');  // 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(  'nInvalid address/ Address not supported n');  return -1;  }  if ((status  = connect(client_fd (struct sockaddr*)&serv_addr  sizeof(serv_addr)))  < 0) {  printf('nConnection Failed n');  return -1;  }    // subtract 1 for the null  // terminator at the end  send(client_fd hello strlen(hello) 0);  printf('Hello message sentn');  valread = read(client_fd buffer  1024 - 1);   printf('%sn' buffer);  // closing the connected socket  close(client_fd);  return 0; } 


Kompilace



gcc client.c -o clientgcc server.c -o server


Výstup

Client:Hello message sentHello from serverServer:Hello from clientHello message sent

Komponenty soketového programování

1. Zásuvky

Zásuvky jsou jednou ze základních komponent používaných programem pro přístup k síti pro komunikaci s jinými procesy/uzly v síti. Je to prostě kombinace IP adresy a čísla portu, který funguje jako koncový bod pro komunikaci.
Příklad: 192.168.1.1:8080 kde dvě části oddělené dvojtečkou představují IP adresa (192.168.1.1) a číslo portu (8080).

Typy zásuvek:

  • TCP Socket (Stream Socket): Poskytuje spolehlivou komunikaci založenou na připojení (t.j. TCP protokol ).
  • Zásuvka UDP (zásuvka pro datagram): Poskytuje rychlejší, ale nespolehlivou komunikaci bez připojení (tj. protokol UDP ).

2. Model klient-server

The model klient-server odkazuje na architekturu používanou v programování soketů, kde klient a server vzájemně spolupracují za účelem výměny informací nebo služeb. Tato architektura umožňuje klientovi odesílat požadavky na služby a serveru zpracovávat a odesílat odpovědi na tyto požadavky na služby.

Stavový diagram pro model serveru a klienta

Programování socketů v CStavový diagram pro serverový a klientský model Socketu

Socket programování v C je výkonný způsob, jak zvládnout síťovou komunikaci.

Vytvoření procesu na straně serveru

Server se vytvoří pomocí následujících kroků:

tojson java

1. Vytvoření zásuvky

Tento krok zahrnuje vytvoření soketu pomocí funkce socket().

Parametry:

  • sockfd: deskriptor soketu celé číslo (jako popisovač souboru)
  • doména: celé číslo určuje komunikační doménu. Pro komunikaci mezi procesy na stejném hostiteli používáme AF_ LOCAL, jak je definováno ve standardu POSIX. Pro komunikaci mezi procesy na různých hostitelích připojených přes IPV4 používáme AF_INET a AF_I NET 6 pro procesy spojené přes IPV6.
  • typ: typ komunikace
    SOCK_STREAM: TCP (orientované na spolehlivé připojení)
    SOCK_DGRAM: UDP (nespolehlivý bez připojení)
  • protokol: Hodnota protokolu pro internetový protokol (IP), která je 0. Toto je stejné číslo, které se zobrazuje v poli protokolu v hlavičce IP paketu. (další podrobnosti naleznete v protokolech man)
C
sockfd = socket(domain type protocol) 

2. Nastavte zásuvku opt

To pomáhá při manipulaci s volbami pro soket, na který odkazuje deskriptor souboru sockfd. Toto je zcela volitelné, ale pomáhá při opětovném použití adresy a portu. Zabraňuje chybám jako: adresa se již používá.

C
setsockopt(sockfd level optname optval socklen_t optlen); 

3. Svázat

Po vytvoření soketu funkce bind() naváže soket na adresu a číslo portu zadané v addr (vlastní datová struktura). V ukázkovém kódu vážeme server k localhost, proto používáme INADDR_ANY k určení IP adresy.

C++
bind(sockfd sockaddr *addr socklen_t addrlen); 

Parametry:

  • sockfd : deskriptor souboru socket vytvořený pomocí funkce socket().
  • adr : ukazatel na strukturu sockaddr, která obsahuje IP adresu a číslo portu pro vazbu soketu.
  • addrlen : délka struktury addr.

4. Poslouchejte

V tomto kroku server používá funkci listen(), která uvede serverový soket do pasivního režimu, kde čeká, až se klient přiblíží k serveru a naváže spojení. Backlog definuje maximální délku, na kterou může narůst fronta čekajících připojení pro sockfd. Pokud požadavek na připojení dorazí, když je fronta plná, klient může obdržet chybu s označením ECONNREFUSED.

C
listen(sockfd backlog); 

Parametry :

  • sockfd : deskriptor souboru socket vytvořený pomocí funkce socket().
  • nevyřízených : číslo představující velikost fronty, která drží nevyřízená připojení, zatímco server čeká na přijetí připojení.

5. Přijměte

V tomto kroku server extrahuje první požadavek na připojení z fronty čekajících připojení pro naslouchající soket sockfd vytvoří nový připojený soket pomocí přijmout() a vrátí nový deskriptor souboru odkazující na daný soket. V tomto okamžiku je navázáno spojení mezi klientem a serverem a jsou připraveni k přenosu dat.

C
new_socket= accept(sockfd sockaddr *addr socklen_t *addrlen); 

Parametry:

  • sockfd : deskriptor souboru soketu vrácený funkcemi socket() a bind().
  • adr : ukazatel na strukturu sockaddr, která bude obsahovat IP adresu klienta a číslo portu.
  • addrlen : ukazatel na proměnnou, která určuje délku struktury adresy.

6. Odeslat/Přijmout

V tomto kroku může server odesílat nebo přijímat data od klienta.

Poslat(): k odeslání dat klientovi

C
send(sockfd *buf len flags); 

Parametry:

  • sockfd : deskriptor souboru socket vrácený funkcí socket().
  • buf : ukazatel na vyrovnávací paměť obsahující data, která mají být odeslána.
  • pouze : počet bajtů dat k odeslání.
  • vlajky : celé číslo určující různé možnosti, jak jsou data odesílána, obvykle se pro výchozí chování používá 0.

Přijmout(): získat data od klienta.

C
recv( sockfd *buf len flags); 

Parametry:

  • sockfd : deskriptor souboru socket vrácený funkcí socket().
  • buf : ukazatel na vyrovnávací paměť obsahující data, která mají být uložena.
  • pouze : počet bajtů dat k odeslání.
  • vlajky : celé číslo určující různé možnosti, jak jsou data odesílána, obvykle se pro výchozí chování používá 0.

6. Zavřete

Po dokončení výměny informací server uzavře soket pomocí funkce close() a uvolní systémové prostředky.

C
close(fd); 

Parametry:

  • fd: deskriptor souboru soketu.

Vytvoření procesu na straně klienta

Při vytváření procesu na straně klienta postupujte podle následujících kroků:

1. Zapojení do zásuvky

Tento krok zahrnuje vytvoření soketu, který se provádí stejným způsobem jako vytváření soketu serveru

2. Připojte

Systémové volání connect() připojí soket, na který odkazuje deskriptor souboru sockfd, na adresu zadanou addr. Adresa a port serveru jsou uvedeny v addr.

C++
connect(sockfd sockaddr *addr socklen_t addrlen); 

Parametry

  • sockfd : deskriptor souboru socket vrácený funkcí socket().
  • adr : ukazatel na strukturu sockaddr obsahující IP adresu serveru a číslo portu.
  • addrlen : velikost adr.

3. Odeslat/Přijmout

V tomto kroku může klient odesílat nebo přijímat data ze serveru, což se provádí pomocí funkcí send() a recieve() podobně jako server odesílá/přijímá data od klienta.

4. Zavřete

Jakmile je výměna informací dokončena, klient také musí zavřít vytvořený soket a uvolnit systémové prostředky pomocí funkce close() stejným způsobem jako server.

Běžné problémy a jejich opravy v programování soketů

  • Selhání připojení: Abychom se vyhnuli selhání připojení, měli bychom zajistit, že se klient pokouší připojit ke správnému IP adresa a port .
  • Chyby vázání portu: K těmto chybám dochází, když je port již používán jinou aplikací. V tomto scénáři vazba na tento port selže. Zkuste použít jiný port nebo zavřete předchozí aplikaci používající port.
  • Blokovací zásuvky: Ve výchozím nastavení jsou zásuvky blokovány. To znamená, že volání jako accept() nebo recv() budou čekat neomezeně dlouho, pokud není k dispozici žádné připojení klienta nebo data. V případě potřeby můžete zásuvku nastavit do neblokovacího režimu.
Vytvořit kvíz