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


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值