epoll的使用例子


 // local.h
#include <iostream>
#include <list>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <errno.h> 




#define BUF_SIZE 1024                 //默认缓冲区
#define SERVER_PORT 44444             //监听端口
#define SERVER_HOST "192.168.1.103"   //服务器IP地址
#define EPOLL_RUN_TIMEOUT -1          //epoll的超时时间
//#define EPOLL_SIZE 10000              //epoll监听的客户端的最大数目
#define EPOLL_SIZE 1019              //epoll监听的客户端的最大数目

#define STR_WELCOME "Welcome to seChat! You ID is: Client #%d"
#define STR_MESSAGE "Client #%d>> %s"
#define STR_NOONE_CONNECTED "Noone connected to server except you!"
#define CMD_EXIT "EXIT"

//两个有用的宏定义:检查和赋值并且检测
#define CHK(eval) if(eval < 0){perror("eval"); exit(-1);}
#define CHK2(res, eval) if((res = eval) < 0){perror("eval"); exit(-1);}

//================================================================================================
//函数名:                  setnonblocking
//函数描述:                设置socket为不阻塞
//输入:                    [in] sockfd socket标示符
//输出:                    无
//返回:                    0
//================================================================================================
int setnonblocking(int sockfd);

//================================================================================================
//函数名:                  handle_message
//函数描述:                处理每个客户端socket
//输入:                    [in] new_fd socket标示符
//输出:                    无
//返回:                    返回从客户端接受的数据的长度
//================================================================================================
int handle_message(int new_fd);

// utils.h 

#include "local.h"


int setnonblocking(int sockfd)
{
    CHK(fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK));
    return 0;
}

server.cpp


生成server  g++ -o server server.cpp

#include "local.h"
#include "utils.h"

using namespace std;

list<int>clients_list;

int main(int ac,char**av)
{
    int listenfd;
    struct sockaddr_in addr,their_addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(SERVER_PORT);
    addr.sin_addr.s_addr=inet_addr(SERVER_HOST);
    socklen_t socklen;
    socklen=sizeof(struct sockaddr_in);


    static struct epoll_event ev,events[EPOLL_SIZE];
    char message[BUF_SIZE];
    int epfd;
    clock_t tStart;

    int client,res,epoll_events_count;

    CHK2(listenfd,socket(AF_INET,SOCK_STREAM,0));
    setnonblocking(listenfd);
    CHK(bind(listenfd,(struct sockaddr*)&addr,sizeof(addr)));
    CHK(listen(listenfd,1));

    CHK2(epfd,epoll_create(EPOLL_SIZE));
    ev.events=EPOLLIN;
    ev.data.fd=listenfd;
    CHK(epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev));

    while(1){
	CHK2(epoll_events_count,epoll_wait(epfd,events,EPOLL_SIZE,
		    EPOLL_RUN_TIMEOUT));
	tStart=clock();
	for(int i=0;i<epoll_events_count;i++){
	    if(events[i].data.fd==listenfd){
		CHK2(client,accept(listenfd,(struct sockaddr*)&their_addr,
			    &socklen));
		setnonblocking(client);
		ev.data.fd=client;
		CHK(epoll_ctl(epfd,EPOLL_CTL_ADD,client,&ev)); //没有改变事件,依然是
								//沿用的ev.events
								//=EPOLL_IN,可读事件
               //这里的ev起到一个绑定事件和描述符的中间变量,可以重复使用
	       //也即,每个描述符都可以绑定特殊的事件
		clients_list.push_back(client);
		bzero(message,BUF_SIZE);
		res=sprintf(message,STR_WELCOME,client);
		CHK2(res,send(client,message,BUF_SIZE,0));
	    }else{
		CHK2(res,handle_message(events[i].data.fd));
	    }
	}
	printf("statistics:%d events handled spent:%.2f second \n",
		epoll_events_count,(double)(clock()-tStart)/CLOCKS_PER_SEC);
    }

    close(listenfd);
    close(epfd);
    return 0;
}
int handle_message(int client)
{
    char buf[BUF_SIZE],message[BUF_SIZE];
    bzero(buf,sizeof(buf));
    bzero(message,sizeof(message));

    int len;
    CHK2(len,recv(client,buf,BUF_SIZE,0));

    if(len==0){ //表示收到了FIN分节
	CHK(close(client));
	clients_list.remove(client);
    }else{
	if(clients_list.size()==1){
	    CHK(send(client,STR_NOONE_CONNECTED,strlen(STR_NOONE_CONNECTED),
			0));
	    return len;
	}
	sprintf(message,STR_MESSAGE,client,buf);
	list<int>::iterator it; //模仿了群聊,转发给所有人
	for(it=clients_list.begin();it!=clients_list.end();it++){
	    if(*it!=client)
		CHK(send(*it,message,BUF_SIZE,0));
	}
    }
    return len;
}

 test.cpp 模拟高并发,连接成功后,函数就返回了,所有连接又断裂

g++ -o test test.cpp

#include "local.h"
#include "utils.h"

using namespace std;

char message[BUF_SIZE];
list<int>list_of_clients;
int res;
clock_t tStart;

int main(int ac,char**av)
{
    int sock;
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(SERVER_PORT);
    addr.sin_addr.s_addr=inet_addr(SERVER_HOST);

    tStart=clock();
    for(int i=0;i<EPOLL_SIZE;i++){
	CHK2(sock,socket(PF_INET,SOCK_STREAM,0));
	CHK(connect(sock,(struct sockaddr*)&addr,sizeof(addr))<0);
	list_of_clients.push_back(sock);

	bzero(&message,BUF_SIZE);
	CHK2(res,recv(sock,message,BUF_SIZE,0));
	printf("%s\n",message);
    }

    list<int>::iterator it;
    for(it=list_of_clients.begin();it!=list_of_clients.end();it++)
	close(*it);
    printf("Test passed at:%.4f second(s)\n",
	    (double)(clock()-tStart)/CLOCKS_PER_SEC);
    printf("Total server connections was:%d\n",EPOLL_SIZE);
    return 0;
}


client.cpp

可以试着群聊:::


#include "local.h"
#include "utils.h"

using namespace std;

char message[BUF_SIZE];

/*
    流程:
        调用fork产生两个进程,两个进程通过管道进行通信
        子进程:等待客户输入,并将客户输入的信息通过管道写给父进程
        父进程:接受服务器的信息并显示,将从子进程接受到的信息发送给服务器
*/
int main(int argc, char *argv[])
{
    int sock, pid, pipe_fd[2], epfd;

    struct sockaddr_in addr;
    addr.sin_family = PF_INET;
    addr.sin_port = htons(SERVER_PORT);
    addr.sin_addr.s_addr = inet_addr(SERVER_HOST);

    static struct epoll_event ev, events[2]; 
    ev.events = EPOLLIN | EPOLLET;

    //退出标志
    int continue_to_work = 1;

    CHK2(sock,socket(PF_INET, SOCK_STREAM, 0));
    CHK(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0);

    CHK(pipe(pipe_fd));
    
    CHK2(epfd,epoll_create(EPOLL_SIZE));

    ev.data.fd = sock;
    CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev));

    ev.data.fd = pipe_fd[0];
    CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, pipe_fd[0], &ev));

    // 调用fork产生两个进程
    CHK2(pid,fork());
    switch(pid)
    {
        case 0:                   // 子进程
            close(pipe_fd[0]);    // 关闭读端
            printf("Enter 'exit' to exit\n");
            while(continue_to_work)
            {
                bzero(&message, BUF_SIZE);
                fgets(message, BUF_SIZE, stdin);

                // 当收到exit命令时,退出
                if(strncasecmp(message, CMD_EXIT, strlen(CMD_EXIT)) == 0)
                {
                    continue_to_work = 0;
                }
                else
                {            
                    CHK(write(pipe_fd[1], message, strlen(message) - 1));
                }
            }
            break;
        default:                 // 父进程
            close(pipe_fd[1]);   // 关闭写端
            int epoll_events_count, res;
            while(continue_to_work) 
            {
                CHK2(epoll_events_count,epoll_wait(epfd, events, 2, EPOLL_RUN_TIMEOUT));

                for(int i = 0; i < epoll_events_count ; i++)
                {
                    bzero(&message, BUF_SIZE);
                    if(events[i].data.fd == sock)   //从服务器接受信息
                    {
                        CHK2(res,recv(sock, message, BUF_SIZE, 0));
                        if(res == 0)               //服务器已关闭
                        {
                            CHK(close(sock));
                            continue_to_work = 0;
                        }
                        else 
                        {
                            printf("%s\n", message);
                        }
                    }
                    else        //从子进程接受信息
                    {
                        CHK2(res, read(events[i].data.fd, message, BUF_SIZE));
                        if(res == 0)
                        {
                            continue_to_work = 0; 
                        }
                        else
                        {
                            CHK(send(sock, message, BUF_SIZE, 0));
                        }
                    }
                }
            }
    }
    if(pid)
    {
        close(pipe_fd[0]);
        close(sock);
    }else
    {
        close(pipe_fd[1]);
    }

    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值