Programação de soquete em C

Programação de soquete em C

Programação de soquete é uma forma de conectar dois nós em uma rede para se comunicarem entre si. Um soquete (nó) escuta em uma porta específica em um IP enquanto o outro soquete alcança o outro para formar uma conexão. O servidor forma o soquete do ouvinte enquanto o cliente acessa o servidor.
A programação de soquete é amplamente utilizada em aplicativos de mensagens instantâneas, streaming binário e colaborações de documentos, plataformas de streaming on-line, etc.

Exemplo

Neste programa C estamos trocando uma mensagem de alô entre servidor e cliente para demonstrar o modelo cliente/servidor.

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

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


Compilando

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


Saída

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

Componentes da programação de soquete

1. Soquetes

Soquetes são um dos principais componentes usados ​​pelo programa para acessar a rede e se comunicar com outros processos/nós na rede. É simplesmente uma combinação de um endereço IP e um número de porta que atua como um ponto final para comunicação.
Exemplo: 192.168.1.1:8080 onde as duas partes separadas por dois pontos representam o Endereço IP (192.168.1.1) e o número da porta (8080).

Tipos de soquete:

  • Soquete TCP (soquete de fluxo): Fornece comunicação confiável baseada em conexão (ou seja, Protocolo TCP ).
  • Soquete UDP (soquete de datagrama): Fornece comunicação sem conexão mais rápida, mas não confiável (ou seja, Protocolo UDP ).

2. Modelo Cliente-Servidor

O modelo cliente-servidor refere-se à arquitetura usada na programação de soquete onde um cliente e um servidor interagem entre si para trocar informações ou serviços. Esta arquitetura permite que o cliente envie solicitações de serviço e o servidor processe e envie respostas a essas solicitações de serviço.

Diagrama de estado para modelo de servidor e cliente

Programação de soquete em CDiagrama de estado para modelo de servidor e cliente do Socket

A programação de soquete em C é uma maneira poderosa de lidar com a comunicação em rede.

Criando um processo do lado do servidor

O servidor é criado usando as seguintes etapas:

1. Criação de soquete

Esta etapa envolve a criação do soquete usando a função socket().

Parâmetros:

  • meia: descritor de soquete um número inteiro (como um identificador de arquivo)
  • domínio: inteiro especifica o domínio de comunicação. Usamos AF_LOCAL conforme definido no padrão POSIX para comunicação entre processos no mesmo host. Para comunicação entre processos em diferentes hosts conectados por IPV4 usamos AF_INET e AF_I NET 6 para processos conectados por IPV6.
  • tipo: tipo de comunicação
    SOCK_STREAM: TCP (orientado a conexão confiável)
    SOCK_DGRAM: UDP (sem conexão não confiável)
  • protocolo: Valor do protocolo para Internet Protocol (IP) que é 0. Este é o mesmo número que aparece no campo de protocolo no cabeçalho IP de um pacote. (man protocols para mais detalhes)
C
   sockfd     =     socket  (  domain       type       protocol  )   

2. Definir opção de soquete

Isso ajuda na manipulação de opções para o soquete referido pelo descritor de arquivo sockfd. Isso é totalmente opcional, mas ajuda na reutilização de endereço e porta. Evita erros como: endereço já em uso.

C
   setsockopt  (  sockfd       level       optname       optval       socklen_t     optlen  );   

3. Vincular

Após a criação do soquete, a função bind() vincula o soquete ao endereço e número da porta especificados em addr (estrutura de dados personalizada). No código de exemplo, vinculamos o servidor ao host local, portanto, usamos INADDR_ANY para especificar o endereço IP.

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

Parâmetros:

  • meia : descritor de arquivo de soquete criado usando a função socket().
  • endereço : ponteiro para uma estrutura sockaddr que contém o endereço IP e o número da porta para vincular o soquete.
  • addrlen : comprimento da estrutura addr.

4. Ouça

Nesta etapa o servidor utiliza a função listen() que coloca o soquete do servidor em modo passivo onde espera o cliente se aproximar do servidor para fazer uma conexão. O backlog define o comprimento máximo que a fila de conexões pendentes para sockfd pode crescer. Se uma solicitação de conexão chegar quando a fila estiver cheia o cliente poderá receber um erro com a indicação ECONNREFUSED.

C
   listen  (  sockfd       backlog  );   

Parâmetros :

  • meia : descritor de arquivo de soquete criado usando a função socket().
  • pendências : número que representa o tamanho da fila que contém as conexões pendentes enquanto o servidor aguarda para aceitar uma conexão.

5. Aceite

Nesta etapa o servidor extrai a primeira solicitação de conexão da fila de conexões pendentes para o soquete de escuta sockfd cria um novo soquete conectado usando o comando aceitar() função e retorna um novo descritor de arquivo referente a esse soquete. Neste ponto a conexão é estabelecida entre cliente e servidor e eles estão prontos para transferir dados.

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

Parâmetros:

  • meia : descritor de arquivo de soquete retornado por socket() e bind().
  • endereço : ponteiro para uma estrutura sockaddr que conterá o endereço IP e o número da porta do cliente.
  • addrlen : ponteiro para uma variável que especifica o comprimento da estrutura de endereço.

6. Enviar/Receber

Nesta etapa o servidor pode enviar ou receber dados do cliente.

Enviar(): enviar dados ao cliente

C
   send  (  sockfd       *  buf       len       flags  );   

Parâmetros:

  • meia : descritor de arquivo de soquete retornado pela função socket().
  • bufante : ponteiro para o buffer que contém os dados a serem enviados.
  • apenas : número de bytes de dados a serem enviados.
  • bandeiras : inteiro especificando várias opções de como os dados são enviados normalmente 0 é usado para comportamento padrão.

Receber() : para receber os dados do cliente.

C
   recv  (     sockfd       *  buf       len       flags  );   

Parâmetros:

  • meia : descritor de arquivo de soquete retornado pela função socket().
  • bufante : ponteiro para o buffer que contém os dados a serem armazenados.
  • apenas : número de bytes de dados a serem enviados.
  • bandeiras : inteiro especificando várias opções de como os dados são enviados normalmente 0 é usado para comportamento padrão.

6. Fechar

Após a conclusão da troca de informações, o servidor fecha o soquete usando a função close() e libera os recursos do sistema.

C
   close  (  fd  );   

Parâmetros:

  • fd: descritor de arquivo do soquete.

Criando Processo do Lado do Cliente

Siga as etapas abaixo para criar um processo do lado do cliente:

1. Conexão de soquete

Esta etapa envolve a criação do soquete que é feita da mesma forma que a criação do soquete do servidor

2. Conecte-se

A chamada de sistema connect() conecta o soquete referido pelo descritor de arquivo sockfd ao endereço especificado por addr. O endereço e a porta do servidor são especificados em addr.

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

Parâmetros

  • meia : descritor de arquivo de soquete retornado pela função socket().
  • endereço : ponteiro para struct sockaddr contendo o endereço IP e o número da porta do servidor.
  • addrlen : tamanho do endereço.

3. Enviar/Receber

Nesta etapa, o cliente pode enviar ou receber dados do servidor, o que é feito usando as funções send() e recieve() semelhantes à forma como o servidor envia/recebe dados do cliente.

4. Fechar

Assim que a troca de informações for concluída, o cliente também precisa fechar o soquete criado e liberar os recursos do sistema usando a função close() da mesma forma que o servidor.

Problemas comuns e suas correções na programação de soquete

  • Falhas de conexão: Para evitar falhas de conexão devemos garantir que o cliente está tentando se conectar ao correto Endereço IP e porta .
  • Erros de ligação de porta: Esses erros ocorrem quando uma porta já está em uso por outro aplicativo. Nesse cenário, a ligação a essa porta falhará. Tente usar uma porta diferente ou feche o aplicativo anterior usando a porta.
  • Bloqueio de soquetes: Por padrão, os soquetes estão bloqueando. Isso significa que chamadas como accept() ou recv() aguardarão indefinidamente se não houver conexão ou dados do cliente. Você pode definir o soquete para o modo sem bloqueio, se necessário.
Criar questionário