该编程实例是摘取了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;
}