linux网络编程----->高并发--->epoll多路I/O转接服务器

简介:

    做网络服务的时候并发服务端程序的编写必不可少。前端客户端应用程序是否稳定一部分取决于客户端自身,而更多的取决于服务器是否相应时间够迅速,够稳定.

    常见的linux并发服务器模型;

    • 多进程并发服务器

    • 多线程并发服务器

    • select多路I/O转接服务器

    • poll多路I/O转接服务器

    • epool多路I/O转接服务器.


    本次主要讨论poll多路I/转接并发服务器模型:

    wKioL1dlV3ORUbUGAAISrviO-QM731.png-wh_50


    前几章介绍完了多进程并发服务器,  多线程并发服务器selete多路I/O转接服务器,  poll多路I/O转接服务器, 本章开始介绍epoll(linux特有)多路I/O转接模型.

    由于多进程和多线程模型在实现中相对简单, 但由于其开销和CPU高度中比较大, 所以一般不用多线程和多进程来实现服务模型. select由于其跨平台, 但其最高上限默认为1024, 修改突破1024的话需要重新编译linux内核, poll虽然解决了select1024的限制, 但由于poll本质实现上也是轮循机制, 所以对于客户端的增加也会使效率降低.  本章来详细介绍epoll多路I/O转发服务器模型.

    epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。

目前epell是linux大规模并发网络程序中的热门首选模型。

    epoll除了提供select/poll那种IO事件的电平触发(Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。


    主要用到的API:

    int epoll_create(int size);

        简介: 创建epoll句柄

     参数: 监听数量

    

  int epoll_ctl(int epfd, int op, int fd, struct epoll_event* evvent);

     简单: 控制某个epoll监听的文件描述符上的事件, 注册, 修改, 删除

     epfd: 为epoll_create的句柄

     op:   动作, 用3个宏来表示

            EPOLL_CTL_ADD  注册新的fd到epfd

            EPOLL_CTL_MOD  修改已经注册的fd的监听事件

            EPOLL_CTL_DEL  从epfd删除一个fd

     event: 告诉内核需要监听的事件


        struct epoll_event{

            __uint32_t events;        /*Epoll events*/

            epoll_data_t data;        /"use data variable"/

        }


        typedef union epoll_data{

            void* ptr;

            int fd;

            uint32_t u32;

            uint64_t u64;

        }epoll_data_t;


    EPOLLIN 监听读事件,  EPOLLOUT 监听写事件   EPOLLERR 监听异常事件


    

    int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);

        函数说明: 等待所监控文件描述符上有事件的产生. 

        epfd: 为epoll_create的句柄

        events: 用来存内核得到事件的集合

        maxevents:  告之内核这个events有多大, 这个maxevents的值不能大于创建epoll_create()的size

        timeout: 等待时间 

                -1: 阻塞等待

                0: 立即返回, 非阻塞

                >0: 指定毫秒

        返回值: 成功返回有多少文件描述符就绪, 时间到时返回0, 出错返回-1


代码如下:

    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/epoll.h>
#include <ctype.h>
 
#define SERV_PORT 9096    //服务端口
#define CLIENT_MAX 1024   //客户端最大连接
 
 
int  main( int  argc,  char * argv[]){
     int  listenfd, connfd, efd;
     struct  sockaddr_in serv_addr, clie_addr;
     socklen_t clie_addr_len;
     char  buf[BUFSIZ];
     int  n, i, j, nready, opt;
     struct  epoll_event op[CLIENT_MAX+1], ep;
 
     //创建监听套接字
     listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 
     //设置端口复用
     opt = 1;
     setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt,  sizeof (opt));
 
     //绑定地址族
     bzero(&serv_addr,  sizeof (serv_addr));
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_port = htons(SERV_PORT);
     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
     bind(listenfd, ( struct  sockaddr*)&serv_addr,  sizeof (serv_addr));
 
     //设置连接同时最高上限
     listen(listenfd, SOMAXCONN);
 
     //创建epoll
     efd = epoll_create(CLIENT_MAX+1);
 
     //添加请求连接监听套接字
     ep.events = EPOLLIN;
     ep.data.fd = listenfd;
     epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &ep);
 
     for (;;){
         //监听事件
         nready = epoll_wait(efd, op, CLIENT_MAX + 1, -1);
         for (i = 0; i < nready; i++){
             if (!(op[i].events & EPOLLIN))
                 continue ;
             //当有连接请求
             if (op[i].data.fd == listenfd){
                 clie_addr_len =  sizeof (clie_addr);
                 //获取连接
                 connfd = accept(listenfd, ( struct  sockaddr*)&clie_addr, &clie_addr_len);
                 printf ( "%s:%d connect successfully!\n" , inet_ntoa(clie_addr.sin_addr), ntohs(clie_addr.sin_port));
                 //监听读事件
                 ep.events = EPOLLIN;
                 ep.data.fd = connfd;
                 //添加至监听
                 epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &ep);
             } else {   //客房端消息
                 bzero(buf,  sizeof (buf));
                 n = read(op[i].data.fd, buf,  sizeof (buf));
                 //客户端关闭连接
                 if (0 == n){
                     //从监听中移除
                     epoll_ctl(efd, EPOLL_CTL_DEL, op[i].data.fd, NULL);
                     //关闭连接
                     close(op[i].data.fd);
                 } else {
 
                     //转为大写
                     for (j = 0; j < n; j++){
                         buf[j] =  toupper (buf[j]);
                     }
                     //回写给客户端
                     write(op[i].data.fd, buf, n);
                 }
             }
         }
     }
     close(listenfd);
     close(efd);
     return  0;
}






      本文转自asd1123509133 51CTO博客,原文链接:http://blog.51cto.com/lisea/1792743,如需转载请自行联系原作者



相关文章
|
6天前
|
iOS开发 MacOS Windows
|
1天前
|
Oracle Java 关系型数据库
【服务器】python通过JDBC连接到位于Linux远程服务器上的Oracle数据库
【服务器】python通过JDBC连接到位于Linux远程服务器上的Oracle数据库
14 6
|
2天前
|
网络协议 Dubbo Java
【网络编程】理解客户端和服务器并使用Java提供的api实现回显服务器
【网络编程】理解客户端和服务器并使用Java提供的api实现回显服务器
9 0
|
3天前
|
域名解析 网络协议 Linux
linux网络配置详解
linux网络配置详解
13 0
|
3天前
|
运维 监控 安全
2023年最详细的:本地Linux服务器安装宝塔面板,并内网穿透实现公网远程登录
2023年最详细的:本地Linux服务器安装宝塔面板,并内网穿透实现公网远程登录
|
4天前
|
网络协议 Java Linux
【探索Linux】P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)
【探索Linux】P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)
11 0
|
4天前
|
存储 网络协议 算法
【探索Linux】P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)
【探索Linux】P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)
11 0
|
4天前
|
网络协议 算法 Linux
【探索Linux】P.27(网络编程套接字 —— UDP协议介绍 | TCP协议介绍 | UDP 和 TCP 的异同)
【探索Linux】P.27(网络编程套接字 —— UDP协议介绍 | TCP协议介绍 | UDP 和 TCP 的异同)
14 0
|
4天前
|
存储 算法 网络协议
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
12 0
|
4天前
|
存储 网络协议 Unix
【探索Linux】P.25(网络编程套接字基本概念 —— 预备知识)
【探索Linux】P.25(网络编程套接字基本概念 —— 预备知识)
6 0