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

    客户端代码

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

  • 相关阅读:
    Linux source命令
    pythoy 基础一: python的特点 if 语句 whlie语句
    linux 基础
    python的游戏之旅( 数字 字符串 列表 元组 字典 即为游戏职业)
    html 基础
    《SQL Server 2008 从入门到精通》 学习笔记 第五天
    [转载] Visual Studio 2010 MSDN Help Library文档位置、错误、重新安装及安装注意事项
    ASP.NET后台通过输出JavaScript弹出窗口小结
    网上搜集的webbrower的资料,很有借鉴价值
    解决MSSQL 2008不能用IP登录的问题 和 打开可以用SA登录SQL2008的方法
  • 原文地址:https://www.cnblogs.com/ajianbeyourself/p/5860042.html
Copyright © 2011-2022 走看看