Linux IO多路复用之epoll网络编程

前言

本章节是用基本的Linux基本函数加上epoll调用编写一个完整的服务器和客户端例子,可在Linux上运行,客户端和服务端的功能如下:

  • 客户端从标准输入读入一行,发送到服务端
  • 服务端从网络读取一行,然后输出到客户端
  • 客户端收到服务端的响应,输出这一行到标准输出

服务端

代码如下:

  #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];    /* 设置每个进程允许打开的最大文件数 */    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;    }    /* 创建 epoll 句柄,把监听 socket 加入到 epoll 集合里 */    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=%dn", listenfd);      return -1;    }    curfds = 1;    printf("epollserver startup,port %d, max connection is %d, backlog is %dn", servPort, MAXEPOLLSIZE, listenq);    for (;;) {      /* 等待有事件发生 */      nfds = epoll_wait(kdpfd, events, curfds, -1);      if (nfds == -1)      {        perror("epoll_wait");        continue;      }      /* 处理所有事件 */      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:%dn", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);          printf("%d:%s", ++acceptCount, buf);            if (curfds >= MAXEPOLLSIZE) {            fprintf(stderr, "too many connection, more than %dn", 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: %sn", connfd, strerror(errno));            return -1;          }          curfds++;          continue;        }         // 处理客户端请求        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);//读取客户端socket流    if (nread == 0) {      printf("client close the connectionn");      close(connfd);      return -1;    }     if (nread < 0) {      perror("read error");      close(connfd);      return -1;    }      write(connfd, buf, nread);//响应客户端     return 0;  }

编译

编译和启动服务端

  gcc epollserver.c -o epollserver  ./epollserver

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。如果你想了解更多相关内容请查看下面相关链接

参与评论