socket编程之epoll多路复用

epoll的相关系统调用

1、int epoll_create(int size);

创建一个epoll描述符,该描述符占用一个fd值,程序退出前必须调用close()关闭,参数size为最大监听事件数

2、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而要在监听之前先注册要监听的事件,函数成功调用返回0,失败返回-1

参数一epfd是epoll_create返回的描述符

参数二op表示操作

EPOLL_CTL_ADD:注册新的fd到epfd中

EPOLL_CTL_DEL:从epfd中删除fd

EPOLL_CTL_MOD:修改已经注册到epfd的监听事件

参数三fd是要监听的fd

参数四event代表需要监听的事件类型,struct epoll_event结构体如下

typedef union epoll_data {  
    void *ptr;  
    int fd;  
    __uint32_t u32;  
    __uint64_t u64;  
} epoll_data_t;

struct epoll_event {  
    __uint32_t events;   
    epoll_data_t data;   
};  

events可以是以下几个宏的集合

EPOLLIN:对应的fd可读

EPOLLOUT:对应的fd可写

EPOLLPRI:对应的fd有紧急数据可读(这里应该表示有带外数据到来)

EPOLLERR:对应的fd出错

EPOLLHUP:对应的fd被挂断

EPOLLET:将EPOLL设为边缘出发(Edge Triggered)模式,这是相对水平触发(Level Triggered)来说的

EPOLLONESHOT:只监听一次,如果需要继续监听该socket,需再次把该socket加入EPOLL队列中

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

收集在epoll监听的事件中已经发生的事件

参数一epfd为epoll_create返回的描述符

参数二events为一个已分配好内存的epoll_event结构体数组,epoll会将已发生的事件存入该数组,events不能是NULL

参数三maxevents为events数组的大小

参数四timeout为超时时间,单位为毫秒,0表示立即返回,-1表示永久阻塞

函数成功调用返回对应I/O上已准备好的fd数目,失败返回-1,超时返回0

/*************************************************************************
 *  Copyright (C): 1540999272@qq.com
 *  Filename:      server.c
 *  Author:        Lu Zengmeng
 *  Description:   
 *  Creat Time:    2016-07-19 12:08
 ************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/epoll.h>

#define PORT             8003
#define BUF_LEN          128
#define BACKLOG          5
#define MAXEVENTS        50

int    epoll_fd;
struct epoll_event events[MAXEVENTS];

void on_accept(int sock);
void on_recv(int sock);

int main(int argc, char *argv[])
{
    int    sock_fd;
    int    yes = 1;
    int    timeout = 5000;
    int    ret;
    struct sockaddr_in     my_addr;


    bzero(&my_addr,sizeof(my_addr));
    my_addr.sin_family        = AF_INET;
    my_addr.sin_port          = htons(PORT);
    my_addr.sin_addr.s_addr   = INADDR_ANY;


    if (-1 == (sock_fd = socket(AF_INET, SOCK_STREAM, 0)))
    {
        perror("socket");
        exit(1);
    }
    
    if (-1 == setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)))
    {
        perror("setsockopt");
        exit(1);
    }

    if (-1 == bind(sock_fd, (struct sockaddr *)&my_addr, sizeof(my_addr)))
    {
        perror("bind");
        exit(1);
    }

    if (-1 == listen(sock_fd, BACKLOG))
    {
        perror("listen");
        exit(1);
    }

    printf("listenning...\n");

    epoll_fd = epoll_create(MAXEVENTS); // 创建epoll描述符
    if(epoll_fd < 0)
    {
        perror("epoll_create");
        exit(1);
    }

        struct epoll_event event;       //81~83 初始化需要监听的事件
    event.events = EPOLLIN|EPOLLET;
    event.data.fd = sock_fd;

    if(-1 == epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sock_fd,&event)) // 注册监听事件
    {
        perror("epoll_ctl");
        exit(1);
    }

    while(1)
    {
        ret = epoll_wait(epoll_fd,events,MAXEVENTS,timeout); // 检查注册到epoll_fd中已发生的事件,并将发生的事件赋值到events数组中    
        if(ret < 0)
        {
            perror("epoll_wait");
            break;
        }

        if(ret == 0)
        {
            printf("timeout\n");
            continue;
        }

        int i;
        for(i=0;i<ret;i++)
        {
            if((events[i].events & EPOLLERR)||
               (events[i].events & EPOLLHUP)||
               (!(events[i].events & EPOLLIN)))
            {
                printf("epoll error\n");
                close(events[i].data.fd);
                continue;
            }

            if(events[i].data.fd == sock_fd) // 发生sock_fd对应的事件说明有新的连接到来
            {
                on_accept(sock_fd);
            }
            else
            {
                on_recv(events[i].data.fd);
            }
        }
    }

    close(epoll_fd);
    close(sock_fd);
    return 0;
}

void on_accept(int sock)
{
    int conn_fd;
    conn_fd = accept(sock,NULL,NULL);

    if(conn_fd < 0)
    {
        perror("accept");
        return;
    }
    else
        printf("new connection client[%d]\n",conn_fd);

    struct epoll_event event; //147~150 将新创建的连接注册到监听列表
    event.data.fd = conn_fd;
    event.events = EPOLLIN|EPOLLET;
    epoll_ctl(epoll_fd,EPOLL_CTL_ADD,conn_fd,&event);

    return;
}

void on_recv(int sock)
{
    char    buf[BUF_LEN];
    int     ret = 0;
    int     len = 0;

    memset(buf,0,BUF_LEN);
    while(1)
    {
        ret =  recv(sock,buf+len,BUF_LEN-len,0);
        if(0 == ret)
        {
            len = 0;
            break;
        }

        else if(0 > ret)
        {
            len = 0;
            break;
        }
        
        len = len + ret;

        if(len < BUF_LEN)
        {
            continue;
        }

        else
        {
            printf("receive %s \n",buf);
            break;
        }
        
    }
    strcat(buf,"\0");
    printf("receive: %s from client[%d]\n",buf,sock);

    return;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值