zoukankan      html  css  js  c++  java
  • 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=%d\n", listenfd);
    return -1;
    }
    curfds = 1;

    printf("epollserver startup,port %d, max connection is %d, backlog is %d\n", 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:%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;
    }
    // 处理客户端请求
    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 connection\n");
    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

    至于客户端可以参考本文的Linux/Unix服务端和客户端Socket编程入门实例的echoclient例子下载编译。



  • 相关阅读:
    sleep、yield、wait、join的区别
    Java 配置全局线程池出错
    RemoteFallbackFactory扫包:No fallbackFactory instance of type class
    JAVA CopyOnWriteArrayList 说明
    Java四种线程池
    jvm总结
    CompletableFuture基本用法【转】
    Visual Studio 当前不会命中断点的问题
    安聊系统1.0发布
    libvpx编译
  • 原文地址:https://www.cnblogs.com/ggjucheng/p/2324974.html
Copyright © 2011-2022 走看看