Skip to content

Instantly share code, notes, and snippets.

@silvioprog
Forked from vndmtrx/epoll_sock.c
Last active September 13, 2015 05:03
Show Gist options
  • Select an option

  • Save silvioprog/c58853b91e32eddeee09 to your computer and use it in GitHub Desktop.

Select an option

Save silvioprog/c58853b91e32eddeee09 to your computer and use it in GitHub Desktop.

Revisions

  1. Eduardo Rolim revised this gist Aug 20, 2012. 1 changed file with 12 additions and 11 deletions.
    23 changes: 12 additions & 11 deletions epoll_sock.c
    Original file line number Diff line number Diff line change
    @@ -101,6 +101,17 @@ static int cria_evento(int epoll, int sock) {
    return status;
    }

    /* Cabeçalhos para funções implementadas depois de main().
    */
    static void ev_clienteconecta(int epoll, int sock);
    static void ev_clientedadosdisp(int sock);

    /* Variáveis globais. Declaradas assim para poderem ser liberadas caso
    * uma interrupção termine a aplicação.
    */
    int servidor, i_epoll;
    struct epoll_event *i_eventos;

    /* void sigcallback(int sig)
    * Função para capturar sinais de término da aplicação e
    * efetuar a limpeza dos recursos alocados.
    @@ -109,20 +120,10 @@ void sigcallback(int sig) {
    close(servidor);
    close(i_epoll);
    free(i_eventos);
    fprintf(stdout, "Server Terminated.\n");
    exit(sig);
    }

    /* Cabeçalhos para funções implementadas depois de main().
    */
    static void ev_clienteconecta(int epoll, int sock);
    static void ev_clientedadosdisp(int sock);

    /* Variáveis globais. Declaradas assim para poderem ser liberadas caso
    * uma interrupção termine a aplicação.
    */
    int servidor, i_epoll;
    struct epoll_event i_eventos;

    /* int main(int argc, char *argv[])
    * Função principal da aplicação, onde o looping de eventos das notificações
    * gerenciadas pelo epoll() está inserido.
  2. Eduardo Rolim revised this gist Aug 18, 2012. 1 changed file with 25 additions and 4 deletions.
    29 changes: 25 additions & 4 deletions epoll_sock.c
    Original file line number Diff line number Diff line change
    @@ -15,6 +15,7 @@

    #include <stdio.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    @@ -100,18 +101,38 @@ static int cria_evento(int epoll, int sock) {
    return status;
    }

    /* void sigcallback(int sig)
    * Função para capturar sinais de término da aplicação e
    * efetuar a limpeza dos recursos alocados.
    */
    void sigcallback(int sig) {
    close(servidor);
    close(i_epoll);
    free(i_eventos);
    exit(sig);
    }

    /* Cabeçalhos para funções implementadas depois de main().
    */
    static void ev_clienteconecta(int epoll, int sock);
    static void ev_clientedadosdisp(int sock);

    /* Variáveis globais. Declaradas assim para poderem ser liberadas caso
    * uma interrupção termine a aplicação.
    */
    int servidor, i_epoll;
    struct epoll_event i_eventos;

    /* int main(int argc, char *argv[])
    * Função principal da aplicação, onde o looping de eventos das notificações
    * gerenciadas pelo epoll() está inserido.
    */
    int main(int argc, char *argv[]) {
    int servidor, status, i_epoll;
    struct epoll_event *eventos;
    int status;

    // Seta manipuladores para os sinais de término da aplicação.
    signal(SIGINT, sigcallback); // Ctrl+C
    signal(SIGTERM, sigcallback); // Comando "kill"

    // Criando o servidor na porta designada
    servidor = cria_servidor(PORTA);
    @@ -138,7 +159,6 @@ int main(int argc, char *argv[]) {
    cria_evento(i_epoll, servidor);

    // Iniciando o looping de eventos para epoll()
    struct epoll_event *i_eventos;
    i_eventos = calloc(QTDEVENTOS, sizeof(struct epoll_event));
    while (1) {
    int n, i;
    @@ -171,8 +191,9 @@ int main(int argc, char *argv[]) {
    }
    }
    }
    free(eventos);
    free(i_eventos);
    close(servidor);
    close(i_epoll);
    return EXIT_SUCCESS;
    }

  3. Eduardo Rolim revised this gist Aug 17, 2012. 1 changed file with 8 additions and 8 deletions.
    16 changes: 8 additions & 8 deletions epoll_sock.c
    Original file line number Diff line number Diff line change
    @@ -81,11 +81,11 @@ static int nonblock_socket(int sock) {
    return 0;
    }

    /* int cria_evento(int epoll, int socket)
    /* static int cria_evento(int epoll, int socket)
    * Função refatorada de main() para adicionar novos sockets ao epoll() usando o
    * método Edge-triggered através da flag EPOLLET.
    */
    int cria_evento(int epoll, int sock) {
    static int cria_evento(int epoll, int sock) {
    int status;
    struct epoll_event i_evento;

    @@ -102,8 +102,8 @@ int cria_evento(int epoll, int sock) {

    /* Cabeçalhos para funções implementadas depois de main().
    */
    void ev_clienteconecta(int epoll, int sock);
    void ev_clientedadosdisp(int sock);
    static void ev_clienteconecta(int epoll, int sock);
    static void ev_clientedadosdisp(int sock);

    /* int main(int argc, char *argv[])
    * Função principal da aplicação, onde o looping de eventos das notificações
    @@ -176,11 +176,11 @@ int main(int argc, char *argv[]) {
    return EXIT_SUCCESS;
    }

    /* void ev_clienteconecta(int epoll, int sock)
    /* static void ev_clienteconecta(int epoll, int sock)
    * Função referenciada cada vez que novos clientes estão aguardando por conexão
    * ao servidor.
    */
    void ev_clienteconecta(int epoll, int sock) {
    static void ev_clienteconecta(int epoll, int sock) {
    struct sockaddr in_addr;
    socklen_t in_len;
    int status, cliente;
    @@ -221,14 +221,14 @@ void ev_clienteconecta(int epoll, int sock) {
    }
    }

    /* void ev_clientedadosdisp(int sock)
    /* static void ev_clientedadosdisp(int sock)
    * Função referenciada cada vez que há dados em um cliente aguardando para ser
    * lido com read().
    * Aviso: operação unsafe em write() precisa ser corrigida para evitar que buffers
    * maiores que 512 bytes disparem duas chamadas seguidas, o que gerará o aviso
    * EWOULDBLOCK na mesma.
    */
    void ev_clientedadosdisp(int sock) {
    static void ev_clientedadosdisp(int sock) {
    int concluido = 0;
    ssize_t qtd;
    char buf[TAMBUFFER];
  4. Eduardo Rolim created this gist Aug 17, 2012.
    262 changes: 262 additions & 0 deletions epoll_sock.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,262 @@
    /* Copyright 2012 Eduardo Rolim
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/epoll.h>
    #include <errno.h>

    #define TAMBUFFER 512
    #define PORTA 9899
    #define QTDEVENTOS 64
    #define CHUCKNORRIS(x) perror(x); exit(EXIT_FAILURE);
    #define DEBUG(x) fprintf(stdout, "%d: %s", __LINE__, x)

    /* static int cria_servidor(int porta)
    * Função para criação simples de socket servidor para IPv4.
    * Para criação de um socket para funcionar tanto em IPv4 quanto em IPv6
    * Consulte a seguinte página: http://linux.die.net/man/3/getaddrinfo
    */
    static int cria_servidor(int porta) {
    struct sockaddr_in endereco;
    int sock, status;

    // Criando o socket servidor
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
    CHUCKNORRIS("Erro de criação do socket.");
    }

    memset(&endereco, 0, sizeof(endereco));
    endereco.sin_family = AF_INET;
    endereco.sin_port = htons(porta);
    endereco.sin_addr.s_addr = htonl(INADDR_ANY);

    // Linkando o socket a um endereço (como é um servidor, nenhum em especial)
    status = bind(sock, (struct sockaddr*) &endereco, sizeof(endereco));
    if (status != 0) {
    CHUCKNORRIS("Erro de ligação do socket.");
    }

    return sock;
    }

    /* static int nonblock_socket(int sock)
    * Função que converte o socket para o modo não bloqueante.
    */
    static int nonblock_socket(int sock) {
    int flags, status;

    flags = fcntl(sock, F_GETFL, 0);
    if (flags == -1) {
    DEBUG("fcntl get");
    return -1;
    }

    flags |= O_NONBLOCK;
    status = fcntl (sock, F_SETFL, flags);
    if (status == -1) {
    DEBUG("fcntl set");
    return -1;
    }

    return 0;
    }

    /* int cria_evento(int epoll, int socket)
    * Função refatorada de main() para adicionar novos sockets ao epoll() usando o
    * método Edge-triggered através da flag EPOLLET.
    */
    int cria_evento(int epoll, int sock) {
    int status;
    struct epoll_event i_evento;

    // Criando um evento em "epoll" para o descritor "sock"
    i_evento.data.fd = sock;
    i_evento.events = EPOLLIN | EPOLLET;
    status = epoll_ctl (epoll, EPOLL_CTL_ADD, sock, &i_evento);
    if (status == -1) {
    CHUCKNORRIS("cria epoll_ctl");
    }

    return status;
    }

    /* Cabeçalhos para funções implementadas depois de main().
    */
    void ev_clienteconecta(int epoll, int sock);
    void ev_clientedadosdisp(int sock);

    /* int main(int argc, char *argv[])
    * Função principal da aplicação, onde o looping de eventos das notificações
    * gerenciadas pelo epoll() está inserido.
    */
    int main(int argc, char *argv[]) {
    int servidor, status, i_epoll;
    struct epoll_event *eventos;

    // Criando o servidor na porta designada
    servidor = cria_servidor(PORTA);

    // Mudando o servidor para não bloqueante
    status = nonblock_socket(servidor);
    if (status == -1) {
    exit(EXIT_FAILURE);
    }

    // Iniciando a escura por novas conexões
    status = listen(servidor, SOMAXCONN);
    if (status != 0) {
    CHUCKNORRIS("Erro ao setar socket no modo escuta.");
    }

    // Criando uma instância de epoll()
    i_epoll = epoll_create1 (0);
    if (i_epoll == -1) {
    CHUCKNORRIS("epoll_create1");
    }

    // Criando um evento para monitorar a chegada de novas conexões
    cria_evento(i_epoll, servidor);

    // Iniciando o looping de eventos para epoll()
    struct epoll_event *i_eventos;
    i_eventos = calloc(QTDEVENTOS, sizeof(struct epoll_event));
    while (1) {
    int n, i;
    n = epoll_wait(i_epoll, i_eventos, QTDEVENTOS, -1);
    for (i = 0; i < n; i++) {
    if ((i_eventos[i].events & EPOLLERR) ||
    (i_eventos[i].events & EPOLLHUP) ||
    (!(i_eventos[i].events & EPOLLIN))) {
    /* Algum erro ocorreu com o socket ou ele não está pronto.
    * Segundo o man de epoll_ctl, epoll() sempre notificará para os eventos
    * EPOLLERR e EPOLLHUP, independente de setados ou não.
    */
    fprintf(stderr, "erro no epoll");
    close(i_eventos[i].data.fd);
    continue;
    } else if (servidor == i_eventos[i].data.fd) {
    /* Recebemos uma notificação no socket servidor, o que significa que há
    * conexões a serem tratadas.
    */
    ev_clienteconecta(i_epoll, i_eventos[i].data.fd);
    continue;
    } else {
    /* Recebemos uma notificação de que há dados para serem lidos em um
    * socket cliente. Precisamos ler todos os dados disponíveis, já que
    * estamos rodando no modo edge-triggered e não receberemos novas
    * notificações para dados ainda não lidos.
    */
    ev_clientedadosdisp(i_eventos[i].data.fd);
    continue;
    }
    }
    }
    free(eventos);
    close(servidor);
    return EXIT_SUCCESS;
    }

    /* void ev_clienteconecta(int epoll, int sock)
    * Função referenciada cada vez que novos clientes estão aguardando por conexão
    * ao servidor.
    */
    void ev_clienteconecta(int epoll, int sock) {
    struct sockaddr in_addr;
    socklen_t in_len;
    int status, cliente;
    char host[NI_MAXHOST], porta[NI_MAXSERV];

    while (1) {
    in_len = sizeof(in_addr);
    cliente = accept(sock, &in_addr, &in_len);
    if (cliente == -1) {
    if ((errno == EAGAIN) ||
    (errno == EWOULDBLOCK)) {
    //Todas as novas conexões processadas.
    break;
    } else {
    perror("accept");
    break;
    }
    }

    status = getnameinfo(&in_addr, in_len,
    host, sizeof(host),
    porta, sizeof(porta),
    NI_NUMERICHOST | NI_NUMERICSERV);
    if (status == 0) {
    printf("Conexao cliente aceita no descritor %d "
    "(host='%s', porta='%s').\n", cliente, host, porta);
    }

    // Tornando a conexão cliente não bloqueante
    status = nonblock_socket(cliente);
    if (status == -1) {
    exit(EXIT_FAILURE);
    }

    //Adicionando a conexão ao epoll
    cria_evento(epoll, cliente);
    continue;
    }
    }

    /* void ev_clientedadosdisp(int sock)
    * Função referenciada cada vez que há dados em um cliente aguardando para ser
    * lido com read().
    * Aviso: operação unsafe em write() precisa ser corrigida para evitar que buffers
    * maiores que 512 bytes disparem duas chamadas seguidas, o que gerará o aviso
    * EWOULDBLOCK na mesma.
    */
    void ev_clientedadosdisp(int sock) {
    int concluido = 0;
    ssize_t qtd;
    char buf[TAMBUFFER];

    while (1) {
    qtd = read(sock, buf, sizeof(buf));
    if (qtd == -1) {
    // Se o erro for EAGAIN significa que todos os dados foram lidos.
    if (errno != EAGAIN) {
    perror ("read");
    concluido = 1;
    }
    break;
    } else if (qtd == 0) {
    // Fim do arquivo. Socket foi fechado pelo cliente remoto.
    concluido = 1;
    break;
    }

    // Enviando de volta para o cliente remoto (unsafe).
    qtd = write(sock, buf, qtd);
    if (qtd == -1) {
    perror("write");
    }
    }

    if (concluido) {
    printf("Conexao cliente terminada no descritor %d.\n", sock);
    close(sock);
    }
    }