【C/C++】实现Reactor高并发服务器 完整版

在这里插入图片描述
代码结构 文件介绍

InetAddress.h
InetAddress类 ip和端口设置

Socket.h
Socket类 设置fd

Epoll.h
epollfd 管理类

Channel.h
Channel类 管理epoll以及对应回调函数实现

EventLoop.h
EventLoop事件循环类

TcpServer.h
服务器类

tcpepoll.cpp 主函数

InetAddress.h

#ifndef _INETADDRESS_H
#define _INETADDRESS_H#pragma on_INETADDRESS_He#include <string>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddress
{
private:sockaddr_in addr_;
public:InetAddress(const std::string &ip, uint16_t port);InetAddress(const sockaddr_in addr);InetAddress();~InetAddress();const char *ip()const;uint16_t port()const;const sockaddr *addr()const;void setaddr(sockaddr_in clientaddr);
};#endif // _INETADDRESS_H

InetAddress.cpp

#include "InetAddress.h"InetAddress::InetAddress()
{}InetAddress::InetAddress(const std::string &ip, uint16_t port)
{addr_.sin_family = AF_INET;addr_.sin_addr.s_addr = inet_addr(ip.c_str());addr_.sin_port = htons(port);
}InetAddress::InetAddress(const sockaddr_in addr):addr_(addr)
{}InetAddress::~InetAddress()
{}const char* InetAddress::ip()const
{return inet_ntoa(addr_.sin_addr);
}
uint16_t InetAddress::port()const
{return ntohs(addr_.sin_port);
}
const sockaddr* InetAddress::addr()const
{return (sockaddr*)&addr_;
}void InetAddress::setaddr(sockaddr_in clientaddr)
{addr_ = clientaddr;
}

Socket.h

#ifndef SOCKET_H
#define SOCKET_H#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/fcntl.h>
#include <sys/epoll.h>
#include <netinet/tcp.h>  // TCP_NODELAY#include "InetAddress.h"int createnonblocking();class Socket
{
public:Socket(int fd);~Socket();int fd() const;void setreuseaddr(bool on);void setreuseport(bool on);void settcpnodelay(bool on);void setkeepalive(bool on);void bind(const InetAddress &servaddr);void listen(int n=128);int accept(InetAddress &clientaddr);private:const int fd_;
};#endif // !SOCKET_H

Socket.cpp

#include "Socket.h"int createnonblocking()
{int listenfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);if(listenfd < 0){perror("socket() failed"); exit(-1);}  return listenfd;
}Socket::Socket(int fd):fd_(fd)
{}
Socket::~Socket()
{close(fd_);
}int Socket::fd() const
{return fd_;
}
void Socket::setreuseaddr(bool on)
{int optval = on ? 1 : 0;setsockopt(fd_,  SOL_SOCKET, SO_REUSEADDR, &optval, static_cast<socklen_t>(sizeof(optval)));
}
void Socket::setreuseport(bool on)
{int optval = on ? 1 : 0;setsockopt(fd_,  SOL_SOCKET, SO_REUSEPORT, &optval, static_cast<socklen_t>(sizeof(optval)));
}
void Socket::settcpnodelay(bool on)
{int optval = on ? 1 : 0;setsockopt(fd_,  SOL_SOCKET, TCP_NODELAY, &optval, static_cast<socklen_t>(sizeof(optval)));    
}
void Socket::setkeepalive(bool on)
{int optval = on ? 1 : 0;setsockopt(fd_,  SOL_SOCKET, SO_KEEPALIVE, &optval, static_cast<socklen_t>(sizeof(optval)));    
}
void Socket::bind(const InetAddress &servaddr)
{if(::bind(fd_, servaddr.addr(), sizeof(sockaddr)) < 0){perror("bind() failed"); close(fd_); exit(-1);}
}
void Socket::listen(int n)
{if(::listen(fd_, n) != 0){perror("listen() failed");close(fd_);exit(-1);}
}
int Socket::accept(InetAddress &clientaddr)
{struct sockaddr_in peeraddr;socklen_t len = sizeof(peeraddr);int clientfd = accept4(fd_, (struct sockaddr*)&clientaddr, &len, SOCK_NONBLOCK);clientaddr.setaddr(peeraddr);return clientfd;
}

Epoll.h

#pragma once
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/fcntl.h>
#include <sys/epoll.h>
#include <netinet/tcp.h>  // TCP_NODELAY
#include <vector>
#include "Channel.h"class Channel;class Epoll
{private:static const int MaxEvents = 100;int epollfd_;epoll_event events_[MaxEvents];public:Epoll();~Epoll();//void addfd(int fd, uint32_t op);void updatechannel(Channel *ch);//std::vector<epoll_event> loop(int timeout=-1);std::vector<Channel*> loop(int timeout=-1);
};

Epoll.cpp

#include "Epoll.h"/*
class Epoll
{private:static const int MaxEvents = 100;int epollfd;epoll_event events_[MaxEvents];public:Epoll();~Epoll();void addfd(int fd, uint32_t op);std::vector<epoll_event> loop(int timeout=-1);
}
*/Epoll::Epoll()
{if((epollfd_ = epoll_create(1)) == -1){printf("epoll_create() failed(%d).\n", errno);exit(-1);}
}
Epoll::~Epoll()
{close(epollfd_);
}
/*
void Epoll::addfd(int fd, uint32_t op)
{struct epoll_event ev;ev.data.fd = fd;ev.events = op; //水平if(epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1){printf("epoll_ctl() failed(%d).\n", errno);exit(-1);        }
}
*/void Epoll::updatechannel(Channel *ch)
{epoll_event ev;ev.data.ptr = ch;ev.events = ch->events();if(ch->inpoll()){if(epoll_ctl(epollfd_, EPOLL_CTL_MOD, ch->fd(),&ev) == -1){printf("epoll_ctl() failed(%d).\n", errno);exit(-1);  }printf("epoll_ctl() EPOLL_CTL_MOD success. %d\n", ch->fd());}else{if(epoll_ctl(epollfd_, EPOLL_CTL_ADD, ch->fd(),&ev) == -1){printf("epoll_ctl() failed(%d).\n", errno);exit(-1);  }ch->setinepoll();printf("epoll_ctl() EPOLL_CTL_ADD success. %d\n", ch->fd());}  
}/*
std::vector<epoll_event> Epoll::loop(int timeout)
{std::vector<epoll_event> evs;bzero(events_, sizeof(events_));int infds = epoll_wait(epollfd_, events_, MaxEvents, timeout);if(infds < 0){perror("epoll_wait() failed "); exit(-1);}if(infds == 0){perror("epoll_wait() timeout \n"); return evs;}for(int i = 0; i < infds; i++){evs.push_back(events_[i]);}return evs;
}*/std::vector<Channel*> Epoll::loop(int timeout)
{std::vector<Channel*> channles;bzero(events_, sizeof(events_));int infds = epoll_wait(epollfd_, events_, MaxEvents, timeout);if(infds < 0){perror("epoll_wait() failed "); exit(-1);}if(infds == 0){perror("epoll_wait() timeout \n"); return channles;}for(int i = 0; i < infds; i++){Channel *ch = (Channel*)events_[i].data.ptr;    ch->setrevents(events_[i].events);channles.push_back(ch);}return channles;
}

Channel.h

#ifndef  CHANNEL_H
#define  CHANNEL_H#pragma once
#include <sys/epoll.h>
#include <functional>#include "Epoll.h"
#include "InetAddress.h"
#include "Socket.h"class Epoll;class Channel
{private:int fd_=-1;Epoll *ep_ = nullptr; //channle 对应的红黑树bool inepoll_=false; // epoll_ctl add moduint32_t events_=0;  //fd_需要监视的事件uint32_t revents_=0; //fd 已发生的事件std::function<void()> readcallback_;public:Channel(Epoll *ep, int fd);~Channel();int fd();void useet(); //采用边缘触发void enablereading(); //让epoll_wait()监视fd_的读事件void setinepoll();void setrevents(uint32_t ev);bool inpoll();uint32_t events();uint32_t revents(); //返回revents_成员void handleevent();void newconnection(Socket* servsock);void onmessage();void setreadcallback(std::function<void()> fn);
};#endif // ! CHANNEL_H

Channel.cpp

#include "Channel.h"/*
class Channel
{private:int fd_=-1;Epoll *ep_ = nullptr; //channle 对应的红黑树bool inepoll_=false; // epoll_ctl add moduint32_t events_=0;  //fd_需要监视的事件uint32_t revents_0; //fd 已发生的事件public:Channel(Epoll *ep, intfd);~Channel();int fd();void useet(); //采用边缘触发void enablereading(); //让epoll_wait()监视fd_的读事件void setinepoll();void setrevents(uint32_t ev);bool inpoll();uint32_t events();uint32_t revents(); //返回revents_成员
};*/Channel::Channel(Epoll *ep, int fd):ep_(ep),fd_(fd)
{}Channel::~Channel()
{
//在析构函数中,不要销毁ep_ 也不能关闭fd_ 不属于channel类
}int Channel::fd()
{return fd_;
}
void Channel::useet()
{events_ = events_ | EPOLLET;
}
void Channel::enablereading()
{events_ |= EPOLLIN;ep_->updatechannel(this);
}
void Channel::setinepoll()
{inepoll_ = true;
}
void Channel::setrevents(uint32_t ev)
{revents_= ev;
}
bool Channel::inpoll()
{return inepoll_;
}
uint32_t Channel::events()
{return events_;
}
uint32_t Channel::revents()
{return revents_;
}//事件处理函数, epoll_wait返回的时候执行它。
void Channel::handleevent()
{if(revents_ & EPOLLRDHUP){printf("cilent fd =%d disconnection\n", fd_);close(fd_);} else if (revents_ & EPOLLIN|EPOLLPRI){readcallback_();}else if (revents_ & EPOLLOUT){}else{printf("cilent fd =%d\n", fd_);close(fd_);}  
}void Channel::newconnection(Socket* servsock)
{InetAddress clientaddr;Socket *clientsock = new Socket(servsock->accept(clientaddr));printf("FILE(%s)FUNCTION(%s)LINE(%d) accept client fd=%d, ip=%s,port=%d ok.\n",__FILE__, __func__, __LINE__,clientsock->fd(), clientaddr.ip(), clientaddr.port());Channel *clientchannel = new Channel(ep_, clientsock->fd());clientchannel->setreadcallback(std::bind(&Channel::onmessage, clientchannel));clientchannel->useet();clientchannel->enablereading();
}void Channel::onmessage()
{char buffer[1024];memset(buffer, 0, sizeof(buffer));size_t nread = recv(fd_, buffer, sizeof(buffer), 0);if(nread > 0){printf("tcpepoll Recv:%s\n", buffer);send(fd_, buffer, strlen(buffer), 0);}else if (nread == -1 && errno == EINTR){}else if(nread == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))){}else if (nread == 0){printf("clientfd:%d disconnected\n", fd_);close(fd_);}
}//设置fd的回调函数
void Channel::setreadcallback(std::function<void()> fn)
{readcallback_ = fn;
}

EventLoop.h

#pragma once#include "Epoll.h"class EventLoop
{
private:Epoll *ep_;
public:EventLoop();~EventLoop();void run();Epoll* ep();
};

EventLoop.cpp

#include "EventLoop.h"/*
class EventLoop
{
private:Epoll *ep_;
public:EventLoop();~EventLoop();void run();
};
*/EventLoop::EventLoop():ep_(new Epoll)
{}EventLoop::~EventLoop()
{delete ep_;
}void EventLoop::run()
{while(true){std::vector<Channel*> channles = ep_->loop();for(auto &ch:channles){ch->handleevent();}}
}Epoll* EventLoop::ep()
{return ep_;
}

TcpServer.h

#pragma once#include "EventLoop.h"
#include "Socket.h"
#include "Channel.h"class TcpServer
{
private:EventLoop loop_;
public:TcpServer(const std::string &ip, const uint16_t port);~TcpServer();void start();
};

TcpServer.cpp

#include "TcpServer.h"/*
class TcpServer
{
private:EventLoop loop_;
public:TcpServer(const std::string &ip, const uint16_t port);~TcpServer();
};
*/TcpServer::TcpServer(const std::string &ip, const uint16_t port)
{Socket *servsock = new Socket(createnonblocking());InetAddress servaddr(ip, port);servsock->setreuseaddr(true);servsock->setreuseport(true);servsock->settcpnodelay(true);servsock->setkeepalive(true);servsock->bind(servaddr);servsock->listen();Channel *servchannel = new Channel(loop_.ep(), servsock->fd());servchannel->setreadcallback(std::bind(&Channel::newconnection, servchannel, servsock));servchannel->enablereading();
}TcpServer::~TcpServer()
{}void TcpServer::start()
{loop_.run();
}

tcpepoll.cpp

#include "TcpServer.h"int main(int argc, char *argv[])
{if(argc !=3){printf("usage: ./tcpepoll ip port\n");printf("examples ./tcpepoll 127.0.0.1 6666\n");return -1;}TcpServer tcpserver(argv[1], atoi(argv[2]));tcpserver.start(); //运行事件循环return 0;
} 

client.cpp

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/fcntl.h>
#include <sys/epoll.h>
#include <netinet/tcp.h>  // TCP_NODELAY
#include <time.h>int main(int argc, char *argv[])
{if(argc !=3){printf("usage: ./client ip port");return -1;}int sockfd;struct sockaddr_in servaddr;char buf[1024];if((sockfd=socket(AF_INET,SOCK_STREAM, 0)) < 0){return -1;}memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(atoi(argv[2]));if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr))!=0){return -1;}printf("connect ok\n");for(int i = 0; i < 200000; i++){memset(buf, 0, sizeof(buf));printf("please input:");scanf("%s", buf);if(send(sockfd, buf, strlen(buf), 0) < 0){close(sockfd);return -1;}memset(buf, 0, sizeof(buf));if(recv(sockfd, buf, sizeof(buf), 0) <= 0){return -1;}printf("i:%d recv:%s\n", i, buf);}return 0;
}

makefile

all: client tcpepollclient: client.cppg++ -g -o client client.cpp tcpepoll:tcpepoll.cpp InetAddress.cpp Socket.cpp Epoll.cpp Channel.cpp EventLoop.cpp TcpServer.cppg++  -g -o tcpepoll tcpepoll.cpp InetAddress.cpp Socket.cpp Epoll.cpp Channel.cpp EventLoop.cpp TcpServer.cppclean:rm -f client tcpepoll

运行

服务器
在这里插入图片描述
客户端
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/260486.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云服务器被攻击黑洞了怎么办?

今天一个用户使用阿里云服务器遭受DDOS攻击&#xff0c;收到了攻击的提醒短信&#xff0c;服务器也进入黑洞&#xff0c;联系到了德迅云安全&#xff0c;询问有什么解决办法。目前网络攻击事件频发&#xff0c;相信不少用户都曾收到过类似的攻击短信。今天德迅云安全就分享下&a…

模板(函数模板)---C++

模板目录 模板1.模板概念&#xff12;.泛型编程 1.函数模板1.1 函数模板语法1.2 函数模板注意事项1.3 普通函数与函数模板的区别1.4 普通函数与函数模板的调用规则1.5 模板的局限性1.6 函数模板案例 模板 1.模板概念 模板就是建立通用的模具&#xff0c;大大提高复用性。 模板…

Leetcoder Day16| 二叉树 part05

语言&#xff1a;Java/C 513.找树左下角的值 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1示例 2: 输入: [1,2,3,4,null,5,6,null,null,7] 输出: 7 本题需要满足两…

3个密码学相关的问题

一、离散对数问题&#xff08;Discrete Logarithm Problem, DLP&#xff09; 问题描述&#xff1a;给定 有限阿贝尓群 G中的2个元素a和b&#xff0c;找出最小的正整数x满足&#xff1a;b a ^^ x &#xff08;或者证明这样的x不存在&#xff09;。 二、阶数问题&#xff08;O…

【STM32】硬件SPI读写W25Q64芯片

目录 基础知识回顾&#xff1a; SPI外设简介 SPI框图 主模式全双工连续传输 非连续传输 初始化SPI外设 核心代码 - 交换一个字节 硬件接线图 Code 程序配置过程 MySPI.c MySPI.h W25Q64.c W25Q64.h W25Q64_Ins.h main.c 基础知识回顾&#xff1a; 【STM32】SP…

【压缩感知基础】Nyquist采样定理

Nyquist定理&#xff0c;也被称作Nyquist采样定理&#xff0c;是由哈里奈奎斯特在1928年提出的&#xff0c;它是信号处理领域的一个重要基础定理。它描述了连续信号被离散化为数字信号时&#xff0c;采样的要求以避免失真。 数学表示 Nyquist定理的核心内容可以描述如下&…

命令执行讲解和函数

命令执行漏洞简介 命令执行漏洞产生原因 应用未对用户输入做严格得检查过滤&#xff0c;导致用户输入得参数被当成命令来执行 命令执行漏洞的危害 1.继承Web服务程序的权限去执行系统命会或读写文件 2.反弹shell&#xff0c;获得目标服务器的权限 3.进一步内网渗透 远程代…

自己动手写编译器:使用 PDA 实现增强和属性语法的解析

在前面章节中我们了解了增强语法和属性语法&#xff0c;特别是看到了这两种语法的结合体&#xff0c;本节我们看看如何使用前面我们说过的自顶向下自动机来实现这两种语法结合体的解析&#xff0c;这里使用的方法也是成熟编译器常用的一种语法解析算法。 首先我们先给出上一节…

【运维】站点可靠性工程介绍:研发,运维,SRE,Devops的关系

文章目录 1、什么是SRE2、SRE与研发、运维的区别 1、什么是SRE 站点可靠性工程&#xff08;SRE&#xff09; 是 IT 运维的软件工程方案。 SRE 团队使用软件作为工具&#xff0c;来管理系统、解决问题并实现运维任务自动化。 SRE 执行的任务以前通常由运维团队手动执行&#x…

URL、DNS过滤,AV---防火墙综合实验

拓扑图 该实验之前的配置请看我的上一篇博客&#xff0c;这里仅配置URL、DNS过滤&#xff0c;AV 需求 8&#xff0c;分公司内部的客户端可以通过域名访问到内部的服务器 这次的拓扑图在外网多增加了一个DNS服务器和HTTP服务器 DNS服务器IP&#xff1a;40.0.0.30 HTTP服务器…

计算机视觉主要知识点

计算机视觉是指利用计算机和算法来解析和理解图片和视频中的内容。这是一个跨学科领域&#xff0c;融合了计算机科学、图像处理、机器学习和模式识别等多方面的技术。以下是一些计算机视觉入门的基本知识点&#xff1a; 主要知识点 图像基础&#xff1a; 像素&#xff1a;图片…

文献学习-1-Continuum Robots for Medical Interventions

Chapt 5. 连续体机构分析 5.1 文献学习 5.1.1 Continuum Robots for Medical Interventions Authors: PIERRE E. DUPONT , Fellow IEEE, NABIL SIMAAN , Fellow IEEE, HOWIE CHOSET , Fellow IEEE, AND CALEB RUCKER , Member IEEE 连续体机器人在医学上得到了广泛的应用&a…

深度学习基础之《TensorFlow框架(4)—Operation》

一、常见的OP 1、举例 类型实例标量运算add&#xff0c;sub&#xff0c;mul&#xff0c;div&#xff0c;exp&#xff0c;log&#xff0c;greater&#xff0c;less&#xff0c;equal向量运算concat&#xff0c;slice&#xff0c;splot&#xff0c;canstant&#xff0c;rank&am…

通配符ssl证书产品

SSL数字证书可以对网站传输数据进行加密以及对服务器的身份进行认证。然而&#xff0c;随着互联网的发展&#xff0c;不管是个人还是企事业单位创建的域名网站越来越多&#xff0c;单域名SSL数字证书无法满足需求&#xff0c;因此通配符SSL证书应运而生。今天就随SSL盾小编了解…

【elk查日志 elastic(kibana)】

文章目录 概要具体的使用方式一&#xff1a;查找接口调用历史二&#xff1a;查找自己的打印日志三&#xff1a;查找错误日志 概要 每次查日志&#xff0c;我都需要别人帮我&#xff0c;时间长了总觉得不好意思&#xff0c;所以这次下定决心好好的梳理一下&#xff0c;怎么查日…

文件IO,目录IO的学习

一&#xff0c;头文件的添加 #ifndef _HEAD_H_ //防止重新定义宏 #define _HEAD_H_#include<stdio.h> #include<sys/stat.h> #include<sys/types.h> #include<fcntl.h> #include<unistd.h> #include<string.h>#endif…

SpringBoot + Nacos 实现动态化线程池

1.背景 在后台开发中&#xff0c;会经常用到线程池技术&#xff0c;对于线程池核心参数的配置很大程度上依靠经验。然而&#xff0c;由于系统运行过程中存在的不确定性&#xff0c;我们很难一劳永逸地规划一个合理的线程池参数。 在对线程池配置参数进行调整时&#xff0c;一…

【已解决】PPT无法复制内容怎么办?

想要复制PPT文件里的内容&#xff0c;却发现复制不了&#xff0c;怎么办&#xff1f; 这种情况&#xff0c;一般是PPT文件被设置了以“只读方式”打开&#xff0c;“只读方式”下的PPT无法进行编辑更改&#xff0c;也无法进行复制粘贴的操作。 想要解决这个问题&#xff0c;我…

PHP分析二维数据表(长度|数字字段|空值|纯姓名|英文用户名|科学计数|是否等长|是否唯一)

先看图&#xff0c;后有完整代码 <?php $t "Excel数据转Sql查询系统字段半智能分析"; $s "Excel复制过来的二维结构表内容,分析查询条件&#xff01;"; $x "字段|最大长度|长度有|数字字段|空值存在|纯姓名|英文用户名|科学计数|是否等长|是否…

DP读书:《openEuler操作系统》(十)套接字 Socket 数据传输的基本模型

10min速通Socket 套接字简介数据传输基本模型1.TCP/IP模型2.UDP模型 套接字类型套接字&#xff08;Socket&#xff09;编程Socket 的连接1.连接概述(1)基本概念(2)连接状态(3)连接队列 2.建立连接3.关闭连接 socket 编程接口介绍数据的传输1. 阻塞与非阻塞2. I/O复用 数据的传输…