Linux IO multiplexing epoll network programming

  • 2020-12-21 18:16:01
  • OfStack

preface

This chapter is to use the basic Linux basic function plus epoll call to write a complete server and client example, which can run on Linux. The functions of the client and server side are as follows:

The client reads in a line from standard input and sends it to the server The server reads 1 row from the network and outputs it to the client The client receives the response from the server, and outputs this line to standard output

The service side

The code is as follows:


#include <unistd.h>
#include <sys/types.h>    /* basic system data types */
#include <sys/socket.h>   /* basic socket definitions */
#include <netinet/in.h>   /* sockaddr_in{} and other Internet defns */
#include <arpa/inet.h>    /* inet(3) functions */
#include <sys/epoll.h> /* epoll function */
#include <fcntl.h>   /* nonblocking */
#include <sys/resource.h> /*setrlimit */
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#define MAXEPOLLSIZE 10000
#define MAXLINE 10240
int handle(int connfd);
int setnonblocking(int sockfd)
{
  if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1) {
    return -1;
  }
  return 0;
}
int main(int argc, char **argv)
{
  int servPort = 6888;
  int listenq = 1024;
  int listenfd, connfd, kdpfd, nfds, n, nread, curfds,acceptCount = 0;
  struct sockaddr_in servaddr, cliaddr;
  socklen_t socklen = sizeof(struct sockaddr_in);
  struct epoll_event ev;
  struct epoll_event events[MAXEPOLLSIZE];
  struct rlimit rt;
  char buf[MAXLINE];
  /*  Sets the maximum number of files that can be opened per process  */
  rt.rlim_max = rt.rlim_cur = MAXEPOLLSIZE;
  if (setrlimit(RLIMIT_NOFILE, &rt) == -1) 
  {
    perror("setrlimit error");
    return -1;
  }
  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET; 
  servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  servaddr.sin_port = htons (servPort);
  listenfd = socket(AF_INET, SOCK_STREAM, 0); 
  if (listenfd == -1) {
    perror("can't create socket file");
    return -1;
  }
  int opt = 1;
  setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
  if (setnonblocking(listenfd) < 0) {
    perror("setnonblock error");
  }
  if (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) == -1) 
  {
    perror("bind error");
    return -1;
  } 
  if (listen(listenfd, listenq) == -1) 
  {
    perror("listen error");
    return -1;
  }
  /*  create  epoll  Handle, put the listener  socket  To join the  epoll  In the collection  */
  kdpfd = epoll_create(MAXEPOLLSIZE);
  ev.events = EPOLLIN | EPOLLET;
  ev.data.fd = listenfd;
  if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listenfd, &ev) < 0) 
  {
    fprintf(stderr, "epoll set insertion error: fd=%d\n", listenfd);
    return -1;
  }
  curfds = 1;
  printf("epollserver startup,port %d, max connection is %d, backlog is %d\n", servPort, MAXEPOLLSIZE, listenq);
  for (;;) {
    /*  Wait for something to happen  */
    nfds = epoll_wait(kdpfd, events, curfds, -1);
    if (nfds == -1)
    {
      perror("epoll_wait");
      continue;
    }
    /*  Handle all events  */
    for (n = 0; n < nfds; ++n)
    {
      if (events[n].data.fd == listenfd) 
      {
        connfd = accept(listenfd, (struct sockaddr *)&cliaddr,&socklen);
        if (connfd < 0) 
        {
          perror("accept error");
          continue;
        }
        sprintf(buf, "accept form %s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);
        printf("%d:%s", ++acceptCount, buf);

        if (curfds >= MAXEPOLLSIZE) {
          fprintf(stderr, "too many connection, more than %d\n", MAXEPOLLSIZE);
          close(connfd);
          continue;
        } 
        if (setnonblocking(connfd) < 0) {
          perror("setnonblocking error");
        }
        ev.events = EPOLLIN | EPOLLET;
        ev.data.fd = connfd;
        if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, connfd, &ev) < 0)
        {
          fprintf(stderr, "add socket '%d' to epoll failed: %s\n", connfd, strerror(errno));
          return -1;
        }
        curfds++;
        continue;
      } 
      //  Process client requests 
      if (handle(events[n].data.fd) < 0) {
        epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].data.fd,&ev);
        curfds--;
      }
    }
  }
  close(listenfd);
  return 0;
}
int handle(int connfd) {
  int nread;
  char buf[MAXLINE];
  nread = read(connfd, buf, MAXLINE);// Read client socket flow 
  if (nread == 0) {
    printf("client close the connection\n");
    close(connfd);
    return -1;
  } 
  if (nread < 0) {
    perror("read error");
    close(connfd);
    return -1;
  }  
  write(connfd, buf, nread);// Response client  
  return 0;
}

compile

Compile and start the server


gcc epollserver.c -o epollserver
./epollserver

conclusion


Related articles: