服务器设计的类
myepoll.h
#ifndef MYEPOLL_H
#define MYEPOLL_H
#pragma once
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <iostream>
#define MAX_BUFF_SIZE 1024 // TCP buff 最大缓存量
#define MAX_EVENTS 100 // 可允许处理的最大处理事件个数
#define FDSIZE 1000
#define CLIENT_MAX 4
/**
* @brief Tcp服务器数据回调接口
*/
class TcpServerCallback
{
public:
virtual void onTcpServerData(unsigned char* data, int dataLen) = 0;
};
class TcpServer
{
public:
TcpServer();
/**
* @brief createServer
* @param port : socket端口
* @param clientsMax : 最大接入的客户端数量
* @param callback : 回调接口
* @param isblock : 是否阻塞
* @return
*/
bool createServer(int port, int clientsMax, TcpServerCallback *callback, bool isblock);
void tcpSend(unsigned char *data, int dataLen);
bool do_epoll();
private:
/** @brief 回调接口 */
TcpServerCallback *m_callback;
/** @brief socket 文件描述符 */
int m_socketfd = 0;
/** @brief 服务端虚拟地址 */
struct sockaddr_in m_servaddr;
/** @brief epoll 文件描述符 */
int m_epollfd;
/** @brief 最大的连接客户端个数 */
int m_clientsMax = 0;
/** @brief 接入的客户端个数 */
int m_tcpClientNum = 0;
/** @brief 客户端文件描述符 */
int m_tcpClientFd[CLIENT_MAX + 1] = {0};
/** @brief 线程 */
pthread_t m_thread;
private:
void add_event(int fd, int state);
void del_event(int fd, int state);
void mod_event(int fd, int state);
void handle_events(epoll_event *events, int num);
bool handle_accept();
bool do_read(int fd);
bool do_write(int fd, char * buf, int buflen);
void select();
};
#endif // MYEPOLL_H
myepoll.cpp
#include "myepoll.h"
static void *tcp_recive_data(void *func)
{
TcpServer *sm = (TcpServer *)func;
sm->do_epoll();
return 0;
}
TcpServer::TcpServer()
{
m_socketfd = 0;
memset(&m_servaddr, 0, sizeof(m_servaddr));
}
/**
* @brief 建立连接
* @param port socket端口
* @param maxEvents 最大处理事件
* @param isblock 是否阻塞
* @return
*/
bool TcpServer::createServer(int port, int clientsMax, TcpServerCallback *callback, bool isblock)
{
m_callback = callback;
m_tcpClientNum = 0;
if(clientsMax > CLIENT_MAX)
m_clientsMax = CLIENT_MAX;
else
m_clientsMax = clientsMax;
if ((m_socketfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return false;
}
m_servaddr.sin_family = AF_INET;
// IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
m_servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
m_servaddr.sin_port = htons(port);
if (!isblock){
int flags = fcntl(m_socketfd, F_GETFL, 0);
fcntl(m_socketfd, F_SETFL, flags | O_NONBLOCK);//设置为非阻塞
}
// 设置重用地址,防止Address already in use 心跳包检测
int on = 1;
if (setsockopt(m_socketfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1){
printf("set reuse addr error: %s(errno: %d)\n", strerror(errno), errno);
return false;
}
// 将本地地址绑定到所创建的套接字上
if (bind(m_socketfd, (struct sockaddr*)&m_servaddr, sizeof(m_servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
return false;
}
// 开始监听是否有客户端连接
if (listen(m_socketfd, 5) == -1){
printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
return false;
}
printf("port [%d] create socket success\n", port);
select();
return true;
}
/**
* @brief 监听数据
* @return
*/
bool TcpServer::do_epoll()
{
struct epoll_event events[MAX_EVENTS];
int ret;
// 创建一个描述符
if ((m_epollfd = epoll_create(FDSIZE)) == -1){
printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
return false;
}
// 添加监听描述符事件
add_event(m_socketfd, EPOLLIN);
while (true){
// 获取已经准备好的描述符事件
/*
如果要设置read超时
1,设置socket非阻塞
2,设置epoll_wait超时1秒
3,每次进入epoll_wait之前,遍历在线用户列表,踢出长时间没有请求的用户.
PS:每次用户发来数据, read之后更新该用户last_request时间, 为了上面的步骤3而做
*/
ret = epoll_wait(m_epollfd, events, MAX_EVENTS, -1);
handle_events(events, ret);
}
close(m_epollfd);
}
/**
* @brief 在文件描述符epfd所引用的epoll实例上注册目标文件描述符fd,并将事件事件与内部文件链接到fd
* @param fd
* @param state
*/
void TcpServer::add_event(int fd, int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(m_epollfd, EPOLL_CTL_ADD, fd, &ev);
}
/**
* @brief 删除关联的文件描述符
* @param fd
* @param state
*/
void TcpServer::del_event(int fd, int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(m_epollfd, EPOLL_CTL_DEL, fd, &ev);
}
/**
* @brief 更改与目标文件描述符fd相关联的事件事件。
* @param fd
* @param state
*/
void TcpServer::mod_event(int fd, int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(m_epollfd, EPOLL_CTL_MOD, fd, &ev);
}
/**
* @brief 处理事件
* @param events 事件描述符
* @param num 事件个数
* @param buf 缓存
* @param buflen 缓存长度
*/
void TcpServer::handle_events(epoll_event * events, int num)
{
int i;
int fd;
//进行选好遍历
for (i = 0; i < num; i++){
fd = events[i].data.fd;
//根据描述符的类型和事件类型进行处理
if ((fd == m_socketfd) && (events[i].events& EPOLLIN))
handle_accept();
else if (events[i].events & EPOLLIN)
do_read(fd);
else
close(fd);
}
}
/**
* @brief 客户端连接处理
* @return
*/
bool TcpServer::handle_accept(void)
{
int clifd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen = sizeof(cliaddr);
clifd = accept(m_socketfd, (struct sockaddr*) &cliaddr, &cliaddrlen);
if (clifd == -1){
printf("accpet error:");
return false;
}
// 添加一个客户描述符和事件
if (m_tcpClientNum < m_clientsMax){
add_event(clifd, EPOLLIN);
m_tcpClientFd[m_tcpClientNum] = clifd;
m_tcpClientNum++;
char msg[128] = { 0 };
// 打印端口
snprintf(msg, sizeof(msg), "accept a new client:%s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);
printf("%s\n", msg);
tcpSend((unsigned char *)msg, strlen(msg));
}else{
printf("TCP Cmd Client Over Max Connect!");
close(clifd);
printf("close(tcpListe)\n");
return false;
}
return true;
}
/**
* @brief 读取事件数据
* @param fd
* @param buf
* @param buflen
* @return
*/
bool TcpServer::do_read(int fd)
{
char buf[MAX_BUFF_SIZE] = { 0 };
memset(buf, 0, sizeof(buf));
int buflen = read(fd, buf, MAX_BUFF_SIZE);
if (buflen == -1){
printf("read error: %s(errno: %d)\n", strerror(errno), errno);
std::cout << "read =-1\n";
close(fd);
del_event(fd, EPOLLIN);
return false;
}else if (buflen == 0){
close(fd);
std::cout << "client close.\n";
del_event(fd, EPOLLIN);
m_tcpClientNum--;
return true;
}else{
m_callback->onTcpServerData((unsigned char*)buf, buflen);
}
return true;
}
/**
* @brief 发送数据
* @param data
* @param dataLen
*/
void TcpServer::tcpSend(unsigned char *data, int dataLen)
{
for (int i = 0; i < m_tcpClientNum; i++)
if (!do_write(m_tcpClientFd[i], (char*) data, dataLen))
printf("tcpSend %d fault!", m_tcpClientFd[i]);
}
/**
* @brief 写入事件数据
* @param fd
* @param buf
* @param buflen
* @return
*/
bool TcpServer::do_write(int fd, char * buf, int buflen)
{
int nwrite;
nwrite = write(fd, buf, buflen);
if (nwrite == -1){
printf("write error:");
return false;
}
return true;
}
/**
* @brief 启动select监听(只允许调用一次且必须在所有文件描述符都添加之后才可以调用)
*/
void TcpServer::select()
{
// 启动线程
int res = pthread_create(&m_thread, NULL, tcp_recive_data, (void*)this);
if (res){
printf("SelectWraper::SelectWraper::create thread fail");
}
}
使用的方法
#include "myepoll.h"
class ClientDevice : public TcpServerCallback
{
public:
ClientDevice();
void init();
// 接收Tcp服务器数据
virtual void onTcpServerData(unsigned char* data, int dataLen);
private:
TcpServer *m_TCPServer;
};
class FileClientDevice : public TcpServerCallback
{
public:
FileClientDevice();
void init();
// 接收Tcp服务器数据
virtual void onTcpServerData(unsigned char* data, int dataLen);
private:
TcpServer *m_TCPServer;
};
ClientDevice::ClientDevice()
{
}
void ClientDevice::init()
{
m_TCPServer = new TcpServer();
printf("创建8080\n");
m_TCPServer->createServer(8080, 1, this, false);
}
void ClientDevice::onTcpServerData(unsigned char *data, int dataLen)
{
printf("8080 str = [%s], len = <%d>\n", data, dataLen);
}
FileClientDevice::FileClientDevice()
{
}
void FileClientDevice::init()
{
m_TCPServer = new TcpServer();
printf("创建8000\n");
m_TCPServer->createServer(8000, 1, this, false);
}
void FileClientDevice::onTcpServerData(unsigned char *data, int dataLen)
{
printf("8000 str = [%s], len = <%d>\n", data, dataLen);
}