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('%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; }
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( '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; }
Compilando
gcc client.c -o clientgcc server.c -o server
Saída
Client:Hello message sentHello from serverServer:Hello from clientHello message sentComponentes 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
Diagrama de estado para modelo de servidor e cliente do SocketA 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:
instanciando java
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)
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.
Csetsockopt(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.
Clisten(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.
Cnew_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
Csend(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.
Crecv( 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.
Cclose(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.