网络编程学习(八)_高质量epoll编程实例(类封装+连接池)

该编程实例是摘取了Linux C++网络编程的epoll实战部分完成了epoll类的封装和连接池部分,后面的文章会继续完善收发包、线程池、心跳以及收发包安全部分。

net_socket.h

#ifndef __NET_SOCKET_H__
#define __NET_SOCKET_H__

#include <vector>  
#include <list>        
#include <sys/epoll.h>
#include <sys/socket.h>

#define NGX_LISTEN_BACKLOG  511    
#define NGX_MAX_EVENTS      512    

typedef struct net_listening_s net_listening_t, *lpnet_listening_t;
typedef struct net_connection_s  net_connection_t,*lpnet_connection_t;
typedef class  CSocket           CSocket;

typedef void (CSocket::*net_event_handler_pt)(lpnet_connection_t c); //定义成员函数指针

struct net_listening_s
{
    int                 port;
    int                 fd;
    lpnet_connection_t  connection;//连接池中的一个连接
};

struct net_connection_s
{
	
	int                       fd;             
	lpnet_listening_t         listening;      		

	//------------------------------------	
	unsigned                  instance:1;     
	uint64_t                  iCurrsequence; 
	struct sockaddr           s_sockaddr;   
	//char                      addr_text[100]; 

	//和读有关的标志-----------------------
	//uint8_t                   r_ready;       
	uint8_t                   w_ready;        

	net_event_handler_pt      rhandler;       
	net_event_handler_pt      whandler;       
	
	//和收包有关
	unsigned char             curStat;                        
	char                      dataHeadInfo[20];   			
	char                      *precvbuf;                     
	unsigned int              irecvlen;                       

	bool                      ifnewrecvMem;                   
	char                      *pnewMemPointer;               

	//--------------------------------------------------
	lpnet_connection_t        data; 
};


class CSocket
{   

public:
    CSocket();
    virtual ~CSocket();
    virtual bool Initialize();

public:
    int net_epoll_init();

	int net_epoll_add_event(int fd,int readevent,int writeevent,uint32_t otherflag,uint32_t eventtype,lpnet_connection_t c);     

	int net_epoll_process_events(int timer);

	//一些业务处理函数handler
	void net_event_accept(lpnet_connection_t oldc);

	void net_wait_request_handler(lpnet_connection_t c); 

private:
    bool net_open_listening_sockets();
	
    bool setnonblocking(int sockfd);  


	ssize_t recvproc(lpnet_connection_t c,char *buff,ssize_t buflen);  //接收从客户端来的数据专用函数

	lpnet_connection_t net_get_connection(int isock);   
	void net_close_connection(lpnet_connection_t c);
	void net_free_connection(lpnet_connection_t c);
private:
    int                            m_worker_connections;
    int                            m_ListenPortCount;     //所监听的端口数量
	int                            m_epollhandle;

	lpnet_connection_t             m_pconnections;                     //注意这里可是个指针,其实这是个连接池的首地址
	lpnet_connection_t             m_pfree_connections;
	
	int                            m_connection_n;                     //当前进程中所有连接对象的总数【连接池大小】
	int                            m_free_connection_n;  
    
	std::vector<lpnet_listening_t> m_ListenSocketList; 
	struct epoll_event             m_events[NGX_MAX_EVENTS]; 
};


#endif

net_socket.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <stdint.h>    
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>     //errno

#include <sys/ioctl.h> 
#include <arpa/inet.h>


#include "net_socket.h"

CSocket::CSocket()
{
    m_worker_connections = 1024;
    m_ListenPortCount = 1;
    m_epollhandle = -1;
    m_pconnections = NULL;
    m_pfree_connections = NULL;

    return;
}

CSocket::~CSocket()
{
    std::vector<lpnet_listening_t>::iterator pos;
    for(pos = m_ListenSocketList.begin(); pos != m_ListenSocketList.end(); ++pos)
    {
        delete (*pos);
    }

    m_ListenSocketList.clear();

    if(m_pconnections != NULL)
    {
        delete [] m_pconnections;
    }

    return;
}

bool CSocket::Initialize()
{
    bool ret = net_open_listening_sockets();

    return ret;
}

bool CSocket::net_open_listening_sockets()
{
    int                isock;
    struct sockaddr_in serv_addr;
    int                iport;
    char               strinfo[100];

    memset(&serv_addr, 0, sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  

    for(int i=0; i < m_ListenPortCount; i++)
    {
        isock = socket(AF_INET, SOCK_STREAM, 0);
        if(isock == -1)
        {
            return false;
        }

        //解决TIME_WAIT这个状态导致bind()失败的问题
        int reuseaddr = 1;
        if(setsockopt(isock,SOL_SOCKET, SO_REUSEADDR,(const void *) &reuseaddr, sizeof(reuseaddr)) == -1)
        {
            close(isock);                                                
            return false;
        }

        //设置为非阻塞
        if(setnonblocking(isock) == false)
        {
            close(isock);                                                
            return false;
        }
/*
        //设置本服务器要监听的地址和端口,这样客户端才能连接到该地址和端口并发送数据        
        strinfo[0] = 0;
        sprintf(strinfo,"ListenPort%d",i);
        iport = p_config->GetIntDefault(strinfo,10000);
        serv_addr.sin_port = htons((in_port_t)iport);   //in_port_t其实就是uint16_t
*/

        serv_addr.sin_port = htons(9000); 
        if(bind(isock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
        {
            close(isock);                                                
            return false;            
        }        

        if(listen(isock, NGX_LISTEN_BACKLOG) == -1)
        {
            close(isock);                                                
            return false;              
        }

        lpnet_listening_t p_listensocketitem = new net_listening_t;
        memset(p_listensocketitem, 0, sizeof(net_listening_t));
        p_listensocketitem->port = iport;
        p_listensocketitem->fd = isock; 
        m_ListenSocketList.push_back(p_listensocketitem);                
    }

    if(m_ListenSocketList.size() <= 0)  //不可能一个端口都不监听吧
        return false;

    return true;
}

bool CSocket::setnonblocking(int sockfd)
{
   int nb=1; //0:清除,1:设置  
    if(ioctl(sockfd, FIONBIO, &nb) == -1) //FIONBIO:设置/清除非阻塞I/O标记:0:清除,1:设置
    {
        return false;
    }
    return true;

    //如下也是一种写法,跟上边这种写法其实是一样的,但上边的写法更简单
    /* 
    //fcntl:file control【文件控制】相关函数,执行各种描述符控制操作
    //参数1:所要设置的描述符,这里是套接字【也是描述符的一种】
    int opts = fcntl(sockfd, F_GETFL);  //用F_GETFL先获取描述符的一些标志信息
    if(opts < 0) 
    {
        return false;
    }
    opts |= O_NONBLOCK; //把非阻塞标记加到原来的标记上,标记这是个非阻塞套接字【如何关闭非阻塞呢?opts &= ~O_NONBLOCK,然后再F_SETFL一下即可】
    if(fcntl(sockfd, F_SETFL, opts) < 0) 
    {
        return false;
    }
    return true;
    */    
}

int CSocket::net_epoll_init()
{
    m_epollhandle = epoll_create(m_worker_connections);
    if(m_epollhandle == -1)
    {
        exit(2);
    }

    m_connection_n = m_worker_connections;

    m_pconnections = new net_connection_t[m_connection_n];

    int i = m_connection_n;
    lpnet_connection_t next = NULL;
    lpnet_connection_t c = m_pconnections;

    do
    {
        i--;

        c[i].data = next;
        c[i].fd = -1;
        c[i].instance = 1;
        c[i].iCurrsequence = 0;

        next = &c[i];
    }while(i);

    m_pfree_connections = next;
    m_free_connection_n = m_connection_n;

    std::vector<lpnet_listening_t>::iterator pos;
    for(pos = m_ListenSocketList.begin(); pos != m_ListenSocketList.end(); ++pos)
    {
        c = net_get_connection((*pos)->fd);
        if(c == NULL)
        {
            exit(2);
        }

        c->listening = (*pos);
        (*pos)->connection = c;

        c->rhandler = &CSocket::net_event_accept;

        if(net_epoll_add_event((*pos)->fd,
                                1,0,
                                0,
                                EPOLL_CTL_ADD,
                                c
                                ) == -1)
        {
            exit(2);
        }
    }

    return 1;
}

lpnet_connection_t CSocket::net_get_connection(int isock)
{
    lpnet_connection_t c = m_pfree_connections;

    if(c == NULL)
    {
        return NULL;
    }

    m_pfree_connections = c->data;
    m_free_connection_n--;

    uintptr_t instance = c->instance;
    uint64_t   iCurrsequence = c->iCurrsequence;    

    memset(c, 0, sizeof(net_connection_t));
    c->fd = isock;
    //c->curStat = _PKG_HD_INIT;   
    c->precvbuf = c->dataHeadInfo;
    //c->irecvlen = sizeof(COMM_PKG_HEADER);
    c->irecvlen = 1024;
    c->ifnewrecvMem = false;                             //标记我们并没有new内存,所以不用释放	 
    c->pnewMemPointer = NULL;

    c->instance = !instance; 
    c->iCurrsequence=iCurrsequence;++c->iCurrsequence;
   
    return c;
}

void CSocket::net_free_connection(lpnet_connection_t c)
{
    if(c->ifnewrecvMem == true)
    {

    }

    c->data = m_pfree_connections;

    ++c->iCurrsequence;

    m_pfree_connections = c;
    ++m_free_connection_n;
    
    return;
}

void CSocket::net_close_connection(lpnet_connection_t c)
{
    if(close(c->fd) == -1)
    {

    }
    c->fd = -1;
    net_free_connection(c);
    return;
}

int CSocket::net_epoll_add_event(int fd,
                                int readevent,int writeevent,
                                uint32_t otherflag, 
                                uint32_t eventtype, 
                                lpnet_connection_t c
                                )
{
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));

    if(readevent == 1)
    {
        ev.events = EPOLLIN|EPOLLRDHUP;
    }
    else
    {

    }

    if(otherflag !=0)
    {
        ev.events |= otherflag;
    }

    ev.data.ptr = (void *)( (uintptr_t)c | c->instance);

    if(epoll_ctl(m_epollhandle, eventtype, fd, &ev) == -1)
    {
        return -1;
    }
    return 1;
}

void CSocket::net_event_accept(lpnet_connection_t oldc)
{
    struct sockaddr     mysockaddr;
    socklen_t           socklen;
    int                 err;
    int                 level;
    int                 s;
    static int          use_accept4 = 1;

    lpnet_connection_t   newc;

    socklen = sizeof(mysockaddr);
    do
    {
        if(use_accept4)
        {
            s = accept4(oldc->fd, &mysockaddr, &socklen, SOCK_NONBLOCK);
        }
        else
        {
            s = accept(oldc->fd, &mysockaddr, &socklen);
        }

        if(s == -1)
        {
            err = errno;

            if(err == EAGAIN)
            {
                return;
            }

            if(use_accept4 && err == ENOSYS)
            {
                use_accept4 = 0;
                continue;
            }
            return;
        }

        newc = net_get_connection(s);
        if(newc == NULL)
        {
            if(close(s) == -1)
            {

            }
            return;
        }

        memcpy(&newc->s_sockaddr, &mysockaddr, socklen);

        if(!use_accept4)
        {
            if(setnonblocking(s))
            {
                net_close_connection(newc);
                return;
            }
        }

        newc->listening = oldc->listening;
        newc->w_ready = 1;

        newc->rhandler = &CSocket::net_wait_request_handler;
        if(net_epoll_add_event(s,
                                1,0,
                                0,
                                EPOLL_CTL_ADD,
                                newc
                                ) == -1)
        {
            net_close_connection(newc);
            return;
        }

        break;
    }while(1);

    return;
}

int CSocket::net_epoll_process_events(int timer)
{
    int events = epoll_wait(m_epollhandle, m_events, NGX_MAX_EVENTS, timer);

    if(events == -1)
    {
        if(errno == EINTR)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }

    if(events == 0)
    {
        if(timer != -1)
        {
            return 1;
        }
        return 0;
    }

    lpnet_connection_t  c;
    uintptr_t           instance;
    uint32_t            revents;

    for(int i=0; i < events; ++i)
    {
        c = (lpnet_connection_t)(m_events[i].data.ptr);
        instance = (uintptr_t) c & 1;

        c = (lpnet_connection_t) ((uintptr_t)c & (uintptr_t)~1);

        if(c->fd == -1)
        {
            continue;
        }

        if(c->instance != instance)
        {
            continue;
        }

        revents = m_events[i].events;

        if(revents & (EPOLLERR|EPOLLHUP))
        {
            revents |= EPOLLIN|EPOLLOUT;
        }

        if(revents & EPOLLIN)
        {
            (this->* (c->rhandler))(c);//执行CSocekt::ngx_event_accept(c)
        }

        if(revents & EPOLLOUT)
        {

        }
    }

    return 1;
}

void CSocket::net_wait_request_handler(lpnet_connection_t c)
{
    ssize_t reco = recvproc(c, c->precvbuf, c->irecvlen);

    if(reco <= 0)
    {
        return;
    }    

    //这里收到数据
    printf("recev data:%s", c->precvbuf);
}

ssize_t CSocket::recvproc(lpnet_connection_t c,char *buff,ssize_t buflen)
{
    ssize_t n;

    n = recv(c->fd, buff, buflen, 0);

    if(n == 0)
    {
        net_close_connection(c);
        return -1;
    }

    if(n < 0)
    {
        if(errno == EAGAIN || errno == EWOULDBLOCK)
        {
            return -1;
        }

        if(errno == EINTR)
        {
            return -1;
        }

        net_close_connection(c);
    }

    return n;
}

net.cpp

#include <stdio.h>
#include <stdlib.h>

#include "net_socket.h"

CSocket g_socket;

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

    if(g_socket.Initialize() == false)//初始化socket
    {
        exit(2);
    }    

    g_socket.net_epoll_init();

    for(;;)
    {
        g_socket.net_epoll_process_events(-1);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值