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
    ", listenfd);
            return -1;
        }
        curfds = 1;
    
        printf("epollserver startup,port %d, max connection is %d, backlog is %d
    ", 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
    ", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);
                    printf("%d:%s", ++acceptCount, buf);
    
                    if (curfds >= MAXEPOLLSIZE) {
                        fprintf(stderr, "too many connection, more than %d
    ", 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
    ", 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
    ");
            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
  • 相关阅读:
    用python爬虫抓站的一些技巧总结
    使用python爬虫抓站的一些技巧总结:进阶篇
    Python模块学习:threading 多线程控制和处理
    Redis操作命令总结
    Redis介绍
    linux内核设计与实现笔记 进程调度
    Python常见数据结构整理
    Linux进程调度原理
    Python yield
    Qt之布局管理器
  • 原文地址:https://www.cnblogs.com/woniu201/p/11694644.html
Copyright © 2011-2022 走看看