├── CMakeLists.txt ├── README.md ├── include ├── Acceptor.h ├── Buffer.h ├── Callbacks.h ├── Channel.h ├── CurrentThread.h ├── EpollPoller.h ├── EventLoop.h ├── EventLoopThread.h ├── EventLoopThreadPool.h ├── InetAddress.h ├── Logger.h ├── Poller.h ├── Socket.h ├── TcpConnection.h ├── TcpServer.h ├── Thread.h ├── Timestamp.h └── noncopyable.h ├── lib └── libyyg_muduo.so └── src ├── Acceptor.cc ├── Buffer.cc ├── Channel.cc ├── CurrentThread.cc ├── DefaultPoller.cc ├── EpollPoller.cc ├── EventLoop.cc ├── EventLoopThread.cc ├── EventLoopThreadPool.cc ├── InetAddress.cc ├── Logger.cc ├── Poller.cc ├── Socket.cc ├── TcpConnection.cc ├── TcpServer.cc ├── Thread.cc └── Timestamp.cc /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.5) 2 | project(yyg_muduo) 3 | 4 | #yyg_muduo最终编译成so 动态库,设置动态库的输出路径,放在根目录的ilb文件夹下 5 | set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) #别写成LIBRAY_OUTPUT_DIRECTORY了 6 | 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++11") 8 | 9 | include_directories(${PROJECT_SOURCE_DIR}/include) 10 | 11 | aux_source_directory(./src SRC_LIST) 12 | 13 | add_library(yyg_muduo SHARED ${SRC_LIST}) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++11 muduo multi-reactor 2 | 用c++11重构了muduo库的核心MultiReactor架构 3 | 4 | 关于这份代码,我写了三万字的剖析博客: https://blog.csdn.net/T_Solotov/article/details/124044175 5 | 这篇博客剖析了里面主要的代码逻辑以及优秀的编程思想启发。 6 | -------------------------------------------------------------------------------- /include/Acceptor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "noncopyable.h" 3 | #include "Channel.h" 4 | #include "Socket.h" 5 | #include 6 | 7 | class EventLoop; 8 | class InetAddress; 9 | 10 | class Acceptor : public muduo::noncopyable 11 | { 12 | public: 13 | using NewConnectionCallback = std::function; 14 | Acceptor(EventLoop* loop, const InetAddress &listenAddr, bool reuseport); 15 | ~Acceptor(); 16 | void setNewConnectionCallback(const NewConnectionCallback &cb) 17 | { 18 | //设置连接事件发生的回调函数。 19 | newConnectionCallback_ = cb; //当有连接事件发生的时候调用newConnectionCallback_ 20 | } 21 | bool listenning() const {return listenning_;} 22 | void listen(); 23 | private: 24 | void handleRead(); 25 | 26 | EventLoop *loop_; 27 | Socket acceptSocket_; 28 | Channel acceptChannel_; // 29 | NewConnectionCallback newConnectionCallback_; 30 | bool listenning_; 31 | }; -------------------------------------------------------------------------------- /include/Buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | /* 6 | 好好看清楚Buffer的样子!!! 7 | +-------------------------+----------------------+---------------------+ 8 | | prependable bytes | readable bytes | writable bytes | 9 | | | (CONTENT) | | 10 | +-------------------------+----------------------+---------------------+ 11 | | | | | 12 | 0 <= readerIndex <= writerIndex size 13 | */ 14 | class Buffer 15 | { 16 | public: 17 | //别搞傻了,readable bytes空间才是要服务端要发送的数据,writable bytes空间是从socket读来的数据存放的地方。 18 | static const size_t kCheapPrepend = 8; //记录数据包的长度的变量长度,用于解决粘包问题 19 | static const size_t kInitialSize = 1024; //缓冲区长度 20 | /*static const int可以在类里面初始化,是因为它既然是const的,那程序就不会再去试图初始化了。*/ 21 | 22 | explicit Buffer(size_t initialSize = kInitialSize) 23 | : buffer_(kCheapPrepend + initialSize) 24 | , readerIndex_(kCheapPrepend) 25 | , writerIndex_(kCheapPrepend) 26 | {} 27 | size_t readableBytes() const 28 | { 29 | return writerIndex_ - readerIndex_; 30 | } 31 | size_t writableBytes() const 32 | { 33 | return buffer_.size() - writerIndex_; 34 | } 35 | size_t prependableBytes() const 36 | { 37 | return readerIndex_; 38 | } 39 | const char* peek() const //peek函数返回可读数据的起始地址 40 | { 41 | return begin() + readerIndex_; 42 | } 43 | void retrieve(size_t len) 44 | { 45 | if(len < readableBytes()) 46 | { 47 | readerIndex_ += len; //应用只读取可读缓冲区数据的一部分,就是len 48 | } 49 | else //len == readableBytes() 50 | { 51 | retrieveAll(); 52 | } 53 | 54 | } 55 | void retrieveAll() 56 | { 57 | readerIndex_ = writerIndex_ = kCheapPrepend; 58 | } 59 | std::string retrieveAllString() 60 | { 61 | //把onMessage函数上报的Buffer数据转成string类型的数据返回 62 | return retrieveAsString(readableBytes()); //应用可读取数据的长度 63 | } 64 | std::string retrieveAsString(size_t len) 65 | { 66 | std::string result(peek(), len); //peek()返回的是可读数据的起始地址 67 | retrieve(len); //上面把一句把缓冲区中的可读数据读取出来,要然后要对缓冲区进行复位操作。 68 | return result; 69 | } 70 | void ensureWritableBytes(size_t len) 71 | { 72 | // writableBytes()返回可写缓冲区的大小 73 | if(writableBytes() < len){ 74 | //扩容 75 | makeSpace(len); 76 | } 77 | } 78 | void append(const char* data, size_t len) 79 | {//把[data, data+len]内存上的数据添加到writable缓冲区当中 80 | ensureWritableBytes(len); //反正每次写之前都会看有没有必要调整一下Buffer缓冲区结构 81 | std::copy(data, data+len,beginWrite()); 82 | writerIndex_ += len; 83 | } 84 | char* beginWrite() 85 | { 86 | return begin() + writerIndex_; 87 | } 88 | const char* beginWrite() const 89 | { 90 | return begin() + writerIndex_; 91 | } 92 | ssize_t readFd(int fd, int* saveErrno); 93 | ssize_t writeFd(int fd, int* saveErrno); 94 | private: 95 | char* begin() 96 | { 97 | return &*buffer_.begin(); 98 | //先调用begin()返回buffer_的首个元素的迭代器,然后再解引用得到这个变量的,再取地址,得到这个变量的首地址。 99 | //反正最后得到的就是vector底层的数组的首地址 100 | } 101 | const char* begin() const{ 102 | return &*buffer_.begin(); 103 | } 104 | 105 | void makeSpace(size_t len) 106 | { 107 | if(writableBytes() + prependableBytes() - kCheapPrepend < len) 108 | {//能用来写的缓冲区大小 < 我要写入的大小len,那么就要扩容了 109 | buffer_.resize(writerIndex_+len); 110 | } 111 | else{ 112 | //如果能写的缓冲区大小 >= 要写的len,那么说明要重新调整一下Buffer的两个游标了。 113 | size_t readable = readableBytes(); 114 | std::copy(begin() + readerIndex_, //源 首迭代器 115 | begin() + writerIndex_, //源 尾迭代器 116 | begin() + kCheapPrepend); //目标 首迭代器 117 | //这里把可读区域的数据给前移了 118 | readerIndex_ = kCheapPrepend; 119 | writerIndex_ = readerIndex_ + readable; 120 | } 121 | 122 | } 123 | std::vector buffer_; //为什么要用vector,因为可以动态扩容啊!!! 124 | size_t readerIndex_; 125 | size_t writerIndex_; 126 | }; 127 | -------------------------------------------------------------------------------- /include/Callbacks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | class Buffer; 5 | class TcpConnection; 6 | class TimeStamp; 7 | /*** 8 | 这里的编程思想还有待摸清,首先我是先决定了TcpConnection应该用智能指针管理,毕竟这种对象 9 | 创建容易泄露也容易,而且也不是那种创建删除很频繁的对象。而TcpConnection内部 10 | */ 11 | 12 | using TcpConnectionPtr = std::shared_ptr; 13 | using ConnectionCallback = std::function; 14 | using CloseCallback = std::function; 15 | using WriteCompleteCallback = std::function; 16 | using MessageCallback = std::function; 17 | using HighWaterMarkCallback = std::function; 18 | -------------------------------------------------------------------------------- /include/Channel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "noncopyable.h" 3 | #include 4 | #include "Timestamp.h" 5 | #include 6 | class EventLoop; 7 | class Channel : public muduo::noncopyable 8 | { 9 | /* 10 | Channel封装了sockfd和它感兴趣的event,比如EPOLLIN和EPOLLOUT事件 11 | 还绑定了poller返回的具体事件。这个poller指代的就是epoll或poll,不过我只 12 | 实现了epoll没有实现poll。 13 | */ 14 | public: 15 | using EventCallback = std::function; //事件回调 16 | using ReadEventCallback = std::function;//只读事件的回调,他其实也是一个EventCallback,只不过需要一个TimStamp而已 17 | 18 | Channel(EventLoop* loop, int fd); 19 | ~Channel(); 20 | 21 | void HandlerEvent(TimeStamp receive_time); 22 | //这个TimeStamp作为函数参数类型,编译的时候是需要知道他的大小的,所以必须要include 23 | //Tmiestamp.h而不能仅仅是声明 TimeStamp类。 24 | 25 | //设置回调函数对象 26 | void setReadCallback(ReadEventCallback cb) {read_callback_ = std::move(cb);}//将cb这个左值转换为右值引用 27 | void setWriteCallback(EventCallback cb) {write_callback_ = std::move(cb);} //这个cb是左值对象,通过move转换为右值引用赋值给write_call_back_,这里编译器会把这个对象的所有权转移给write_call_back_。不用复制 28 | void setCloseCallback(EventCallback cb) {close_callback_ = std::move(cb);} 29 | void setErrorCallback(EventCallback cb) {error_callback_ = std::move(cb);} 30 | void tie(const std::shared_ptr&);//防止当channel被手动remove掉,channel还正在执行回调操作 31 | int fd() const {return fd_;} 32 | int events() const {return events_;} 33 | 34 | int set_revents(int revt) { revents_ = revt;} //通过这个设置得知当前channel的fd发生了什么事件类型(可读、可写、错误、关闭) 35 | 36 | void enableReading() {events_ |= kReadEvent; update();} //设置fd相应的事件状态,比如让这个fd监听可读事件,你就必须要用epoll_ctl设置它! 37 | void disableReading() {events_ &= ~kReadEvent; update();} 38 | void enableWriting() {events_ |= kWriteEvent; update();} 39 | void disableWriting() {events_ &= ~kWriteEvent; update();} 40 | void disableAll() {events_ = kNoneEvent; update();} 41 | bool isWriting() const {return events_ & kWriteEvent;} //当前感兴趣的事件是否包含可写事件 42 | bool isReading() const {return events_ & kReadEvent;} //当前感兴趣的事件是否包含可读事件 43 | bool isNoneEvent() const {return events_ == kNoneEvent;} 44 | 45 | int index() {return index_;} 46 | void set_index(int idx) {index_ = idx;} 47 | 48 | //one loop per thread 49 | EventLoop* ownerLoop() {return loop_;} //当前这个channel属于哪一个event loop 50 | void remove(); 51 | 52 | private: 53 | void update(); 54 | void HandleEventWithGuard(TimeStamp receiveTime);//和上面的handleEvent差不多,处理受保护的事件 55 | 56 | //下面三个变量代表这个fd对哪些事件类型感兴趣 57 | static const int kNoneEvent; //0 58 | static const int kReadEvent; // = EPOLLIN | EPOLLPRI; EPOLLPRI带外数据,和select的异常事件集合对应 59 | static const int kWriteEvent; // = EPOLLOUT 60 | 61 | EventLoop* loop_; //这个channel属于哪个EventLoop对象 62 | const int fd_;//fd, poller监听的对象 63 | int events_; //注册fd感兴趣的事件 64 | int revents_; //poller返回的具体发生的事件。 65 | int index_; //这个index_其实表示的是这个channel的状态,是kNew还是kAdded还是kDeleted 66 | //kNew代表这个channel未添加到poller中,kAdded表示已添加到poller中,kDeleted表示从poller中删除了 67 | 68 | std::weak_ptr tie_; //这个tie_ 绑定了.... 69 | bool tied_; 70 | //channel通道里面能够获知fd最终发生的具体事件revents,所以它负责调用具体事件的回调操作。 71 | ReadEventCallback read_callback_; 72 | EventCallback write_callback_; 73 | EventCallback close_callback_; 74 | EventCallback error_callback_; 75 | }; -------------------------------------------------------------------------------- /include/CurrentThread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace CurrentThread 7 | { 8 | extern __thread int t_cachedTid; //通过__thread 修饰的变量,在线程中地址都不一样, 9 | //__thread变量每一个线程有一份独立实体,各个线程的值互不干扰。 10 | 11 | void cacheTid(); 12 | 13 | inline int tid() 14 | { 15 | if (__builtin_expect(t_cachedTid == 0, 0)) //提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。 16 | { //这里的意思时说 t_cachedTid == 0的可能性很小,不过如果t_cachedTid==0那就要调用cacheTid获取线程的tid了。 17 | cacheTid(); 18 | } 19 | return t_cachedTid; 20 | } 21 | } -------------------------------------------------------------------------------- /include/EpollPoller.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Poller.h" 3 | #include 4 | #include 5 | #include "Timestamp.h" 6 | /* 7 | * epoll的使用:https://www.cnblogs.com/fnlingnzb-learner/p/5835573.html 8 | 1. int epoll_create(int size); 9 | 创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大, 10 | 当创建好epoll句柄后,它就是会占用一个fd值 11 | 2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 12 | epoll的事件注册函数在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示: 13 | EPOLL_CTL_ADD:注册新的fd到epfd中; 14 | EPOLL_CTL_MOD:修改已经注册的fd的监听事件; 15 | EPOLL_CTL_DEL:从epfd中删除一个fd; 16 | 第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事 17 | struct epoll_event结构如下: 18 | typedef union epoll_data { 19 | void *ptr; 20 | int fd; 21 | __uint32_t u32; 22 | __uint64_t u64; 23 | } epoll_data_t; 24 | 25 | struct epoll_event { 26 | __uint32_t events; // Epoll events 27 | epoll_data_t data; 28 | }; 29 | events可以是以下几个宏的集合: 30 | EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭); 31 | EPOLLOUT:表示对应的文件描述符可以写; 32 | EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来); 33 | EPOLLERR:表示对应的文件描述符发生错误; 34 | EPOLLHUP:表示对应的文件描述符被挂断; 35 | EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。 36 | EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里 37 | 3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); 38 | 等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合, 39 | maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create() 40 | 时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。 41 | 该函数返回需要处理的事件数目,如返回0表示已超时。 42 | */ 43 | class EpollPoller : public Poller 44 | { 45 | public: 46 | EpollPoller(EventLoop *loop); 47 | ~EpollPoller() override; //子类设计者可以在其析构函数后增加关键字override,一旦父类缺少关键字virtual就会被编译器发现并报错 48 | 49 | //重写基类Poller的抽象方法 50 | TimeStamp poll(int timeoutMs, ChannelList *activeChannels) override; 51 | void updateChannel(Channel *channel) override;//增加一个channel 52 | void removeChannel(Channel *channel) override;//将一个channel移除 53 | 54 | private: 55 | static const int kInitEventListSize = 16; //事件列表的初始长度。 56 | 57 | // 填写活跃的连接 58 | void fillActiveChannels(int numEvents, ChannelList *activeChannels) const; 59 | 60 | // 更新channel通道 61 | void update(int operation, Channel *channel); 62 | 63 | using EventList = std::vector; 64 | int epollfd_; //epoll事件循环本身还需要一个fd 65 | EventList events_; 66 | }; -------------------------------------------------------------------------------- /include/EventLoop.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "noncopyable.h" 6 | #include "Timestamp.h" 7 | #include 8 | #include 9 | #include "CurrentThread.h" 10 | class Channel; 11 | class Poller; 12 | 13 | //事件循环类, 主要包含了两大模块,channel 和 poller 14 | class EventLoop : public muduo::noncopyable 15 | { 16 | public: 17 | using Functor = std::function; 18 | EventLoop(); 19 | ~EventLoop(); 20 | void loop(); //开启事件循环 21 | void quit(); //关闭事件循环 22 | TimeStamp poolReturnTime() const {return pollReturnTime_;} 23 | void runInLoop(Functor cb); //mainReactor用于唤醒Subreactor的 24 | void queueInLoop(Functor cb); // 25 | void wakeup(); 26 | void updateChannel(Channel *channel); 27 | void removeChannel(Channel *channel); 28 | bool hasChannel(Channel *channel); 29 | 30 | //判断当前的eventloop对象是否在自己的线程里面 31 | bool isInLoopThread() const {return threadId_ == CurrentThread::tid();} 32 | 33 | private: 34 | void handleRead(); //处理唤醒相关的逻辑。 35 | void doPendingFunctors();//执行回调的 36 | 37 | using ChannelList = std::vector; 38 | std::atomic looping_; //标志进入loop循环 39 | std::atomic quit_; //标志退出loop循环 这个和looping_ 其实本质上有重叠 40 | std::atomic callingPendingFunctors_; //标识当前loop是否有需要执行回调操作 41 | const pid_t threadId_; //当前loop所在的线程的id 42 | TimeStamp pollReturnTime_; //poller返回发生事件的channels的时间点 43 | std::unique_ptr poller_; //一个EventLoop需要一个poller,这个poller其实就是操控这个EventLoop的对象。 44 | //统一事件源 45 | int wakeupFd_; //主要作用,当mainLoop获取一个新用户的channel通过轮询算法选择一个subloop(subreactor)来处理channel。 46 | std::unique_ptr wakeupChannel_; 47 | ChannelList activeChannels_; 48 | Channel *currentActiveChannel_; 49 | std::vector pendingFunctors_; //存储loop需要执行的所有回调操作。 50 | std::mutex mutex_; //保护上面vector容器的线程安全操作。 51 | }; 52 | -------------------------------------------------------------------------------- /include/EventLoopThread.h: -------------------------------------------------------------------------------- 1 | /*** 2 | EventLoopThread类绑定了一个EventLoop和一个线程 3 | */ 4 | 5 | #pragma once 6 | #include "noncopyable.h" 7 | #include 8 | #include 9 | #include 10 | #include "Thread.h" 11 | #include 12 | class EventLoop; 13 | 14 | class EventLoopThread : public muduo::noncopyable 15 | { 16 | public: 17 | using ThreadInitCallback = std::function; 18 | EventLoopThread(const ThreadInitCallback &cb = ThreadInitCallback(), const std::string &name = std::string()); 19 | ~EventLoopThread(); 20 | EventLoop* startLoop(); 21 | 22 | private: 23 | void threadFunc(); 24 | EventLoop *loop_; 25 | bool exiting_; 26 | Thread thread_; 27 | std::mutex mutex_; 28 | std::condition_variable cond_; 29 | ThreadInitCallback callback_; 30 | }; 31 | -------------------------------------------------------------------------------- /include/EventLoopThreadPool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "noncopyable.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | class EventLoop; 8 | class EventLoopThread; 9 | 10 | class EventLoopThreadPool:public muduo::noncopyable 11 | { 12 | /* 13 | 管理EventLoopThread的线程池 14 | */ 15 | public: 16 | using ThreadInitCallback = std::function; 17 | EventLoopThreadPool(EventLoop *baseLoop, const std::string &nameArg); 18 | ~EventLoopThreadPool(); 19 | void setThreadNum(int numThreads){numThreads_ = numThreads;} 20 | void start(const ThreadInitCallback &cb = ThreadInitCallback()); 21 | 22 | //如果是工作在多线程中,baseLiio_默认以轮循的方式分配channel给subloop 23 | //当然还有一个哈希的定制方式分配channel,这里就暂时不写他了 24 | EventLoop* getNextLoop(); 25 | std::vector getAllGroups(); 26 | bool started() const {return started_;} 27 | const std::string name() const {return name_;} 28 | 29 | private: 30 | EventLoop *baseLoop_; //如果你没有通过setThreadNum来设置线程数量,那整个网络框架就只有一个 31 | //线程,这唯一的一个线程就是这个baseLoop_,既要处理新连接,还要处理已连接的事件监听。 32 | std::string name_; 33 | bool started_; 34 | int numThreads_; 35 | int next_; 36 | std::vector> threads_; 37 | std::vector loops_; //包含了所有EventLoop线程的指针 38 | 39 | 40 | }; -------------------------------------------------------------------------------- /include/InetAddress.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // 4 | #include //sockaddr_in 5 | #include 6 | 7 | /*** 8 | 封装socket地址类型,这里就没有对ipv6进行封装了,只对ipv4进行封装 9 | */ 10 | class InetAddress 11 | { 12 | public: 13 | explicit InetAddress(uint16_t port = 0, std::string ip = "127.0.0.1"); 14 | explicit InetAddress(const sockaddr_in &addr); 15 | std::string toIp() const; 16 | std::string toIpPort() const; 17 | uint16_t toPort() const; 18 | 19 | const sockaddr_in *getSockAddr() const; 20 | void setSockAddr(const sockaddr_in &addr){addr_ = addr;} 21 | private: 22 | sockaddr_in addr_; 23 | }; -------------------------------------------------------------------------------- /include/Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "noncopyable.h" 4 | /* 5 | 日志级别:INFO ERROR FATAL DEBUG 6 | */ 7 | //LOG_INFO(%s %d, agr1, arg2) 8 | #define LOG_INFO(LogmsgFormat, ...) \ 9 | do \ 10 | { \ 11 | Logger &logger = Logger::Instance(); \ 12 | logger.setLogLevel(INFO); \ 13 | char buf[1024] = {0}; \ 14 | snprintf(buf, 1024, LogmsgFormat, ##__VA_ARGS__);\ 15 | logger.log(buf); \ 16 | } while(0) 17 | #define LOG_ERROR(LogmsgFormat, ...) \ 18 | do \ 19 | { \ 20 | Logger &logger = Logger::Instance(); \ 21 | logger.setLogLevel(ERROR); \ 22 | char buf[1024] = {0}; \ 23 | snprintf(buf, 1024, LogmsgFormat, ##__VA_ARGS__);\ 24 | logger.log(buf); \ 25 | } while(0) 26 | #define LOG_FATAL(LogmsgFormat, ...) \ 27 | do \ 28 | { \ 29 | Logger &logger = Logger::Instance(); \ 30 | logger.setLogLevel(FATAL); \ 31 | char buf[1024] = {0}; \ 32 | snprintf(buf, 1024, LogmsgFormat, ##__VA_ARGS__);\ 33 | logger.log(buf); \ 34 | exit(-1);\ 35 | } while(0) 36 | 37 | #ifdef MUDEBUG 38 | #define LOG_DEBUG(LogmsgFormat, ...) \ 39 | do \ 40 | { \ 41 | Logger &logger = Logger::Instance(); \ 42 | logger.setLogLevel(DEBUG); \ 43 | char buf[1024] = {0}; \ 44 | snprintf(buf, 1024, LogmsgFormat, ##__VA_ARGS__);\ 45 | logger.log(buf); \ 46 | } while(0) 47 | #else 48 | #define LOG_DEBUG(LogmsgFormat, ...) 49 | #endif 50 | 51 | enum LoggerLevel{ 52 | INFO, //普通日志信息 53 | ERROR, //错误日志信息 54 | FATAL, //core dump信息 55 | DEBUG //调试信息 56 | }; 57 | 58 | class Logger 59 | { 60 | public: 61 | static Logger& Instance(); //获取日志实例对象 62 | void setLogLevel(int level); //设置日志级别 63 | void log(std::string msg); //写日志 64 | private: 65 | Logger() = default; 66 | int level_; //日志级别 67 | 68 | }; 69 | 70 | -------------------------------------------------------------------------------- /include/Poller.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "noncopyable.h" 3 | #include 4 | #include 5 | #include "Channel.h" 6 | class Channel; 7 | class EventLoop; 8 | class Poller : public muduo::noncopyable 9 | { 10 | public: 11 | using ChannelList = std::vector; 12 | Poller(EventLoop *loop); 13 | virtual ~Poller(); 14 | 15 | //给所有io复用保留统一的接口,当前激活的channels,需要poller去循查的channel(fd) 16 | virtual TimeStamp poll(int timeoutMs, ChannelList *ativateChannels) = 0; 17 | 18 | virtual void updateChannel(Channel *channel) = 0; 19 | virtual void removeChannel(Channel *channel) = 0; 20 | 21 | bool hasChannel(Channel *channel) const; //判断一个poller里面有没有这个channel 22 | 23 | //EventLoop可以通过该接口获取默认的IO复用的具体实现 24 | static Poller* newDefaultPoller(EventLoop *loop); 25 | 26 | protected: 27 | using ChannelMap = std::unordered_map; 28 | ChannelMap channels_; 29 | private: 30 | EventLoop *ownerLoop_; 31 | }; 32 | -------------------------------------------------------------------------------- /include/Socket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "noncopyable.h" 3 | class InetAddress; 4 | class Socket : public muduo::noncopyable 5 | { 6 | public: 7 | explicit Socket(int sockfd) : sockfd_(sockfd){} 8 | ~Socket(); 9 | 10 | public: 11 | int fd() const {return sockfd_;} 12 | void bindAddress(const InetAddress &localaddr); //调用bind绑定服务器IP端口 13 | void listen(); //调用listen监听套接字 14 | int accept(InetAddress *peeradd); //调用accept接受新客户连接请求 15 | void shutdownWrite(); //调用shutdown关闭服务端写通道 16 | 17 | /** 下面四个函数都是调用setsockopt来设置一些socket选项 **/ 18 | void setTcpNoDelay(bool on); //不启用naggle算法,增大对小数据包的支持 19 | void setReuseAddr(bool on); 20 | void setReusePort(bool on); 21 | void setKeepAlive(bool on); 22 | 23 | private: 24 | const int sockfd_; //服务器监听套接字文件描述符 25 | }; -------------------------------------------------------------------------------- /include/TcpConnection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "noncopyable.h" 4 | #include 5 | #include 6 | #include "InetAddress.h" 7 | #include "Callbacks.h" 8 | #include "Timestamp.h" 9 | #include "Buffer.h" 10 | class Channel; 11 | class EventLoop; 12 | class Socket; 13 | 14 | /** 15 | 原文链接:https://blog.csdn.net/breadheart/article/details/112451022 16 | 1. 为什么要用enable_shared_from_this? 17 | * 需要在类对象的内部中获得一个指向当前对象的shared_ptr 对象 18 | * 如果在一个程序中,对象内存的生命周期全部由智能指针来管理。在这种情况下, 19 | 要在一个类的成员函数中,对外部返回this指针就成了一个很棘手的问题。 20 | 2. 什么时候用? 21 | * 当一个类被 share_ptr 管理,且在类的成员函数里需要把当前类对象作为参数传给其他函数时, 22 | 这时就需要传递一个指向自身的 share_ptr。 23 | 3. 效果: 24 | TcpConnection类继承 std::enable_shared_from_this ,则会为该类TcpConnection提供成员函数 25 | shared_from_this。TcpConnection对象 t 被一个为名为 pt 的 std::shared_ptr 类对象管理时, 26 | 调用 T::shared_from_this 成员函数,将会返回一个新的 std::shared_ptr 对象, 27 | 它与 pt 共享 t 的所有权。 28 | */ 29 | 30 | 31 | class TcpConnection : public muduo::noncopyable, public std::enable_shared_from_this 32 | { 33 | /** 34 | * @brief TcpConnection主要用于打包成功连接的TCP连接通信链路。 35 | * TcpServer => Acceptor => 新用户连接,通过accept函数拿到connfd =》 TcpConnection设置回调 => Channel => Poller => Poller监听到事件 => Channel的回调操作 36 | */ 37 | public: 38 | TcpConnection(EventLoop* loop, const std::string &name, int sockfd, const InetAddress &localAddr, const InetAddress &peerAddr); 39 | ~TcpConnection(); 40 | EventLoop* getLoop() const {return loop_;} 41 | const InetAddress& localAddress() const {return localAddr_;} 42 | const InetAddress& peerAddress() const {return peerAddr_;} 43 | bool connected() const {return state_ == kConnected;} 44 | void send(const std::string &buf); 45 | void shutdown(); 46 | const std::string& name() const { return name_;} 47 | void setConnectionCallback(const ConnectionCallback& cb) {connectionCallback_ = cb;} 48 | void setMessageCallback(const MessageCallback& cb) {messageCallback_ = cb;} 49 | void setWriteCompleteCallback(const WriteCompleteCallback& cb) {writeCompleteCallback_ = cb;} 50 | void setCloseCallback(const CloseCallback& cb){closeCallback_ = cb;} 51 | void setHighWaterMarkCallback(const HighWaterMarkCallback& cb, size_t highWaterMark) {highWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark;} 52 | // 连接建立 53 | void connectEstablished(); 54 | // 连接销毁 55 | void connectDestroyed(); 56 | private: 57 | enum StateE {kDisconnected, kConnecting, kConnected, kDisconnecting}; 58 | void setState(StateE state) {state_ = state;} 59 | void handleRead(TimeStamp receiveTime); 60 | void handleWrite(); 61 | void handleClose(); 62 | void handleError(); 63 | 64 | void sendInLoop(const void* message, size_t len); 65 | void shutdownInLoop(); 66 | 67 | EventLoop *loop_; //这里绝对不是baseLoop,因为TcpConnection都是在subLoop里面管理的 68 | const std::string name_; 69 | std::atomic state_; 70 | bool reading_; 71 | 72 | std::unique_ptr socket_; 73 | std::unique_ptr channel_; 74 | 75 | const InetAddress localAddr_; 76 | const InetAddress peerAddr_; 77 | 78 | ConnectionCallback connectionCallback_; //有新连接时的回调 79 | MessageCallback messageCallback_; //有读写消息时的回调 80 | WriteCompleteCallback writeCompleteCallback_; //消息发送完后的回调 81 | HighWaterMarkCallback highWaterMarkCallback_;//发送和接收缓冲区超过水位线的话容易导致溢出,这是很危险的。 82 | CloseCallback closeCallback_; 83 | size_t highWaterMark_; //水位线 84 | Buffer inputBuffer_; //接收的缓冲区大小,我猜的 85 | Buffer outputBuffer_; //发送的缓冲区大小,我猜的 86 | 87 | }; -------------------------------------------------------------------------------- /include/TcpServer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | //TcpServere和使用网络库的程序员直接交互,最好把该包含的头文件都包含了,不然用户还要自己包含, 3 | //就挺拉稀的 4 | #include "noncopyable.h" 5 | #include "EventLoop.h" 6 | #include "Acceptor.h" 7 | #include "InetAddress.h" 8 | #include "noncopyable.h" 9 | #include 10 | #include "EventLoop.h" 11 | #include "EventLoopThreadPool.h" 12 | #include "Callbacks.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | class TcpServer : public muduo::noncopyable 18 | { 19 | public: 20 | using ThreadInitCallback = std::function; 21 | enum Option 22 | { //预置两个选项,是否对端口进行复用 23 | kNoReusePort, 24 | kReusePort, 25 | }; 26 | TcpServer(EventLoop* loop, 27 | const InetAddress &listenAddr, 28 | const std::string &nameArg, 29 | Option option = kNoReusePort); //默认不重用端口 30 | ~TcpServer(); 31 | void setThreadInitcallback(const ThreadInitCallback &cb){ 32 | threadInitCallback_ = cb; 33 | } 34 | void setConnectionCallback(const ConnectionCallback &cb){ 35 | connectionCallback_ = cb; 36 | } 37 | void setMessageCallback(const MessageCallback &cb){ 38 | messageCallback_ = cb; 39 | } 40 | void setWriteCompleteCallback(const WriteCompleteCallback &cb){ 41 | writeCompleteCallback_ = cb; 42 | } 43 | void setThreadNum(int numThreads); //设置底层subloop个数 44 | void start(); //开启服务器监听 45 | private: 46 | void newConnection(int sockfd, const InetAddress &peerAddr); 47 | void removeConnection(const TcpConnectionPtr &conn); 48 | void removeConnectionInLoop(const TcpConnectionPtr &conn); 49 | 50 | using ConnectionMap = std::unordered_map; 51 | EventLoop *loop_; //baseLoop,用户自己定义的 52 | const std::string ipPort_; 53 | const std::string name_; 54 | std::unique_ptr acceptor_; //运行在baseLoop,任务就是监听新连接事件 55 | std::shared_ptr threadPool_; //one loop per thread 56 | ConnectionCallback connectionCallback_; //有新连接时的回调 57 | MessageCallback messageCallback_; //有读写消息时的回调 58 | WriteCompleteCallback writeCompleteCallback_; //消息发送完成的回调 59 | ThreadInitCallback threadInitCallback_; //loop线程初始化的回调 60 | 61 | std::atomic started_; 62 | int nextConnId_; 63 | ConnectionMap connections_; //保存所有的连接 64 | 65 | }; -------------------------------------------------------------------------------- /include/Thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "noncopyable.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class Thread : muduo::noncopyable 11 | { //只关注一个线程 12 | public: 13 | using ThreadFunc = std::function; 14 | explicit Thread(ThreadFunc, const std::string &name = std::string());//这个写法我倒是少见 15 | ~Thread(); 16 | void start(); 17 | void join(); 18 | 19 | bool started() const{return started_;} 20 | pid_t tid() const {return tid_;} //muduo库上返回的tid相当于linux上用top命令查出来的tid,不是pthread的tid。 21 | static int numCreated(){return numCreated_;} 22 | 23 | 24 | private: 25 | void setDefaultName(); 26 | bool started_; 27 | bool joined_; 28 | pid_t tid_; 29 | ThreadFunc func_; 30 | std::string name_; 31 | std::shared_ptr thread_;//注意这里,如果你直接定义一个thread对象, 32 | //那这个线程就直接开始运行了,所以这里定义一个智能指针,在需要运行的时候再给他创建对象。 33 | static std::atomic numCreated_; 34 | }; -------------------------------------------------------------------------------- /include/Timestamp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | class TimeStamp 6 | { 7 | /* 8 | 这里只复现了一部分用到的TimeStamp方法,其他的方法因为没用到,所以就没复现了。 9 | */ 10 | public: 11 | TimeStamp(); 12 | explicit TimeStamp(int64_t microSecondSinceEpoch); //禁止隐式转换 13 | static TimeStamp now(); //返回当前时间 14 | std::string toString() const; 15 | 16 | private: 17 | int64_t microSecondSinceEpoch_; //muduo中用了一个int64_t作为当前时间,单位为微妙 18 | }; -------------------------------------------------------------------------------- /include/noncopyable.h: -------------------------------------------------------------------------------- 1 | #pragma once //编译级别,防止重复包含 2 | 3 | namespace muduo{ 4 | class noncopyable 5 | { 6 | /** 7 | * @brief 派生类可以正常构构造和析构,但是不能拷贝构造和赋值。这种做法一定要好好学习一下!! 8 | */ 9 | public: 10 | noncopyable(const noncopyable&) = delete; 11 | noncopyable& operator=(const noncopyable&) = delete; //如果要做连续赋值的话就返回noncopyable&,不然就返回void 12 | protected: 13 | noncopyable() = default; 14 | ~noncopyable() = default; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /lib/libyyg_muduo.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yyg192/Cpp11-Muduo-MultiReactor/baeecfce537da95c3516b5276955fb20fa82d6b7/lib/libyyg_muduo.so -------------------------------------------------------------------------------- /src/Acceptor.cc: -------------------------------------------------------------------------------- 1 | #include "Acceptor.h" 2 | #include 3 | #include 4 | #include "Logger.h" 5 | #include 6 | #include "InetAddress.h" 7 | /*** 8 | 9 | */ 10 | 11 | static int createNonblocking() 12 | { 13 | /** 14 | * @brief 创建一个非阻塞的IO 15 | * 16 | */ 17 | int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK| SOCK_CLOEXEC, IPPROTO_TCP); 18 | if(sockfd < 0) 19 | { 20 | LOG_FATAL("%s:%s:%d listen socket create err:%d \n", __FILE__, __FUNCTION__, __LINE__,errno); 21 | } 22 | return sockfd; 23 | 24 | } 25 | 26 | Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport) 27 | :loop_(loop) 28 | ,acceptSocket_(createNonblocking()) //可以看出一个服务端一个acceptSocket_了 29 | ,acceptChannel_(loop, acceptSocket_.fd())//这里的loop是baseLoop_ 30 | ,listenning_(false) 31 | { 32 | acceptSocket_.setReuseAddr(true); //设置socket选项 33 | acceptSocket_.setReusePort(true); //设置socket选项 34 | acceptSocket_.bindAddress(listenAddr); //bind 35 | // TcpServer::start() Acceptor.listen 有新用户连接 执行一个回调 connfd => channel => subloop 36 | // baseLoop_ 监听到Accpetor有监听事件,baseLoop_就会帮我们新客户连接的回调函数 37 | acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this)); 38 | } 39 | Acceptor::~Acceptor() 40 | { 41 | acceptChannel_.disableAll(); 42 | acceptChannel_.remove(); 43 | } 44 | void Acceptor::listen() 45 | { 46 | /** 47 | * @brief 开启对server socket fd的可读事件监听 48 | * 49 | */ 50 | listenning_ = true; 51 | acceptSocket_.listen(); 52 | acceptChannel_.enableReading(); 53 | } 54 | 55 | void Acceptor::handleRead() 56 | { 57 | //server socket fd 有读事件发生了,即有新用户连接了,就会调用这个handleRead 58 | InetAddress peerAddr; 59 | //当有新用户连接了又会调用Socket::accpet函数,该函数底层真正调用了sccket编程的accept函数 60 | //并且把peerAddr设置好后就传递给newConnectionCallback_来调用真正的新用户连接的处理函数。 61 | int connfd = acceptSocket_.accept(&peerAddr); 62 | 63 | if(connfd >= 0) 64 | { 65 | if(newConnectionCallback_) 66 | newConnectionCallback_(connfd, peerAddr);//轮循找到subloop,唤醒,然后分发当前客户端的channel 67 | else 68 | ::close(connfd); //其实这个几乎不会被执行 69 | } 70 | else 71 | { 72 | LOG_ERROR("%s:%s:%d accept err:%d \n", __FILE__, __FUNCTION__, __LINE__, errno); 73 | if(errno == EMFILE) //进程的fd已用尽 74 | { 75 | LOG_ERROR("%s:%s:%d sockfd reached limit! \n", __FILE__, __FUNCTION__, __LINE__); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/Buffer.cc: -------------------------------------------------------------------------------- 1 | #include "Buffer.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | ssize_t readv(int fd, const struct iovec *iov, int iovcnt); 9 | 使用read()将数据读到不连续的内存、使用write将不连续的内存发送出去,需要多次调用read和write 10 | 如果要从文件中读一片连续的数据到进程的不同区域,有两种方案,要么使用read一次将它们读到 11 | 一个较大的缓冲区中,然后将他们分成若干部分复制到不同的区域,要么调用read若干次分批把他们读至 12 | 不同的区域,这样,如果想将程序中不同区域的连续数据块写到文件,也必须进行类似的处理。 13 | 频繁系统调用和拷贝开销比较大,所以Unix提供了另外两个函数readv()和writev(),它们只需要 14 | 一次系统调用就可以实现在文件和进程的多个缓冲区之间传送数据,免除多次系统调用或复制数据的开销。 15 | readv叫散布读,即把若干连续的数据块读入内存分散的缓冲区中, 16 | writev叫聚集写,吧内存中分散的若干缓冲区写到文件的连续区域中 17 | */ 18 | 19 | ssize_t Buffer::readFd(int fd, int* saveErrno) 20 | { 21 | //从客户端套接字fd上读取数据, Poller工作在LT模式,只要数据没读完epoll_wait就会一直上报 22 | char extrabuf[65536] = {0}; //栈上的内存空间 23 | struct iovec vec[2]; 24 | const size_t writableSpace = writableBytes(); //可写缓冲区的大小 25 | vec[0].iov_base = begin() + writerIndex_; //第一块缓冲区 26 | vec[0].iov_len = writableSpace; //当我们用readv从socket缓冲区读数据,首先会先填满这个vec[0] 27 | //也就是我们的Buffer缓冲区 28 | vec[1].iov_base = extrabuf; //第二块缓冲区,如果Buffer缓冲区都填满了,那就填到我们临时创建的 29 | vec[1].iov_len = sizeof(extrabuf); //栈空间上。 30 | const int iovcnt = (writableSpace < sizeof(extrabuf) ? 2 : 1); 31 | //如果Buffer缓冲区大小比extrabuf(64k)还小,那就Buffer和extrabuf都用上 32 | //如果Buffer缓冲区大小比64k还大或等于,那么就只用Buffer。这意味着,我们最少也能一次从socket fd读64k空间 33 | const ssize_t n = ::readv(fd, vec, iovcnt); 34 | if(n < 0){ 35 | *saveErrno = errno; //出错了!! 36 | } 37 | else if(n <= writableSpace){ //说明Buffer空间足够存了 38 | writerIndex_ += n; // 39 | } 40 | else{ //Buffer空间不够存,需要把溢出的部分(extrabuf)倒到Buffer中(会先触发扩容机制) 41 | writerIndex_ = buffer_.size(); 42 | append(extrabuf, n-writableSpace); 43 | } 44 | return n; 45 | } 46 | ssize_t Buffer::writeFd(int fd, int* saveErrno) 47 | { 48 | //向socket fd上写数据,假如TCP发送缓冲区满 49 | const size_t readableSpace = readableBytes(); 50 | ssize_t n = ::write(fd, peek(), readableSpace); //从Buffer中有的数据(readableBytes)写到socket中 51 | if(n < 0) 52 | { 53 | *saveErrno = errno; 54 | } 55 | return n; 56 | } -------------------------------------------------------------------------------- /src/Channel.cc: -------------------------------------------------------------------------------- 1 | #include "Channel.h" 2 | #include 3 | #include "Logger.h" 4 | #include 5 | using namespace std; 6 | const int Channel::kNoneEvent = 0; 7 | const int Channel::kReadEvent = EPOLLIN | EPOLLPRI; 8 | const int Channel::kWriteEvent = EPOLLOUT; 9 | 10 | Channel::Channel(EventLoop* loop, int fd) 11 | : loop_(loop) 12 | , fd_(fd) 13 | , events_(0) 14 | , revents_(0) 15 | , index_(-1) 16 | , tied_(false) 17 | { 18 | 19 | } //EventLoop对象里面包含了很多的channel 20 | Channel::~Channel(){} 21 | 22 | void Channel::tie(const shared_ptr& obj) 23 | { 24 | /** 25 | * @brief channel的tie方法什么时候调用?一个TcpConnection新连接创建的时候, 26 | * TcpConnection => Channel; 27 | */ 28 | tie_ = obj; 29 | tied_ = true; 30 | } 31 | 32 | void Channel::update() 33 | { 34 | /** 35 | * @brief 当改变channel所对应的fd的events事件后,update负责在poller里面更改 36 | * fd相应的事件epoll_ctl 37 | * 通过channel所属的EventLoop,调用poller的相应方法,注册fd的events事件 38 | * channel和poller通过EventLoop进行连接。 39 | */ 40 | loop_->updateChannel(this); 41 | 42 | } 43 | 44 | void Channel::remove() 45 | { 46 | /** 47 | * @brief 在channel所属的EventLoop中,把这个channel给删掉 48 | */ 49 | loop_->removeChannel(this); 50 | } 51 | void Channel::HandlerEvent(TimeStamp receiveTime) 52 | { 53 | if(tied_){ 54 | shared_ptr guard = tie_.lock(); 55 | if (guard) 56 | HandleEventWithGuard(receiveTime); 57 | } 58 | else 59 | HandleEventWithGuard(receiveTime); 60 | } 61 | void Channel::HandleEventWithGuard(TimeStamp receiveTime) 62 | { 63 | /** 64 | * @brief 根据poller通知的channel发生的具体事件类型,由channel负责调用具体的回调操作。 65 | */ 66 | LOG_INFO("channel HandleEvent revents:%d", revents_); 67 | if((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN))//EPOLLHUP表示这个设备已经断开连接 68 | { 69 | if(close_callback_) 70 | close_callback_(); 71 | } 72 | if(revents_ & EPOLLERR) 73 | { 74 | if(error_callback_) 75 | error_callback_(); 76 | } 77 | if(revents_ & (EPOLLIN | EPOLLPRI)) 78 | { 79 | if(read_callback_) 80 | read_callback_(receiveTime); 81 | } 82 | if (revents_ & EPOLLOUT) 83 | { 84 | if (write_callback_) 85 | write_callback_(); 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /src/CurrentThread.cc: -------------------------------------------------------------------------------- 1 | #include "CurrentThread.h" 2 | 3 | namespace CurrentThread 4 | { 5 | __thread int t_cachedTid = 0; 6 | 7 | void cacheTid() 8 | { 9 | if (t_cachedTid == 0) 10 | { 11 | // 通过linux系统调用,获取当前线程的tid值 12 | t_cachedTid = static_cast(::syscall(SYS_gettid)); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/DefaultPoller.cc: -------------------------------------------------------------------------------- 1 | #include "Poller.h" 2 | #include 3 | #include "EpollPoller.h" 4 | Poller* Poller::newDefaultPoller(EventLoop *loop) 5 | { 6 | //Poller是基类,基类不能引用派生类 PollPoller 或EpollPoller,所以这个 7 | //newDefaultPoller函数的实现必须不能写在Poller.cc里面,最好是专门写在 8 | //一个新的文件里面 9 | if(::getenv("MUDUO_USE_POLL")) //通过环境变量控制选择epoll还是poll 10 | return nullptr; //生成poll实例 11 | else 12 | return new EpollPoller(loop); //生成epoll实例 13 | } -------------------------------------------------------------------------------- /src/EpollPoller.cc: -------------------------------------------------------------------------------- 1 | #include "EpollPoller.h" 2 | #include "Logger.h" 3 | #include "Channel.h" 4 | #include 5 | #include 6 | #include 7 | /* 下面三个常量值表示了一个channel的三种状态 */ 8 | // channel未添加到poller中 9 | const int kNew = -1; // channel的成员index_ = -1 10 | // channel已添加到poller中 11 | const int kAdded = 1; 12 | // channel从poller中删除 13 | const int kDeleted = 2; 14 | 15 | EpollPoller::EpollPoller(EventLoop* loop) 16 | : Poller(loop), 17 | epollfd_(epoll_create1(EPOLL_CLOEXEC)), 18 | events_(kInitEventListSize) 19 | { 20 | //自己去man一下这个epoll_create 21 | /* 22 | epoll_create()创建一个epoll的事例,通知内核需要监听size个fd。size指的并不是最大的后备存储设备, 23 | 而是衡量内核内部结构大小的一个提示。当创建成功后,会占用一个fd,所以记得在使用完之后调用close(), 24 | 否则fd可能会被耗尽。 25 | Note:自从Linux2.6.8版本以后,size值其实是没什么用的,不过要大于0,因为内核可以动态的分配大小, 26 | 所以不需要size这个提示了。 27 | int epoll_create1(int flag);它和epoll_create差不多,不同的是epoll_create1函数的参数是flag, 28 | 当flag是0时,表示和epoll_create函数完全一样,不需要size的提示了。 29 | 当flag = EPOLL_CLOEXEC,创建的epfd会设置FD_CLOEXEC 30 | 当flag = EPOLL_NONBLOCK,创建的epfd会设置为非阻塞 31 | 一般用法都是使用EPOLL_CLOEXEC。 32 | 关于FD_CLOEXEC它是fd的一个标识说明,用来设置文件close-on-exec状态的。当close-on-exec状态为0时,调用exec时, 33 | fd不会被关闭;状态非零时则会被关闭,这样做可以防止fd泄露给执行exec后的进程。因为默认情况下子进程是会继承父进程 34 | 的所有资源的。 35 | */ 36 | if(epollfd_ < 0) //epoll_create创建失败则fatal error退出 37 | LOG_FATAL("epoll create error:%d \n", errno); 38 | } 39 | 40 | EpollPoller::~EpollPoller() 41 | { 42 | close(epollfd_); //关闭文件描述符, unistd.h 43 | } 44 | 45 | /** 下面这些代码其实就是对epoll_ctl的一些封装 **/ 46 | void EpollPoller::updateChannel(Channel *channel) 47 | { 48 | /** 49 | * @brief 先获取channel的状态,根据channel的状态调用update(),其实就是调用epoll_ctl注册或修改或删除这个channel 50 | * 对应的fd的在epoll中的监听事件。 51 | * 52 | */ 53 | ///Channel如果想注册事件的话,它是没办法直接访问Poller的,它要通过EventLoop的updateChannel来注册 54 | //channel update remove => EventLoop updateChannel reomveChannel =>Poller updateChannel removeChannel 55 | const int index = channel->index(); //获取当前channel的状态,刚创建还是已在EventLoop上注册还是已在EventLoop删除 56 | LOG_INFO("fd=%d events=%d index=%d \n",channel->fd(), channel->events(), index); 57 | if(index == kNew || index == kDeleted) 58 | { 59 | if(index == kNew) //这个channel从来都没有添加到poller中,那么就添加到poller的channel_map中 60 | { 61 | int fd = channel->fd(); 62 | channels_[fd] = channel; 63 | } 64 | channel->set_index(kAdded); //设置当前channel的状态 65 | update(EPOLL_CTL_ADD, channel); //注册新的fd到epfd中; 66 | } 67 | else //channel已经在poller上注册过了 68 | { 69 | int fd = channel->fd(); 70 | if(channel->isNoneEvent()) //这个channel已经对任何事件都不感兴趣了 71 | { 72 | update(EPOLL_CTL_DEL,channel); //从epfd中删除一个fd; 73 | channel->set_index(kDeleted); 74 | } 75 | else 76 | { 77 | update(EPOLL_CTL_MOD, channel); //这个channel还是对某些事件感兴趣的,修改已经注册的fd的监听事件; 78 | } 79 | } 80 | 81 | } 82 | void EpollPoller::removeChannel(Channel *channel) 83 | { 84 | /** 85 | * @brief 从epoll里面删掉这个channel,然后更改这个channel的状态,从Poller的channel_map删除这个channel 86 | * 87 | */ 88 | int fd = channel->fd(); 89 | channels_.erase(fd); 90 | 91 | LOG_INFO("func=%s => fd=%d\n", __FUNCTION__, fd); //这个__FUNCTION__是获取函数名 92 | 93 | int index = channel->index(); 94 | if (index == kAdded) 95 | update(EPOLL_CTL_DEL, channel); 96 | 97 | channel->set_index(kNew); 98 | 99 | } 100 | 101 | // 填写活跃的连接 102 | void EpollPoller::fillActiveChannels(int numEvents, ChannelList *activeChannels) const 103 | { 104 | for(int i = 0; i < numEvents; ++i) 105 | { 106 | Channel *channel = static_cast(events_[i].data.ptr); 107 | channel->set_revents(events_[i].events); 108 | activeChannels->push_back(channel); 109 | } 110 | } 111 | 112 | // 更新channel通道 113 | void EpollPoller::update(int operation, Channel *channel) 114 | { 115 | //这里主要就是根据operation: EPOLL_CTL_ADD MOD DEL来具体的调用epoll_ctl更改这个channel对应的fd在epoll上的监听事件 116 | epoll_event event; 117 | memset(&event, 0, sizeof(event)); 118 | event.events = channel->events(); //events()函数返回fd感兴趣的事件。 119 | event.data.ptr = channel; //这个epoll_event.data.ptr是给用户使用的, 120 | //epoll不关心里面的内容。用户通过这个可以附带一些自定义消息。这个 epoll_data 会随着 121 | // epoll_wait 返回的 epoll_event 一并返回。那附带的自定义消息,就是ptr指向这个自定义消息。 122 | int fd = channel->fd(); 123 | if (::epoll_ctl(epollfd_, operation, fd, &event) < 0) 124 | { 125 | if (operation == EPOLL_CTL_DEL) 126 | { 127 | LOG_ERROR("epoll_ctl del error:%d\n", errno); 128 | } 129 | else 130 | { 131 | LOG_FATAL("epoll_ctl add/mod error:%d\n", errno); 132 | } 133 | } 134 | 135 | } 136 | 137 | TimeStamp EpollPoller::poll(int timeoutMs, ChannelList *activeChannels) //这里不用写override,已经声明过了。 138 | { 139 | /** 140 | * @brief poll其实就是调用epoll或poll,然后向activeChannels填入有事件发生的channel! 141 | * 142 | */ 143 | LOG_DEBUG("func=%s => fd total count: %lu \n", __FUNCTION__, channels.size()); 144 | 145 | //这个写法很有意思!因为我们的events_是一个vector,支持动态扩容,而这里又要传入数组首地址,而events_又是一个对象, 146 | //events_.begin()是迭代器,*events_.begin()代表第一个元素(对象)&*events.begin()就代表第一个元素的地址。 147 | //这种技巧好好学一下! 148 | int numEvents = epoll_wait(epollfd_, &*events_.begin(), static_cast(events_.size()), timeoutMs); 149 | int saveErrno = errno; 150 | TimeStamp now(TimeStamp::now()); 151 | if(numEvents > 0) 152 | { 153 | LOG_DEBUG("%d events happened \n", numEvents); 154 | fillActiveChannels(numEvents, activeChannels); 155 | if(numEvents == events_.size()) //如果有活动的连接数量大于我们的events_能承载的数量,就要对events_扩容 156 | { 157 | events_.resize(events_.size() * 2);//反正我们用的是LT模式,那些没被添加进activeChannels的通道后面还会有机会被添加进来的。 158 | } 159 | } 160 | else if(numEvents == 0) 161 | { 162 | LOG_DEBUG("%s timeout! \n", __FUNCTION__); 163 | } 164 | else{ 165 | if(saveErrno != EINTR) 166 | { 167 | errno = saveErrno; 168 | LOG_ERROR("EPollPoller::poll() err!"); 169 | } 170 | } 171 | return now; 172 | 173 | } 174 | 175 | -------------------------------------------------------------------------------- /src/EventLoop.cc: -------------------------------------------------------------------------------- 1 | #include "EventLoop.h" 2 | #include 3 | #include "Poller.h" 4 | #include "Channel.h" 5 | #include 6 | #include 7 | #include "Logger.h" 8 | #include "CurrentThread.h" 9 | using namespace std; 10 | //__thread是一个thread_local的机制,代表这个变量是这个线程独有的全局变量,而不是所有线程共有 11 | __thread EventLoop *t_loopInThisThread = nullptr; //防止一个线程创建多个EventLoop 12 | //当一个eventloop被创建起来的时候,这个t_loopInThisThread就会指向这个Eventloop对象。 13 | //如果这个线程又想创建一个EventLoop对象的话这个t_loopInThisThread非空,就不会再创建了。 14 | 15 | const int kPollTimeMs = 10000; //定义默认的Pooler IO复用接口的超时时间 16 | 17 | //创建wakeupfd,用来通notify subreactor处理新来的channelx 18 | int createEventfd() 19 | { 20 | int evtfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 21 | if(evtfd < 0) 22 | { 23 | LOG_FATAL("eventfd error: %d \n", errno); 24 | } 25 | return evtfd; 26 | 27 | } 28 | 29 | EventLoop::EventLoop() : 30 | looping_(false), 31 | quit_(false), 32 | callingPendingFunctors_(false), 33 | threadId_(CurrentThread::tid()), //获取当前线程的tid 34 | poller_(Poller::newDefaultPoller(this)), //获取一个封装着控制epoll操作的对象 35 | wakeupFd_(createEventfd()), //生成一个eventfd,每个EventLoop对象,都会有自己的eventfd 36 | wakeupChannel_(new Channel(this, wakeupFd_)),//每个channel都要知道自己所属的eventloop, 37 | currentActiveChannel_(nullptr) 38 | { 39 | LOG_DEBUG("EventLoop created %p in thread %d \n", this, threadId_); 40 | if(t_loopInThisThread) //如果当前线程已经绑定了某个EventLoop对象了,那么该线程就无法创建新的EventLoop对象了 41 | LOG_FATAL("Another EventLoop %p exits in this thread %d \n", t_loopInThisThread, threadId_); 42 | else 43 | { 44 | t_loopInThisThread = this; 45 | } 46 | wakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this)); 47 | //用了functtion就要用bind,别用什么函数指针函数地址之类的。 48 | 49 | wakeupChannel_->enableReading(); //每一个EventLoop都将监听wakeupChannel的EpollIN读事件了。 50 | //mainReactor通过给wakeupFd_给sbureactor写东西。 51 | } 52 | 53 | EventLoop::~EventLoop() 54 | { 55 | wakeupChannel_->disableAll(); 56 | wakeupChannel_->remove(); 57 | close(wakeupFd_); //回收资源 58 | t_loopInThisThread = nullptr; 59 | } 60 | 61 | void EventLoop::handleRead() 62 | { 63 | uint64_t one = 1; 64 | ssize_t n = read(wakeupFd_, &one, sizeof(one));//mainReactor给subreactor发消息,subReactor通过wakeupFd_感知。 65 | if(n != sizeof(one)) 66 | LOG_ERROR("EventLoop::handleRead() reads %d bytes instead of 8", n); 67 | 68 | } 69 | void EventLoop::loop() 70 | { //EventLoop 所属线程执行 71 | looping_ = true; 72 | quit_ = false; 73 | LOG_INFO("EventLoop %p start looping \n", this); 74 | while(!quit_) 75 | { 76 | //在我们的Epoller里面poll方法里面,我们把EventLoop的ActiveChannels传给了poll方法 77 | //当poll方法调用完了epoll_wait()之后,把有事件发生的channel都装进了这个ActiveChannels数组里面 78 | activeChannels_.clear(); 79 | 80 | //监听两类fd,一个是client的fd,一个是wakeupfd,用于mainloop和subloop的通信 81 | pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);//此时activeChannels已经填好了事件发生的channel 82 | for(Channel *channel : activeChannels_) 83 | { 84 | //Poller监听哪些channel发生事件了,然后上报(通过activeChannels)给EventLoop,通知channel处理相应事件 85 | channel->HandlerEvent(pollReturnTime_); 86 | } 87 | /** 88 | IO线程 mainLoop负责接收新用户的连接, 89 | mainloop实现注册一个回调cb,这个回调由subloop来执行。 90 | wakeup subloop后执行下面的方法,执行mainloop的cb操作。 91 | */ 92 | doPendingFunctors(); //执行当前EventLoop事件循环需要处理的回调操作。 93 | 94 | } 95 | LOG_INFO("EventLoop %p stop looping. \n", t_loopInThisThread); 96 | } 97 | 98 | void EventLoop::quit() 99 | { 100 | //退出事件循环,有可能是loop在自己的线程中调用quit,loop()函数的while循环发现quit_=true就结束了循环 101 | //如果是在其他线程中调用的quit,在一个subloop线程中调用了mainloop的quit,那就调用wakeup唤醒mainLoop线程, 102 | /* 103 | mainLoop 104 | 105 | subloop1 subloop2 subloop3 106 | 这个quit可以是自己的loop调用,也可以是别的loop调用。比如mainloop调用subloop的quit 107 | */ 108 | quit_ = true; 109 | if(!isInLoopThread()) 110 | wakeup(); 111 | } 112 | 113 | void EventLoop::runInLoop(Functor cb) 114 | {//保证了调用这个cb一定是在其EventLoop线程中被调用。 115 | if(isInLoopThread()){ 116 | //如果当前调用runInLoop的线程正好是EventLoop的运行线程,则直接执行此函数 117 | cb(); 118 | } 119 | else{ 120 | //否则调用 queueInLoop 函数 121 | queueInLoop(cb); 122 | } 123 | } 124 | void EventLoop::queueInLoop(Functor cb) 125 | { 126 | 127 | { 128 | unique_lock lock(mutex_); 129 | pendingFunctors_.emplace_back(cb); 130 | } 131 | //唤醒相应的,需要执行上面回调操作的loop线程 132 | // || callingPendingFunctors_的意思是:当前loop正在执行回调,但是loop又有了新的回调, 133 | // 这个时候就要wakeup()loop所在线程,让它继续去执行它的回调。 134 | if(!isInLoopThread() || callingPendingFunctors_) 135 | { 136 | /*** 137 | 这里还需要结合下EventLoop循环的实现,其中doPendingFunctors()是每轮循环的最后一步处理。 138 | 如果调用queueInLoop和EventLoop在同一个线程,且callingPendingFunctors_为false时, 139 | 则说明:此时尚未执行到doPendingFunctors()。 那么此时即使不用wakeup,也可以在之后照旧 140 | 执行doPendingFunctors()了。这么做的好处非常明显,可以减少对eventfd的io读写。 141 | ***/ 142 | wakeup(); 143 | /*** 144 | 为什么要唤醒 EventLoop,我们首先调用了 pendingFunctors_.push_back(cb), 145 | 将该函数放在 pendingFunctors_中。EventLoop 的每一轮循环在最后会调用 146 | doPendingFunctors 依次执行这些函数。而 EventLoop 的唤醒是通过 epoll_wait 实现的, 147 | 如果此时该 EventLoop 中迟迟没有事件触发,那么 epoll_wait 一直就会阻塞。 148 | 这样会导致,pendingFunctors_中的任务迟迟不能被执行了。 149 | 所以必须要唤醒 EventLoop ,从而让pendingFunctors_中的任务尽快被执行。 150 | ***/ 151 | 152 | } 153 | } 154 | 155 | void EventLoop::wakeup() 156 | { 157 | /** 158 | * @brief 在 EventLoop 建立之后,就创建一个 eventfd,并将其可读事件注册到 EventLoop 中。 159 | wakeup() 的过程本质上是对这个 eventfd 进行写操作,以触发该 eventfd 的可读事件。 160 | 这样就起到了唤醒 EventLoop 的作用。 161 | * 162 | */ 163 | uint64_t one = 1; 164 | ssize_t n = write(wakeupFd_, &one, sizeof(one)); 165 | if(n != sizeof(n)) 166 | { 167 | LOG_ERROR("EventLoop::wakeup() writes %lu bytes instead of 8 \n", n); 168 | } 169 | } 170 | void EventLoop::updateChannel(Channel *channel) 171 | { 172 | poller_->updateChannel(channel); // 173 | } 174 | void EventLoop::removeChannel(Channel *channel) 175 | { 176 | poller_->removeChannel(channel); 177 | } 178 | bool EventLoop::hasChannel(Channel *channel) 179 | { 180 | poller_->hasChannel(channel); 181 | } 182 | void EventLoop::doPendingFunctors() 183 | { 184 | /*** 185 | 这里面又有精华!! 186 | 我们在queueInLoop里面往pendingFunctors_数组里面插入新回调, 187 | 这里定义了一个局部的functors数组,每次执行doPendingFunctors的时候都和pendingFunctors_ 188 | 交换,相当于把pendingFunctors_的对象全部导入到functors数组里面,让那后把pendingFunctors 189 | 置为空,这样的好处是避免频繁的锁,因为如果你不用这种机制的话,生产者queueInLoop函数插入新回调, 190 | 消费者doPendingFunctors消费回调,他们共用一个pendingFunctors_,这样生产者插入和消费者消费 191 | 就会不停的互相触发锁机制。 192 | ***/ 193 | std::vector functors; 194 | callingPendingFunctors_ = true; 195 | { 196 | unique_lock lock(mutex_); 197 | functors.swap(pendingFunctors_); //这里的swap其实只是交换的vector对象指向的内存空间的指针而已。 198 | } 199 | for(const Functor &functor:functors) 200 | { 201 | functor(); 202 | } 203 | callingPendingFunctors_ = false; 204 | } -------------------------------------------------------------------------------- /src/EventLoopThread.cc: -------------------------------------------------------------------------------- 1 | #include "EventLoopThread.h" 2 | #include "EventLoop.h" 3 | #include 4 | using namespace std; 5 | EventLoopThread::EventLoopThread(const ThreadInitCallback &cb, 6 | const string &name): 7 | loop_(nullptr), 8 | exiting_(false), 9 | thread_(bind(&EventLoopThread::threadFunc, this), name), 10 | mutex_(), 11 | cond_(), //注意这里初始化的时候也不用放mutex,人家也没有这样的初始化构造函数 12 | callback_(cb) 13 | {} 14 | EventLoopThread::~EventLoopThread() 15 | { 16 | exiting_ = true; 17 | if(loop_ != nullptr) 18 | { 19 | loop_->quit(); 20 | thread_.join(); //等待这个子线程结束。 21 | } 22 | } 23 | 24 | EventLoop* EventLoopThread::startLoop() 25 | { 26 | thread_.start(); //启动底层新线程 27 | //必须要等待threadFunc的loop_初始化好了下面才能继续执行 28 | EventLoop* loop = nullptr; 29 | { 30 | unique_lock lock(mutex_); 31 | while(loop_ == nullptr) 32 | {//https://segmentfault.com/a/1190000006679917 为什么把_loop放在nullptr里面,因为 33 | //要防止假唤醒,就是明明自己被唤醒了,刚想要得到资源的时候却被别人剥夺了。 34 | cond_.wait(lock); 35 | } 36 | loop = loop_; //这里相当于做了一个线程间的通信操作,另外一个线程初始化的loop_,在这个线程里面获取 37 | } 38 | return loop; 39 | } 40 | 41 | void EventLoopThread::threadFunc() 42 | { 43 | //该方法是在单独的新线程里面运行的。 44 | EventLoop loop; //创建一个独立的EventLoop,和上面的线程是一一对应的, 45 | //在面试的时候要是能把one loop per thread给说到具体的方法上,就能认同你了。 46 | if(callback_) 47 | { 48 | callback_(&loop); 49 | } 50 | { 51 | unique_lock lock(mutex_); 52 | loop_ = &loop; //EventLoopThread里面绑定的loop对象就是在这个线程里面创建的 53 | cond_.notify_one(); 54 | } 55 | loop.loop(); //EventLoop loop => Poller.poll开始监听远端连接或者已连接的fd的事件 56 | unique_lock lock(mutex_); 57 | loop_ = nullptr; //如果走到这里说明服务器程序要关闭了,不进行事件循环了。 58 | //为什么这里要加锁,大概是悲观锁的思想吧,这个loop_毕竟会被多个程序使用 59 | } 60 | 61 | /* 62 | EventLoop *loop_; 63 | bool exiting_; 64 | Thread thread_; 65 | std::mutex mutex_; 66 | std::condition_variable cond_; 67 | ThreadInitCallback callback_; 68 | */ 69 | 70 | -------------------------------------------------------------------------------- /src/EventLoopThreadPool.cc: -------------------------------------------------------------------------------- 1 | #include "EventLoopThreadPool.h" 2 | #include "EventLoopThread.h" 3 | #include 4 | using namespace std; 5 | EventLoopThreadPool::EventLoopThreadPool(EventLoop *baseLoop, const std::string &nameArg): 6 | baseLoop_(baseLoop), 7 | name_(nameArg), 8 | started_(false), 9 | numThreads_(0), 10 | next_(0) //轮循分配channel给EventLoopThreadPool中的EvenLoopThread的下标 11 | { 12 | 13 | } 14 | EventLoopThreadPool::~EventLoopThreadPool() 15 | { 16 | //我们不需要去析构EventLoop对象,因为EventLoopThread对象,因为EventLoopThread对象是在栈上创建的,结束后会自动删除 17 | } 18 | void EventLoopThreadPool::start(const ThreadInitCallback &cb) 19 | { 20 | started_ = true; 21 | for(int i = 0; i < numThreads_; i++) 22 | { 23 | char buf[name_.size() + 32]; 24 | snprintf(buf, sizeof(buf), "%s%d",name_.c_str(),i); 25 | EventLoopThread *t = new EventLoopThread(cb, buf); 26 | threads_.push_back(std::unique_ptr(t)); 27 | //其实这个EventLoopThread只提供了一些控制EventLoop线程的方法,而EventLoop对象的创建 28 | //则是在EventLoopThread::threadFunc()函数内创建的,当EventLoopThread析构了, 29 | //局部对象EvetLoop对象就会从栈中删除。 30 | loops_.push_back(t->startLoop()); 31 | } 32 | if(numThreads_ == 0) 33 | { 34 | //整个服务端只有一个线程运行着baseLoop_; 35 | cb(baseLoop_); 36 | } 37 | } 38 | 39 | //如果是工作在多线程中,baseLiio_默认以轮循的方式分配channel给subloop 40 | //当然还有一个哈希的定制方式分配channel,这里就暂时不写他了 41 | EventLoop* EventLoopThreadPool::getNextLoop() 42 | { 43 | /** 44 | * 这个很好理解,就是普通的轮循而已 45 | */ 46 | EventLoop *loop = baseLoop_; 47 | if(!loops_.empty()) 48 | { 49 | loop = loops_[next_]; 50 | ++next_; 51 | if(next_ >= loops_.size()) 52 | next_ = 0; 53 | } 54 | return loop; 55 | 56 | } 57 | 58 | vector EventLoopThreadPool::getAllGroups() 59 | { 60 | if(loops_.empty()) 61 | return std::vector {baseLoop_}; 62 | else 63 | return loops_; 64 | } -------------------------------------------------------------------------------- /src/InetAddress.cc: -------------------------------------------------------------------------------- 1 | #include "InetAddress.h" 2 | #include 3 | #include 4 | using namespace std; 5 | /* 6 | class InetAddress 7 | { 8 | public: 9 | explicit InetAddress(uint16_t port = 0, std::string ip = "127.0.0.1"); 10 | explicit InetAddress(const sockaddr_in &addr); 11 | std::string toIp() const; 12 | std::string toIpPort() const; 13 | uint16_t toPort() const; 14 | 15 | const sockaddr_in *getSockAddr() const; 16 | void setSockAddr(const sockaddr_in &addr); 17 | private: 18 | sockaddr_in addr_; 19 | }; 20 | */ 21 | 22 | InetAddress::InetAddress(const sockaddr_in &addr):addr_(addr) {} 23 | 24 | InetAddress::InetAddress(uint16_t port, string ip){ 25 | bzero(&addr_, sizeof(addr_)); //man bzero 头文件在strings.h 26 | addr_.sin_family = AF_INET; 27 | addr_.sin_port = htons(port); //从主机字节顺序转变成网络字节顺序 28 | addr_.sin_addr.s_addr = inet_addr(ip.c_str()); //inet_addr()用来将参数cp 所指的网络地址字符串转换成网络所使用的二进制数字 29 | } 30 | 31 | string InetAddress::toIp() const 32 | { 33 | char buf[64] = {0}; 34 | ::inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof(buf)); //将数值格式转化为点分十进制的字符串ip地址格式 35 | return buf; 36 | } 37 | string InetAddress::toIpPort() const 38 | { 39 | char buf[64] = {0}; 40 | ::inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof(buf)); 41 | size_t end = strlen(buf); //这个函数在里面,是个c函数而不在里面 42 | uint16_t port = ntohs(addr_.sin_port); 43 | sprintf(buf+end, ":%u", port); 44 | return buf; 45 | } 46 | uint16_t InetAddress::toPort() const 47 | { 48 | // 49 | return ntohs(addr_.sin_port); //将一个无符号短整形数从网络字节顺序转换为主机字节顺序 50 | } -------------------------------------------------------------------------------- /src/Logger.cc: -------------------------------------------------------------------------------- 1 | #include "Logger.h" 2 | #include 3 | #include "Timestamp.h" 4 | using namespace std; 5 | 6 | 7 | Logger& Logger::Instance() 8 | { 9 | static Logger logger; 10 | return logger; 11 | } 12 | void Logger::setLogLevel(int level) 13 | { 14 | level_ = level; 15 | } 16 | void Logger::log(string msg) 17 | { 18 | // 写日志 [级别信息] time : msg 19 | switch(level_) 20 | { 21 | case INFO: 22 | { 23 | cout << "[INFO]"; 24 | break; 25 | } 26 | case ERROR: 27 | { 28 | cout << "[ERROR]"; 29 | break; 30 | } 31 | case FATAL: 32 | { 33 | cout << "[FATAL]"; 34 | break; 35 | } 36 | case DEBUG: 37 | { 38 | cout << "[DEBUG]"; 39 | break; 40 | } 41 | default: 42 | break; 43 | } 44 | //打印时间和消息 45 | cout << TimeStamp::now().toString() << " : " << msg << endl; 46 | } 47 | -------------------------------------------------------------------------------- /src/Poller.cc: -------------------------------------------------------------------------------- 1 | #include "Poller.h" 2 | 3 | Poller::Poller(EventLoop *loop) : ownerLoop_(loop) {} 4 | 5 | bool Poller::hasChannel(Channel *channel) const 6 | { 7 | //map: (sockfd, channel*) 8 | auto it = channels_.find(channel->fd()); 9 | return it != channels_.end() && it->second == channel; 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Socket.cc: -------------------------------------------------------------------------------- 1 | #include "Socket.h" 2 | #include "Logger.h" 3 | #include "InetAddress.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | using namespace std; 12 | 13 | Socket::~Socket() 14 | { 15 | close(sockfd_); 16 | } 17 | 18 | void Socket::bindAddress(const InetAddress &localaddr) 19 | { 20 | if (0 != bind(sockfd_, (sockaddr*)localaddr.getSockAddr(), sizeof(sockaddr_in))) 21 | { 22 | LOG_FATAL("bind sockfd:%d fail \n", sockfd_); 23 | } 24 | } 25 | void Socket::listen() 26 | { 27 | if (0 != ::listen(sockfd_, 1024)) //似乎最大就是1024 28 | { 29 | LOG_FATAL("listen sockfd:%d fail \n", sockfd_); 30 | } 31 | } 32 | int Socket::accept(InetAddress *peeraddr) 33 | { //传进来的是个空的InetAddress对象,当accept成功了就给这个InetAddress对象设成功连接的sockaddr_in 34 | /** 35 | * 1. accept函数的参数不合法 36 | * 2. 对返回的connfd没有设置非阻塞 37 | * Reactor模型 one loop per thread 38 | * poller + non-blocking IO 39 | */ 40 | sockaddr_in addr; 41 | socklen_t len = sizeof(addr); 42 | bzero(&addr, sizeof(addr)); 43 | int connfd = ::accept4(sockfd_, (sockaddr*)&addr, &len, SOCK_NONBLOCK | SOCK_CLOEXEC); 44 | //当试图对该文件描述符进行读写时,如果当时没有东西可读,或者暂时不可写,程序就进入等待状态, 45 | //直到有东西可读或者可写为止。而对于非阻塞状态,如果没有东西可读,或者不可写,读写函数马上返回,而不会等待。 46 | if (connfd >= 0) 47 | { 48 | peeraddr->setSockAddr(addr); //给InetAddress对象设置sockaddr_in 49 | } 50 | return connfd; 51 | } 52 | void Socket::shutdownWrite() 53 | { 54 | if (shutdown(sockfd_, SHUT_WR) < 0) 55 | { 56 | LOG_ERROR("shutdownWrite error"); 57 | } 58 | } 59 | void Socket::setTcpNoDelay(bool on) //不启用naggle算法,增大对小数据包的支持 60 | { 61 | int optval = on ? 1 : 0; 62 | ::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)); 63 | } 64 | void Socket::setReuseAddr(bool on) 65 | { 66 | int optval = on ? 1 : 0; 67 | ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); 68 | //SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。 69 | } 70 | void Socket::setReusePort(bool on) 71 | { 72 | int optval = on ? 1 : 0; 73 | ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); 74 | // 75 | } 76 | void Socket::setKeepAlive(bool on) 77 | { 78 | int optval = on ? 1 : 0; 79 | ::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)); 80 | //TCP保活机制 81 | } 82 | -------------------------------------------------------------------------------- /src/TcpConnection.cc: -------------------------------------------------------------------------------- 1 | #include "TcpConnection.h" 2 | #include "Logger.h" 3 | #include "Socket.h" 4 | #include "Channel.h" 5 | #include "EventLoop.h" 6 | #include 7 | using namespace std; 8 | using namespace std::placeholders; 9 | static EventLoop* CheckLoopNotNull(EventLoop* loop) //这里定义为static怕TcpConnection和TcpServer的这个函数产生冲突 10 | { 11 | if(loop == nullptr) 12 | { 13 | LOG_FATAL("%s:%s:%d mainLoop is null \b", __FILE__, __FUNCTION__, __LINE__); 14 | } 15 | return loop; 16 | } 17 | 18 | TcpConnection::TcpConnection(EventLoop* loop, const std::string &nameArg, int sockfd, const InetAddress &localAddr, const InetAddress &peerAddr) 19 | : loop_(CheckLoopNotNull(loop)) 20 | , name_(nameArg) 21 | , state_(kConnected) 22 | , reading_(true) 23 | , socket_(new Socket(sockfd)) 24 | , channel_(new Channel(loop, sockfd)) 25 | , localAddr_(localAddr) 26 | , peerAddr_(peerAddr) 27 | , highWaterMark_(64*1024*1024) //64M 28 | { 29 | //给channel设置相应的回调函数,poller给channel通知感兴趣的事件发生了,channel会回调相应的操作 30 | channel_->setReadCallback(bind(&TcpConnection::handleRead, this, _1)); 31 | channel_->setWriteCallback(bind(&TcpConnection::handleWrite, this)); 32 | channel_->setCloseCallback(bind(&TcpConnection::handleClose, this)); 33 | channel_->setErrorCallback(bind(&TcpConnection::handleError, this)); 34 | LOG_INFO("TcpConnection::creator[%s] at fd=%d\n", name_.c_str(), sockfd); 35 | socket_->setKeepAlive(true); 36 | } 37 | 38 | TcpConnection::~TcpConnection() 39 | { 40 | LOG_INFO("TcpConnection::deletor[%s] at fd=%d state=%d \n", 41 | name_.c_str(), channel_->fd(), (int)state_); 42 | } 43 | 44 | void TcpConnection::send(const string &buf)//发送数据 注意留意一下这个强制转换 45 | { 46 | if(state_ == kConnected) 47 | { 48 | if(loop_->isInLoopThread()) 49 | { //其实在我们这个通信框架里面,因为TcpConnection的Channel肯定是注册到 50 | //一个EventLoop里面,这个send肯定是在对应的EventLoop线程里面执行的 51 | //不过有一些应用场景可能会把connection记录下来,在其他线程调用connection的send这也是有可能的 52 | sendInLoop(buf.c_str(), buf.size()); 53 | } 54 | else{ 55 | loop_->runInLoop(std::bind(&TcpConnection::sendInLoop, 56 | this, 57 | buf.c_str(), 58 | buf.size())); 59 | } 60 | } 61 | } 62 | void TcpConnection::sendInLoop(const void* data, size_t len) 63 | { //将Buffer的数据 64 | //发送数据 应用写的快,而内核发送数据慢,需要把待发送数据写入缓冲区,而且设置了水位回调 65 | ssize_t nwrote = 0; 66 | size_t remaining = len; //还没发送的数据的长度 67 | bool faultError = false; //记录是否产生错误 68 | if(state_ == kDisconnected) 69 | { 70 | //之前调用过该connection的shutdown,不能再进行发送了 71 | LOG_ERROR("disconnected, give up writing"); 72 | return ; 73 | } 74 | if(!channel_->isWriting() && outputBuffer_.readableBytes() == 0) 75 | { 76 | //channel第一次开始写数据,而且用户空间的发送缓冲区中还没有待发送数据 77 | nwrote = ::write(channel_->fd(), data, len); 78 | if(nwrote >= 0) 79 | { 80 | remaining = len - nwrote; 81 | if(remaining == 0 && writeCompleteCallback_) 82 | {//既然一次性发送完了就不用再给channel设置epollout事件了,即不用设置enableWriting 83 | //这样epoll_wait就不用监听可写事件并且执行handleWrite了。这算是提高效率吧 84 | loop_->queueInLoop(bind(writeCompleteCallback_, shared_from_this())); 85 | //发送完数据了就调用发送完后的处理函数吧 86 | } 87 | } 88 | else 89 | { //一次性没法把data全部拷贝到socket发送缓冲区中 90 | nwrote = 0; // 91 | if(errno != EWOULDBLOCK) 92 | {//如果是非阻塞模式,socket发送缓冲区满了就会立即返回并且设置EWOULDBLOCK 93 | LOG_ERROR("TcpConnection::sendInLoop"); 94 | if(errno == EPIPE || errno == ECONNRESET) //接收到对端的socket重置 95 | faultError = true; 96 | } 97 | } 98 | } 99 | if(!faultError && remaining > 0) 100 | { 101 | //说明刚才的write没有把数据全部拷贝到socket发送缓冲区中,剩余的数据需要保存到用户的outputBuffer缓冲区当中。 102 | //然后给channel注册epollout事件,poller发现tcp的发送缓冲区有空间,会通知相应的sock channel 103 | //调用handleWrite回调方法,把发送缓冲区中的数据全部发送完成。 104 | size_t oldLen = outputBuffer_.readableBytes();//目前发送缓冲区Buffer剩余的待发送数据(待拷贝到socket缓冲区)的长度 105 | if(oldLen +remaining >= highWaterMark_ 106 | && oldLen < highWaterMark_ //??????????? 107 | && highWaterMarkCallback_) 108 | { 109 | loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining)); 110 | } 111 | outputBuffer_.append((char*)data + nwrote, remaining); 112 | if(!channel_->isWriting()) 113 | channel_->enableWriting();//这里一定要注册channel的写事件,否则poller不会给channel通知pollout 114 | } 115 | } 116 | void TcpConnection::shutdown() 117 | { 118 | if(state_ == kConnected) 119 | { 120 | setState(kDisconnecting); //这里设置成kDisconnecting是怕你缓冲区还有数据没发完 121 | //在handleWrite函数中,如果发送完数据会检查state_是不是KDisconnecting,如果是就设为KDisconnected 122 | // 123 | loop_->runInLoop(bind(&TcpConnection::shutdownInLoop, this)); 124 | } 125 | 126 | 127 | } 128 | 129 | void TcpConnection::shutdownInLoop() 130 | { 131 | if(!channel_->isWriting()) 132 | { 133 | //说明当前outputBuffer中的数据全部发送完成 134 | socket_->shutdownWrite(); //关闭写端 触发Channel的EPOLLHUP 135 | 136 | } 137 | } 138 | 139 | 140 | // 连接建立 141 | void TcpConnection::connectEstablished() 142 | { 143 | setState(kConnected); 144 | channel_->tie(shared_from_this()); 145 | //tie(const std::shared_ptr &obj); 146 | //shared_from_this()返回的是shared_ptr,这能被tie函数接受? 147 | //这个shared_ptr实际上底层就是一个void*指针,而shared_ptr 148 | //实际上是一个TcpConnection*指针 149 | //Channel类里面有一个weak_ptr会指向这个传进来的shared_ptr 150 | //如果这个TcpConnection已经被释放了,那么Channel类中的weak_ptr就没办法在 151 | //Channel::handleEvent 就没法提升weak_ptr。 152 | /** 153 | * @brief 我倒是觉得这个思想很不错,当你的TcpConnection对象以shared_ptr的形式 154 | * 进入到Channel中被绑定,然后Channel类通过调用handleEvent把Channel类中的 155 | * weak_ptr提升为shared_ptr共同管理这个TcpConnection对象,这样的话,即使 156 | * 外面的这个TcpConnection智能指针被释放析勾删除,Channel类里面还有一个智能指针 157 | * 指向这个对象,这个TcpConnection对象也不会被释放。因为引用计数没有变为0. 158 | * 这个思想超级好,防止你里面干得好好的,外边却突然给你釜底抽薪 159 | * 160 | */ 161 | channel_->enableReading(); //向poller注册channel的epollin事件 162 | //新连接建立,执行回调 163 | connectionCallback_(shared_from_this()); 164 | 165 | } 166 | 167 | 168 | 169 | void TcpConnection::handleRead(TimeStamp receiveTime) 170 | { 171 | int savedErrno = 0; 172 | ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);//这里的channel的fd也一定仅有socket fd 173 | if(n > 0) //从fd读到了数据,并且放在了inputBuffer_上 174 | { 175 | //已建立连接的用户,有可读事件发生了,调用用户传入的回调操作onMessage 176 | //这个shared_from_this()就是TcpConnection对象的智能指针 177 | messageCallback_(shared_from_this(), &inputBuffer_, receiveTime); 178 | } 179 | else if(n == 0) 180 | handleClose(); 181 | else 182 | { 183 | errno = savedErrno; 184 | LOG_ERROR("TcpConnection::handleRead"); 185 | handleError(); 186 | } 187 | } 188 | 189 | void TcpConnection::handleWrite() 190 | { 191 | if(channel_->isWriting()) //当前感兴趣的事件是否包含可写事件??? 192 | { 193 | int savedErrno = 0; 194 | ssize_t n = outputBuffer_.writeFd(channel_->fd(), &savedErrno);//通过fd发送数据 195 | if(n > 0) //n > 0说明向Buffer写入成功,Buffer是要发出去给socket的数据 196 | { 197 | outputBuffer_.retrieve(n);//如果成功写入了,就要一定一下readerIndex;我觉得把这句话封装进writeFd更好吧 198 | if(outputBuffer_.readableBytes() == 0) 199 | { //如果说Buffer里面已经没有数据了,全都拷贝到发送缓冲区了 200 | channel_->disableWriting(); //那么就关闭这个channel的可写事件, 201 | //我推测,当Buffer又有数据了,就启动可写事件监听,这样做有一个好处,就是 202 | //尽可能的将epoll_wait留给可读关闭等事件的监听,不会频繁触发可写事件,提高效率! 203 | if(writeCompleteCallback_) 204 | { 205 | loop_->queueInLoop( 206 | std::bind(writeCompleteCallback_, shared_from_this()) 207 | );//queueInLoop,唤醒这个loop对应的thread线程来执行回调,其实我觉得这里也可以是runInLoop,的确也可以是 208 | } 209 | if(state_ == kDisconnecting) //正在关闭中 210 | { 211 | shutdownInLoop(); 212 | } 213 | } 214 | } 215 | else{ 216 | LOG_ERROR("TcpConnection::handleWrite"); 217 | } 218 | } 219 | else{ 220 | LOG_ERROR("TcpConnection fd=%d is down, no more writing \n", channel_->fd()); 221 | } 222 | } 223 | 224 | void TcpConnection::handleClose() 225 | { 226 | LOG_INFO("fd=%d state=%d \n",channel_->fd(), static_cast(state_)); 227 | setState(kDisconnected); 228 | channel_->disableAll(); 229 | TcpConnectionPtr connPtr(shared_from_this()); 230 | 231 | //其实我觉得这里应该加入判断条件,万一没设置执行关闭连接的回调函数怎么办 232 | connectionCallback_(connPtr); //执行连接关闭的回调 ?????????????????????? 233 | closeCallback_(connPtr); //关闭连接 234 | } 235 | 236 | void TcpConnection::handleError() 237 | { 238 | int optval; 239 | socklen_t optlen = sizeof(optval); 240 | int err = 0; 241 | 242 | //《Linux高性能服务器编程》page88,获取并清除socket错误状态,getsockopt成功则返回0,失败则返回-1 243 | if(::getsockopt(channel_->fd(), SOL_SOCKET, SO_ERROR, &optval, &optlen)<0) 244 | err = errno; 245 | else 246 | err = optval; 247 | LOG_ERROR("TcpConnection::handleError name:%s - SO_ERROR:%d \n", name_.c_str(), err); 248 | 249 | } 250 | 251 | // 连接销毁 252 | void TcpConnection::connectDestroyed() 253 | { 254 | if(state_ == kConnected) 255 | { 256 | setState(kDisconnected); 257 | channel_->disableAll(); 258 | connectionCallback_(shared_from_this()); 259 | } 260 | channel_->remove();//把channel从poller中删除掉。 261 | } 262 | 263 | -------------------------------------------------------------------------------- /src/TcpServer.cc: -------------------------------------------------------------------------------- 1 | #include "TcpServer.h" 2 | #include "Logger.h" 3 | #include 4 | #include "TcpConnection.h" 5 | using namespace std; 6 | using namespace std::placeholders; 7 | static EventLoop* CheckLoopNotNull(EventLoop* loop) //这里定义为static怕TcpConnection和TcpServer的这个函数产生冲突 8 | { 9 | if(loop == nullptr) 10 | { 11 | LOG_FATAL("%s:%s:%d mainLoop is null \b", __FILE__, __FUNCTION__, __LINE__); 12 | } 13 | return loop; 14 | } 15 | 16 | TcpServer::TcpServer(EventLoop* loop, 17 | const InetAddress &listenAddr, 18 | const string &nameArg, 19 | Option option) //默认不重用端口 20 | : loop_(loop) 21 | , ipPort_(listenAddr.toIpPort()) 22 | , name_(nameArg) 23 | , acceptor_(new Acceptor(loop, listenAddr , option == kReusePort)) 24 | //把baseLoop和listenAddr(服务端监听的ip和port)交给acceptor,acceptor生成一个acceptrFd_ 25 | //并且打包成一个Channel注册到这个loop(也就是baseLoop上)进行循环监听新连接事件(读acceptFd_事件) 26 | , threadPool_(new EventLoopThreadPool(loop, name_)) 27 | , connectionCallback_() 28 | , messageCallback_() 29 | , nextConnId_(1) 30 | , started_(0) 31 | { 32 | acceptor_->setNewConnectionCallback(bind(&TcpServer::newConnection, this, 33 | _1, _2)); //这两个占位符是connfd和ip地址端口号 34 | } 35 | TcpServer::~TcpServer() 36 | { 37 | //connections类型为std::unordered_map; 38 | for(auto &item : connections_) 39 | { 40 | /*** 41 | 注意这里的写法,先让conn持有这个item.second这个智能指针 42 | 然后把item.second 这个智能指针释放了,但是此时item.second所指向的资源 43 | 其实还没有释放,因为这个资源现在还被conn管理,不过当这个for循环结束后,这个 44 | conn离开作用域,这个智能指针也会被释放。 45 | 46 | 为什么要这么麻烦,我个人推测,不过逻辑还没闭环: 因为我们后面还要调用TcpConnection的connectDestroyed方法 47 | 而且这个方法的调用还是在另一个线程中执行的,还是担心悬空指针的问题。 48 | ***/ 49 | TcpConnectionPtr conn(item.second); 50 | item.second.reset(); 51 | conn->getLoop()->runInLoop(bind(&TcpConnection::connectDestroyed, conn)); 52 | } 53 | } 54 | 55 | void TcpServer::setThreadNum(int numThreads) //设置底层subloop个数 56 | { 57 | threadPool_->setThreadNum(numThreads); 58 | } 59 | 60 | void TcpServer::start() //开启服务器监听 61 | { 62 | if(started_++ == 0) //防止一个TcpServer对象被start多次 63 | { 64 | threadPool_->start(threadInitCallback_); //启动底层的loop线程池 65 | loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get())); 66 | //让这个EventLoop,也就是mainloop来执行Acceptor的listen函数,开启服务端监听 67 | 68 | } 69 | } 70 | 71 | void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr) 72 | { 73 | //根据轮询算法选择一个subloop,唤醒subloop,把当前的connfd封装成channel分发给subloop 74 | //这个newConnection在baseLoop里面运行(mainLoop mainReactor都是以一个东西),baseLoop 75 | //选择一个ioLoop(subreactor subloop都是一个东西),把这个socketfd打包成channel交给这个ioLoop 76 | //每一个loop在执行属于自己loop的io操作的时候都必须在自己的线程中执行,所以eventloop类提供了 77 | //runInLoop,如果调用runInLoop的线程就是当前的EventLoop类绑定的线程,那就直接执行,否则就 78 | //调用queueInLoop,先唤醒那个线程再让那个线程去执行。 79 | 80 | //有一个新的客户端连接,acceptor会执行这个回调操作 81 | 82 | EventLoop *ioLoop = threadPool_->getNextLoop(); //轮循算法选择一个subLoop来管理新连接的channel 83 | char buf[64] = {0}; 84 | snprintf(buf, sizeof(buf), "-%s#%d",ipPort_.c_str(), nextConnId_); //表示一个连接的名称 85 | ++nextConnId_; //没必要把这个变量变为原子变量,因为这个nextConnId也只会在baseloop里面处理 86 | string connName = name_ + buf; 87 | LOG_INFO("TcpServer::newConnection [%s] - new connection [%s] from %s \n", 88 | name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str()); 89 | //通过sockfd获取其绑定的本机的ip地址和端口信息 90 | sockaddr_in local; 91 | bzero(&local, sizeof(local)); 92 | socklen_t addrlen = sizeof(local); 93 | if(getsockname(sockfd, (sockaddr*)&local, &addrlen) < 0) 94 | {//getsockname函数用于获取与某个套接字关联的本地协议地址,然后放到local里面 95 | LOG_ERROR("sockets::getLocalAddr"); 96 | } 97 | 98 | InetAddress localAddr(local); 99 | //根据连接成功的sockfd创建TcpConnection连接对象 100 | TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr)); 101 | connections_[connName] = conn; 102 | 103 | //下面的回调都是用户设置给TcpServer的 104 | conn->setConnectionCallback(connectionCallback_); 105 | conn->setMessageCallback(messageCallback_); 106 | conn->setWriteCompleteCallback(writeCompleteCallback_); 107 | conn->setCloseCallback(bind(&TcpServer::removeConnection, this, _1)); 108 | // 109 | ioLoop->runInLoop(bind(&TcpConnection::connectEstablished, conn)); 110 | 111 | } 112 | 113 | void TcpServer::removeConnection(const TcpConnectionPtr &conn) 114 | { 115 | //当TcpConnection的CloseCallback调用的回调函数 116 | loop_->runInLoop( 117 | bind(&TcpServer::removeConnectionInLoop, this, conn) 118 | ); 119 | } 120 | 121 | void TcpServer::removeConnectionInLoop(const TcpConnectionPtr &conn) 122 | { 123 | LOG_INFO("TcpServer::removeConnectionInLoop [%s] - connection %s\n", 124 | name_.c_str(), conn->name().c_str()); 125 | connections_.erase(conn->name()); 126 | EventLoop *ioLoop = conn->getLoop(); 127 | ioLoop->queueInLoop(bind(&TcpConnection::connectDestroyed, conn)); 128 | //拐来拐去最后又拐到connectDestroyed 129 | } 130 | 131 | 132 | -------------------------------------------------------------------------------- /src/Thread.cc: -------------------------------------------------------------------------------- 1 | #include "Thread.h" 2 | #include "CurrentThread.h" 3 | #include 4 | using namespace std; 5 | 6 | atomic numCreated_(0); 7 | 8 | Thread::Thread(ThreadFunc func, const std::string &name): 9 | started_(false), 10 | joined_(false), //是join线程还是detach线程。 11 | tid_(0), 12 | func_(std::move(func)), 13 | name_(name) 14 | { 15 | setDefaultName(); 16 | } 17 | Thread::~Thread(){ 18 | if(started_ && !joined_){ 19 | thread_->detach(); //分离线程 20 | } 21 | } 22 | void Thread::start() 23 | { 24 | started_ = true; 25 | sem_t sem; 26 | sem_init(&sem, false, 0); 27 | thread_ = shared_ptr(new thread([&] () { 28 | //获取线程的tid值 29 | tid_ = CurrentThread::tid(); //子线程获取当前所在线程的tid,注意执行到这一行已经是在新创建的子线程里面了。 30 | sem_post(&sem); 31 | func_(); //开启一个新线程,专门执行一个线程函数。 32 | })); 33 | //这里必须等待上面的新线程获取了它的tid值才能继续执行。 34 | sem_wait(&sem); 35 | //当这个start函数结束后,就可以放心的访问这个新线程了,因为他的tid已经获取了。 36 | } 37 | void Thread::join() 38 | { //待解释。。。。。。。。 39 | joined_ = true; 40 | thread_->join(); 41 | } 42 | 43 | 44 | void Thread::setDefaultName() 45 | { 46 | int num = ++numCreated_; 47 | if(name_.empty()) 48 | { 49 | char buf[32] = {0}; 50 | snprintf(buf, sizeof(buf), "Thread%d", num); ///给这个线程一个名字 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Timestamp.cc: -------------------------------------------------------------------------------- 1 | #include "Timestamp.h" 2 | #include 3 | using namespace std; 4 | 5 | TimeStamp::TimeStamp(int64_t microSecondSinceEpoch) : microSecondSinceEpoch_(microSecondSinceEpoch){} 6 | 7 | TimeStamp TimeStamp::now() //返回当前时间 这是一个static函数 8 | { 9 | return TimeStamp(time(NULL)); 10 | } 11 | 12 | string TimeStamp::toString() const //把int64_t的时间转换为字符串 13 | { 14 | char buf[128] = {0}; 15 | tm *tm_time = localtime(µSecondSinceEpoch_); //localtime函数返回的结构体类型tm如下所示。 16 | snprintf(buf, 128, "%4d/%02d/%02d %02d:%02d:%02d", 17 | tm_time->tm_year+1900, 18 | tm_time->tm_mon+1, 19 | tm_time->tm_mday, 20 | tm_time->tm_hour, 21 | tm_time->tm_min, 22 | tm_time->tm_sec); 23 | return buf; 24 | } 25 | /** 26 | struct tm 27 | { 28 | int tm_sec; // Seconds. [0-60] (1 leap second) 29 | int tm_min; // Minutes. [0-59] 30 | int tm_hour; // Hours. [0-23] 31 | int tm_mday; // Day. [1-31] 32 | int tm_mon; // Month. [0-11] 33 | int tm_year; // Year - 1900. 34 | int tm_wday; // Day of week. [0-6] 35 | int tm_yday; // Days in year.[0-365] 36 | int tm_isdst; // DST. [-1/0/1] 37 | 38 | # ifdef __USE_MISC 39 | long int tm_gmtoff; // Seconds east of UTC. 40 | const char *tm_zone; // Timezone abbreviation. 41 | # else 42 | long int __tm_gmtoff; // Seconds east of UTC. 43 | const char *__tm_zone; // Timezone abbreviation. 44 | # endif 45 | }; 46 | */ 47 | 48 | /* 49 | #include 50 | int main() 51 | { 52 | cout << TimeStamp::now().toString() << endl; 53 | return 0; 54 | }*/ --------------------------------------------------------------------------------