【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.mzph.cn/news/691820.shtml

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

相关文章

桌面便签怎么设置提醒,哪个备忘录便签好?

2024年终于开工了&#xff0c;第一天上班比较迷茫&#xff0c;不知道做什么比较好&#xff0c;这个时候如果有一款简单好用且可提醒的桌面便签软件该多好。那么&#xff0c;桌面便签怎么设置提醒&#xff0c;哪个备忘录便签好&#xff1f; 桌面便签怎么设置提醒&#xff0c;哪个…

216961-98-7,BODIPY 493/503 SE,具有相对较长的激发状态寿命

文章关键词&#xff1a;216961-98-7&#xff0c;BODIPY 493/503 NHS 活化酯&#xff0c;BODIPY 493/503 NHS ester&#xff0c;BODIPY 493/503 SE 一、基本信息 产品简介&#xff1a;BODIPY染料是一种独特的荧光染料&#xff0c;由于其具有疏水性&#xff0c;特别适合用于染色…

基于芯驰 X9HP PTG4.1 在 yocto 中添加 Linux 应用

1.参考例程并添加应用 1.1 参考例程 &#xff08;1&#xff09;查看自带的串口测试例程 uart_test &#xff0c;查看 bb 文件怎么写的。 1.2 添加 printf-test 应用 &#xff08;1&#xff09;在 yocto/meta-semidrive/recipes-bsp/ 目录中 copy 自带例程 uart-test 改名为 …

java中容易被忽视的toString()方法

之前一直认为toString就是将数据转换成字符类型&#xff0c;直到最近写出了一个bug才对toString有了新的认识 不同数据类型&#xff0c;toString() 有不同的操作 定义一个student类&#xff0c;包含姓名 String类型、性别 String类型、年龄 int 类型、分数列表 String类型的li…

2024年机器人技术的五大发展趋势,你有看好的吗?

文 BFT机器人 前言&#xff1a; 近些年来机器人技术作为推动社会发展的重要动力之一&#xff0c;它的发展成为重点关注对象&#xff0c;这一领域经历了漫长的发展。随着计算机、传感器和人工智能等技术的进步&#xff0c;现代机器人已经从简单的工具变成了各行各业的生产力。从…

9、使用 ChatGPT 的 GPT 制作自己的 GPT!

使用 ChatGPT 的 GPT 制作自己的 GPT! 想用自己的 GPT 超越 GPT ChatGPT 吗?那么让我们 GPT GPT 吧! 山姆 奥特曼利用这个机会在推特上宣传 GPTs 的同时还猛烈抨击了埃隆的格罗克。 GPTs概览 他们来了! 在上周刚刚宣布之后,OpenAI 现在推出了其雄心勃勃的新 ChatGPT…

信奥一本通:1075:药房管理

这个题可能有点误解&#xff0c;看这个实例&#xff0c;不是用30依次去减10 5 20 6 7 8&#xff0c;如果按照这个减法&#xff0c;30先减10再减5就剩15了&#xff0c;那完全不够后面20减的&#xff0c;所以次数还剩4次。但是&#xff0c;这道题是谁能减就减谁&#xff0c;意思就…

使用云渲染要注意什么?渲染100邀请码1a12

云渲染是利用云服务器进行渲染的方法&#xff0c;它能节省用户成本&#xff0c;提高效率&#xff0c;如果要用云渲染需要注意以下几点&#xff1a; 1、选择专业平台 一个专业的平台对渲染非常重要&#xff0c;比如渲染100&#xff0c;它是国内知名的云渲染提供商&#xff0c;拥…

【力扣 - 二叉树的中序遍历】

题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 提示&#xff1a; 树中节点数目在范围 [0, 100] 内 -100 < Node.val < 100方法一&#xff1a;递归 思路与算法 首先我们需要了解什么是二叉树的中序遍历&#xff1a;按照访问左子树——…

Linux小程序--进度条

目录 1.知识补充 1.1回车和换行 1.2缓冲区 2.实现倒计时 3.实现进度条 1.知识补充 1.在制作小程序进度条之前&#xff0c;我们先了解一下&#xff0c;回车换行和行缓冲区的概念。 2.动态效果&#xff0c;在同一个位置刷新不同的图像&#xff0c;实现一个倒计时的效果。…

2024最新软件测试面试题(带答案)

1. 请自我介绍一下(需简单清楚的表述自已的基本情况&#xff0c;在这过程中要展现出自信&#xff0c;对工作有激情&#xff0c;上进&#xff0c;好学) 面试官您好&#xff0c;我叫###&#xff0c;今年26岁&#xff0c;来自江西九江&#xff0c;就读专业是电子商务&#xff0c;毕…

Linux 磁盘分区、挂载

Linux 磁盘分区、挂载 Linux 分区 介绍 Linux 来说无论有几个分区&#xff0c;分给哪一目录使用&#xff0c;它归根结底就只有一个根目录&#xff0c;一个独立且唯一的文件结构 , Linux 中每个分区都是用来组成整个文件系统的一部分。 Linux 采用了一种叫“载入”的处理方法&…

(十四)devops持续集成开发——jenkins流水线使用pipeline方式发布项目

前言 本节内容我们使用另外一种方式pipeline实现项目的流水线部署发布&#xff0c;Jenkins Pipeline是一种允许以代码方式定义持续集成和持续交付流水线的工具。通过Jenkins Pipeline&#xff0c;可以将整个项目的构建、测试和部署过程以脚本的形式写入Jenkinsfile中&#xff…

打通全渠道,聚道云助力时尚巨头提升运营效能

客户介绍&#xff1a; 北京某时尚有限公司是一家集设计、生产、销售于一体的时尚产业领军企业。自成立以来&#xff0c;该公司一直秉承着对时尚的独特理解和不懈追求&#xff0c;以打造高品质、高品位的时尚产品为己任&#xff0c;深受国内外消费者的喜爱。 客户痛点&#xff…

springcloud:1.Eureka详细讲解

Eureka 是 Netflix 开源的一个服务注册和发现工具,被广泛应用于微服务架构中。作为微服务架构中的核心组件之一,Eureka 提供了服务注册、发现和失效剔除等功能,帮助构建弹性、高可用的分布式系统。在现代软件开发领域,使用 Eureka 可以有效地管理和监控服务实例,实现服务之…

网络安全综合实验

1.实验拓扑 在这里注意因为第四个要求配置双击热备&#xff0c;我们可以第一时间配置&#xff0c;避免二次重复配置消耗时间 4、FW1和FW3组成主备模式的双机热备 具体配置位置在系统-->高可靠性-->双机热备-->配置 这里上行链路有两组&#xff0c;分别为电信和移动&…

Sora后观察:AI大模型产业落地的八个锚点

在正在进行的2024年&#xff0c;国内大模型也将更下沉和落地&#xff0c;在技术上的突破之外&#xff0c;也会出现更多的向下的产业兼容和产业实践案例&#xff0c;作为新质生产力推动产业数字化转型的航船加速前进。 作者|斗斗 编辑|皮爷 出品|产业家 “电影讲述了一名…

期权无风险套利策略[2]—牛市垂直价差套利

牛市垂直价差 牛市垂直价差可以分为牛市看涨期权价差策略与牛市看跌期权价差策略。 其中&#xff0c;牛市看涨价差策略是指投资者买入较低行权价的认购期权、同时卖出数量较高行权价的同月认购期权。 牛市看跌价差策略同理&#xff0c;将看涨期权换成看跌期权即可。 牛市价…

分布式id实战

目录 常用方式 特征 潜在问题 信息安全 高性能 UUID 雪花算法 数据库生成 美团Leaf方案 Leaf-segment 数据库方案 Leaf-snowflake 方案 常用方式 uuid雪花算法数据库主键 特征 全局唯一趋势递增信息安全 潜在问题 信息安全 如果id连续递增, 容易被爬虫, 批量下…

汽车控制器软件正向开发

需求常见问题: 1.系统需求没有分层,没有结构化,依赖关系不明确 2.需求中没有验证准则 3.对客户需求的追溯缺失,不完整,颗粒度不够 4.系统需求没有相应的系统架构,需求没有分解到硬件和软件 5.需求变更管控不严格,变更频繁,变更纪录描述不准确,有遗漏,客户需求多…