socket epoll 模型

11 篇文章 0 订阅
4 篇文章 0 订阅
/* 
 * File:   main.c
 * Author: xys
 *
 * Created on 2013年12月12日, 下午3:14
 * 
 * 实现功能:通过epoll,处理多个socket请求
 * 监听一个端口,监听到有链接时,添加epoll_event
 * 
 * 命令行下 telnet localhost 3304
 */

#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>

#define MYPORT 3304

//最多处理的connect
#define MAX_EVENTS 500

//当前连接数
int currentClinet = 0;

//数据接受buf
#define REVLEN 1024
char recvBuf[REVLEN];

//epoll相关
//epoll 描述符
int epollfd;
//事件数组
struct epoll_event eventList[MAX_EVENTS];

void AcceptConn(int srvfd);
void RecvData(int fd);
void setSockNonBlock(int sock);

int main(int argc, char** argv) {

    int sockListen;
    struct sockaddr_in server_addr;
    struct epoll_event event;
    int yes = 1;
    
    //socket
    if((sockListen = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        printf("socket error.\n");
        return -1;
    }
    
    //in case of 'address already in use' error message
    if (setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))) {
        perror("setsockopt failed");
        exit(EXIT_FAILURE);
    }

    //设置sock为non-blocking
    setSockNonBlock(sockListen);
    
    //创建要bind的socket address
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(MYPORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 
    
    //bind
    if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){
        printf("bind error.\n");
        return -1;
    }
    
    //listen
    if(listen(sockListen, 5) < 0){
        printf("listen error.\n");
        return -1;
    }
    
    //1.epoll 初始化
    epollfd = epoll_create(MAX_EVENTS);
    if(epollfd == -1){
        perror("epoll create failed.");
        exit(EXIT_FAILURE);
    }
    event.events = EPOLLIN|EPOLLET;
    event.data.fd = sockListen;
    
    //2.epoll_ctrl
    if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockListen, &event) < 0){
        printf("epoll add fail : fd = %d\n", sockListen);
        return -1;
    }
    
    //epoll
    while(1){
        int n = 0;
        int timeout = 3000;
        
        //3.epoll_wait
        int ret = epoll_wait(epollfd, eventList, MAX_EVENTS, timeout);
        if(ret < 0){
            if(errno == EINTR && epollfd > 0){
                usleep(1*1000);
                continue;
            }
            printf("epoll wait failed\n");
            break;
        }else if(ret == 0){
            fprintf(stderr, "no socket ready for read within %d secs\n", 
                    timeout/1000);
            sleep(1);
            continue;
        }
        
        //直接获取了事件的量,给出了活动的流,这里是和poll区别的关键
        //检测到ret个IO file descriptor的events,loop各个fd进行响应
        for(n = 0; n < ret; n++){
            //错误退出
            /*events[n]即为检测到的event,域events[n].events表示具体哪些events,
            域events[n].data.fd即对应的IO fd*/
            if((eventList[n].events & EPOLLERR) ||
                    (eventList[n].events & EPOLLHUP) ||
                    !(eventList[n].events & EPOLLIN))
            {
                //由于events[i].events使用每个bit表示event,因此判断是否包含某个具体事件可以使用`&`操作符
                printf("epoll error.\n");
                close(eventList[n].data.fd);
                return -1;
            }
            
            //对检测到event的各IO fd进行响应
            if(eventList[n].data.fd == sockListen){
                //当前fd是server的socket,不进行读而是accept所有client连接请求
                AcceptConn(sockListen);
            }else{
                //当前fd是client连接的socket,可以读(read from client)
                RecvData(eventList[n].data.fd);
                //不删除
                //epoll_ctl(epollfd, EPOLL_CTL_DEL, eventList[n].data.fd, &eventList[n]);
            }
        }
    }
    
    close(epollfd);
    close(sockListen);
    
    printf("test.\n");
    return 0;
}

/************************************************** 
函数名:AcceptConn 
功能:接受客户端的链接 
参数:srvfd:监听SOCKET 
***************************************************/  
void AcceptConn(int srvfd)
{
    int confd;
    struct epoll_event event;
    char client_ip_str[INET_ADDRSTRLEN];
    struct sockaddr_in sin;
    socklen_t len = sizeof(struct sockaddr_in);
    bzero(&sin, len);
    
    confd = accept(srvfd, (struct sockaddr*)&sin, &len);

    if (confd == -1) {
        if ( (errno == EAGAIN) || (errno == EWOULDBLOCK) ) {
            //non-blocking模式下无新connection请求,跳出while (1)
            return;
        } else {
            perror("accept failed");
            exit(EXIT_FAILURE);
        }
    }
    if (!inet_ntop(AF_INET, &(sin.sin_addr), client_ip_str, sizeof(client_ip_str))) {
        perror("inet_ntop failed");
        exit(EXIT_FAILURE);
    }
    printf("accept a client from: %s, client=%d\n", client_ip_str, confd);
    
    //设置conn_sock为non-blocking
    setSockNonBlock(confd);
    
    //4.epoll_wait
    //将新建的链接添加到EPOLL的监听中
    event.data.fd = confd;
    event.events = EPOLLIN|EPOLLET;
    if ( epoll_ctl(epollfd, EPOLL_CTL_ADD, confd, &event) == -1 ) {
        perror("epoll_ctl(EPOLL_CTL_ADD) failed\n");
        exit(EXIT_FAILURE);
    }
}

//读取数据
void RecvData(int fd)
{
    int ret;
    memset(recvBuf, 0, REVLEN);
    printf("RecvData function.\n");
    //recv数据
    ret = recv(fd, (char *)recvBuf, sizeof(recvBuf), 0);
    if(ret == 0){
        return;
    }

    //数据接受完毕
    printf("client=%d,buf=%s\n", fd, recvBuf);
}

//函数:设置sock为non-blocking mode
void setSockNonBlock(int sock) 
{
    int flags;
    flags = fcntl(sock, F_GETFL, 0); 
    if (flags < 0) {
        perror("fcntl(F_GETFL) failed");
        exit(EXIT_FAILURE);
    }
    if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
        perror("fcntl(F_SETFL) failed");
        exit(EXIT_FAILURE);
    }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值