Abra um socket em C

Jinku Hu 12 outubro 2023
Abra um socket em C

Este artigo irá explicar vários métodos de como abrir um soquete em C.

Use as funções listen e accept para abrir um soquete em C

listen e accept são parte dos recursos de rede do UNIX, às vezes chamados de Sockets API, que implementa a funcionalidade principal para lidar com comunicações entre processos. Observe que um soquete é uma representação abstrata do terminal para o caminho de comunicação. Existem diferentes tipos de soquetes, por exemplo, Domínio Unix e Domínio da Internet, mas todos eles seguem procedimentos semelhantes para estabelecer a comunicação entre dois processos.

Lembre-se de que a rede é apenas a comunicação entre dois programas em execução. Em primeiro lugar, a função socket é chamada para criar o endpoint e o identificador correspondente. Os soquetes são tratados com descritores de arquivo semelhantes aos arquivos regulares ou canais em sistemas UNIX. Após a chamada socket, existem dois cenários dependendo do tipo de programa que está a desenvolver. Geralmente, temos um modelo cliente-servidor na comunicação de rede, onde um programa (servidor) precisa esperar que outros (clientes) se conectem a ele.

Neste exemplo, implementamos um programa do lado do servidor que precisa ouvir o ponto de extremidade específico e aceitar conexões de cliente. Neste cenário, a função bind é chamada após socket para ligar ao endereço específico onde o programa aceitará as conexões de entrada. Depois de bind, as funções listen e accept são chamadas para formar uma chamada tomada passiva. Observe que accept bloqueia o processo até que o processo diferente não estabeleça uma conexão usando a chamada de função connect.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define handle_error(msg) \
  do {                    \
    perror(msg);          \
    exit(EXIT_FAILURE);   \
  } while (0)

#define SOCKET_PATH "/tmp/my.sock"

enum { LISTEN_QUEUE = 100 };

int main(int argc, char *argv[]) {
  int listenfd, connfd;
  socklen_t len;
  struct sockaddr_un servaddr, cliaddr;

  listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (listenfd == -1) handle_error("socket");

  memset(&servaddr, 0, sizeof(servaddr));
  servaddr.sun_family = AF_UNIX;
  strncpy(servaddr.sun_path, SOCKET_PATH, sizeof(servaddr.sun_path) - 1);

  if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    handle_error("bind");

  if (listen(listenfd, LISTEN_QUEUE) == -1) handle_error("listen");

  len = sizeof(cliaddr);
  connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
  if (connfd == -1) handle_error("accept");

  unlink(SOCKET_PATH);
  exit(EXIT_SUCCESS);
}

Normalmente, a chamada accept é seguida pelo código que implementa o tratamento das conexões de entrada. Um programa externo que deseja estabelecer uma conexão com este servidor precisa saber o endereço e a porta. Assumindo que ambos são conhecidos pelo processo, ele invoca a função connect após a chamada de socket e esperançosamente se conecta ao processo do servidor. Uma vez que a conexão é estabelecida, ambos os processos podem escrever e ler no descritor de socket como tendo um canal bidirecional. Em cenários do mundo real, vários processos de cliente devem se conectar ao servidor simultaneamente; o código de manipulação de conexão deve ser implementado simultaneamente. Caso contrário, o servidor não poderá atender a mais de um cliente por vez.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define handle_error(msg) \
  do {                    \
    perror(msg);          \
    exit(EXIT_FAILURE);   \
  } while (0)

#define SOCKET_PATH "/tmp/my.sock"

enum { MAXLINE = 4096, LISTEN_QUEUE = 100 };

int main(int argc, char *argv[]) {
  int listenfd, connfd;
  socklen_t len;
  struct sockaddr_un servaddr, cliaddr;
  char buf[MAXLINE];

  listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (listenfd == -1) handle_error("socket");

  memset(&servaddr, 0, sizeof(servaddr));
  servaddr.sun_family = AF_UNIX;
  strncpy(servaddr.sun_path, SOCKET_PATH, sizeof(servaddr.sun_path) - 1);

  if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    handle_error("bind");

  if (listen(listenfd, LISTEN_QUEUE) == -1) handle_error("listen");

  len = sizeof(cliaddr);
  connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
  if (connfd == -1) handle_error("accept");

  size_t ret = read(connfd, buf, MAXLINE);
  if (ret == -1) handle_error("read");

  printf("read %d bytes\nmessage: %s\n", connfd, buf);

  unlink(SOCKET_PATH);
  exit(EXIT_SUCCESS);
}
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Artigo relacionado - C Networking