当前位置: 首页 > news >正文

Epoll实现服务器高并发

       最近在做一个关于高并发服务器相关的项目需要用到异步/非阻塞IO通信,实现高TCP并发。

       以下用epoll技术实现一个简单的TCP高并发服务器,验证无业务处理的情况下,epoll处理并发连接的数的效果。

       

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/epoll.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<errno.h>
#include <sys/resource.h>


#define MAXBUF 1024
#define MAX_EVENTS 40000



/*
 setnoblocking ---设置句柄为非阻塞方式
 * */

int setnoblocking(int sockfd)
{
    if(fcntl(sockfd,F_SETFL,fcntl(sockfd,F_GETFL,0)|O_NONBLOCK)==-1)
    {
        return -1;
    }
    return 0;
}


int main(int argc,char **argv)
{
    int listenfd,clientfd;
    struct sockaddr_in serv_addr,cli_addr;     //服务器网络地址结构体
    struct sockaddr_in remote_addr; //客户端网络地址结构体
    unsigned int myport;
    int sin_size;
    int curfds;                     //记录当前的fd数据,即并发连接数
    struct epoll_event ev;          //epoll事件结构体
    struct epoll_event events[MAX_EVENTS];  //事件监听队列
    
    struct rlimit rt;     
    pthread_t thread;
    pthread_attr_t attr;


    sin_size=sizeof(struct sockaddr);
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); //运行任何IP地址连接
    serv_addr.sin_port=htons(9000);             

    //创建服务器套接字
    if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0){
        perror("create socket error");
        exit(EXIT_FAILURE);
    }
    
    /*设置socket属性,端口可以重用*/
    int opt=SO_REUSEADDR;
    setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    
    /* 设置每个进程允许打开的最大文件数 */ 
    rt.rlim_max = rt.rlim_cur = MAX_EVENTS; 
    if (setrlimit(RLIMIT_NOFILE, &rt) == -1) { 
        perror("setrlimit"); 
        exit(1); 
    } 

    //将套接字设置为非阻塞模式
    setnoblocking(listenfd);

    if((bind(listenfd,(struct sockaddr *)&serv_addr,sin_size))<0){
        perror("bind failed!");
        exit(EXIT_FAILURE);
    }

    //监听长度已经是内核允许最大值,512。
    listen(listenfd,512);
    
    //创建一个epoll句柄,把监听socket加入到epoll集合里
    int epoll_fd;
    if((epoll_fd=epoll_create(MAX_EVENTS))==-1){
        perror("epoll_create failed!");
        exit(EXIT_FAILURE);
    }
    ev.events=EPOLLIN | EPOLLET;
    ev.data.fd=listenfd;
    // 注意,这里的参数EPOLLIN | EPOLLET并没有设置对写socket的监听,
    // 如果有写操作的话,这个时候epoll是不会返回事件的,如果要对写操作
    // 也监听的话,应该是EPOLLIN | EPOLLOUT | EPOLLET

    //向epoll注册listenfd监听事件
    if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,listenfd,&ev)==-1)
    {
        perror("epll_ctl:server_sockfd register failed");
        exit(EXIT_FAILURE);
    }

    curfds=1;
    int nfds;  //epoll监听事件发生的个数
    int nread=0; 
    char recvbuf[1024];
    int len;
    
    //循环接受客户端请求
    while(1)
    {
        //等待事件的发生
        nfds=epoll_wait(epoll_fd,events,MAX_EVENTS,-1);
        if(nfds==-1){
            perror("epoll_wait");
            exit(EXIT_FAILURE);
        }

        /*处理所有事件*/ 
        int n;
        for(n=0;n<nfds;n++){

            // 如果是主socket的事件的话,则表示有新连接进入了,进行新连接的处理。
            if(events[n].data.fd==listenfd && (events[n].events & EPOLLIN)){ 
                 clientfd=accept(listenfd,(struct sockaddr *)&cli_addr,&sin_size);
                 if(clientfd<0){
                    perror("accpet error");
                    continue;
                 }
                 nread++;
                 printf("accept success!\n");
                 printf("nconnect:%d\n",nread);
                 setnoblocking(clientfd);           //将新连接置于阻塞模式
                 ev.events=EPOLLIN | EPOLLET;       //将新的连接也加入epoll队列
                 ev.data.fd=clientfd;

                 if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,clientfd,&ev)==-1){
                    perror("epll_ctl:server_sockfd register failed");
                    exit(EXIT_FAILURE);
                 }
                 curfds++;
                 printf("curfds:%d\n",curfds);
            }
            else if(events[n].events & EPOLLIN){//如果是已连接的用户,并且收到数据,进行读入
                    clientfd=events[n].data.fd;
                    len=recv(clientfd,recvbuf,MAXBUF,0);
                    if(len>0){
                        send(clientfd,"hello",5,0);
                        do{
                            len=recv(clientfd,recvbuf,MAXBUF,0);
                        }while(len>0);
                    }
                    else if(len==0){ //发出了EPOLLIN事件却没有读取的,表示断线
                        epoll_ctl(epoll_fd,EPOLL_CTL_DEL,clientfd,&events[n]);
                        close(clientfd);
                        events[n].data.fd=-1;
                    }
                }
            }
    }
    close(listenfd);
    return 0;
}


其他相关有价值的参考文档:

实现了一个比nginx速度更快的HTTP服务器
http://www.cnblogs.com/clowwindy/archive/2011/09/23/a_http_server_faster_than_nginx.html

常见多线程与并发服务器设计方案举例

http://blog.csdn.net/pi9nc/article/details/23246641


相关文章:

  • Linux中实现线程池
  • 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之18---商业模式...
  • 凤巢能否成功关键还看用户体验
  • 谁能不查字典把这文章念出来我就服谁
  • Android开发指南-框架主题-意图和意图过滤器
  • XML相关资源
  • Android开发指南-框架主题-基础知识
  • 目前的一些感悟
  • Android开发指南-框架主题-用户界面
  • 部分生僻字
  • SQLServer中的Scanf和Printf
  • Java Swing入门基础 (转)
  • 【三字经全文】
  • 网站开发规范及流程
  • 在vs2005中出现: error LNK2001: 无法解析的外部符号 __security_cookie现象的处理办法...
  • [PHP内核探索]PHP中的哈希表
  • 《Java编程思想》读书笔记-对象导论
  • CSS相对定位
  • ES2017异步函数现已正式可用
  • GitUp, 你不可错过的秀外慧中的git工具
  • Java比较器对数组,集合排序
  • java中具有继承关系的类及其对象初始化顺序
  • js
  • Laravel Mix运行时关于es2015报错解决方案
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • 京东美团研发面经
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • NLPIR智能语义技术让大数据挖掘更简单
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • ​HTTP与HTTPS:网络通信的安全卫士
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #if 1...#endif
  • (C语言)逆序输出字符串
  • (js)循环条件满足时终止循环
  • (NSDate) 时间 (time )比较
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (理论篇)httpmoudle和httphandler一览
  • (转)nsfocus-绿盟科技笔试题目
  • (转)负载均衡,回话保持,cookie
  • ***利用Ms05002溢出找“肉鸡
  • ./include/caffe/util/cudnn.hpp: In function ‘const char* cudnnGetErrorString(cudnnStatus_t)’: ./incl
  • .Net 4.0并行库实用性演练
  • .NET 6 在已知拓扑路径的情况下使用 Dijkstra,A*算法搜索最短路径
  • .NET I/O 学习笔记:对文件和目录进行解压缩操作
  • .NET 服务 ServiceController
  • .NET/C# 检测电脑上安装的 .NET Framework 的版本
  • .NET构架之我见
  • .Net接口调试与案例
  • .net快速开发框架源码分享
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题
  • .set 数据导入matlab,设置变量导入选项 - MATLAB setvaropts - MathWorks 中国
  • @JoinTable会自动删除关联表的数据
  • @value 静态变量_Python彻底搞懂:变量、对象、赋值、引用、拷贝