生疏

  1. 在进行日志库的开发时,涉及到的几个重要的宏:
    1. __FILE__:文件名称,包含路径
    2. __LINE__:行号
    3. __func__:函数名称
  2. cpp中含有默认参数时,函数声明和定义不能同时出现
  3. cpp中静态成员变量以及静态成员函数在定义时候不能加static关键字,同理还有virtual关键字
  4. __thread关键字:该关键字修饰的变量为线程局部存储变量
  5. FD_CLOEXEC标志:这是文件描述符的标志之一。在新创建一个文件描述符时都应该设置这个标志位,特别是多线程程序下。man文档描述如下:

  6. __VA_ARGS__:一个可变参数宏,一般使用在宏定义中,例如:
#define LOG_ERROR(formatString, ...)\
do {\
    Logger& logger = Logger::getInstance();\
    logger.setLogLevel(static_cast<int>(Logger::LoggerLevel::ERROR));\
    char buffer[1024] = {0};\
    snprintf(buffer, 1024, formatString, ##__VA_ARGS__);\
} while (0)
  1. 名称空间
    1. 匿名命名空间:在使用namespace关键字定义名称空间时不指定名称,例如namespace {}。
    2. 引用全局命名空间时一般指定::,例如main函数就是在全局命名空间下。
  2. #pragma GCC diagnostic ignored的使用
  3. extern关键字的作用
  4. __builtin_expect的使用
  5. syscall(SYS_gettid)函数
  6. open("/dev/null)
  7. enable_shared_from_this的使用
  8. mutable关键字的使用:
    1. 修饰类的成员变量
    mutable std::mutex mutex_;
    
    size_t EventLoop::queueSize() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return pendingFunctors_.size();
    }
    
    
    error: binding reference of type ‘std::lock_guard<std::mutex>::mutex_type&’ {aka ‘std::mutex&’} to ‘const std::mutex’ discards qualifiers
    
    1. lambda表达式中修饰以值捕获的变量,这样仅仅可以让编译通过。但是lambda表达式的函数体中修改以值捕获的变量不会影响外部变量的改变。自导自演
  9. SO_KEEPALIVE选项:这是一个套接字选项,用于判断连接是否存活。但是在实际的游戏服务器开发中,在应用层上实现心跳包机制。

muduo的基本使用

  1. 参考https://github.com/chenshuo/muduo-tutorial/tree/master,将其克隆至本地,然后选择第二种构建方式。
  2. echo-server示例如下:
    1. EchoServer.h
    #pragma once
    
    #include "muduo/net/TcpServer.h"
    #include "muduo/net/EventLoop.h"
    #include "muduo/net/InetAddress.h"
    #include "muduo/net/TcpConnection.h"
    #include "muduo/base/AsyncLogging.h"
    #include "muduo/base/Logging.h"
    #include "muduo/base/CurrentThread.h"
    
    #include <memory>
    
    class EchoServer {
    public:
        EchoServer(muduo::net::EventLoop* loop, const muduo::net::InetAddress addr);
    
        void start() {
            _serverptr->start();
        }
    private:
        std::unique_ptr<muduo::net::TcpServer> _serverptr;
        muduo::net::EventLoop * _eventloop;
    
        void onConnection(const muduo::net::TcpConnectionPtr&);
        void onMessage(const muduo::net::TcpConnectionPtr&,
                                muduo::net::Buffer*,
                                muduo::Timestamp);
    };
    
    1. EchoServer.cc
    #include "EchoServer.h"
    
    
    #include <functional>
    #include <string>
    
    EchoServer::EchoServer(muduo::net::EventLoop* loop, const muduo::net::InetAddress addr)
        : _eventloop(loop){
        _serverptr.reset(new muduo::net::TcpServer(_eventloop, addr, "ECHO-SERVER"));
        _serverptr->setConnectionCallback(std::bind(&EchoServer::onConnection, this, std::placeholders::_1));
        _serverptr->setMessageCallback(std::bind(&EchoServer::onMessage,
            this,
            std::placeholders::_1,
            std::placeholders::_2,
            std::placeholders::_3));
        _serverptr->setThreadNum(4);
    }
    
    void EchoServer::onConnection(const muduo::net::TcpConnectionPtr& conn) {
        LOG_TRACE << conn->peerAddress().toIpPort() << " -> "
                  << conn->localAddress().toIpPort() << " is "
                  << (conn->connected() ? "UP" : "DOWN");
    }
    
    void EchoServer::onMessage(const muduo::net::TcpConnectionPtr& conn,
                            muduo::net::Buffer* buf,
                            muduo::Timestamp time) {
        std::string msg(buf->retrieveAllAsString());
        LOG_TRACE << conn->name() << " recv " << msg.size() << " bytes at " << time.toString();
        conn->send(msg);
    
    }
    
    
    1. main.cc
    #include "EchoServer.h"
    
    int kRollSize = 500*1000*1000;
    
    std::unique_ptr<muduo::AsyncLogging> g_asyncLog;
    
    void asyncOutput(const char* msg, int len) {
        g_asyncLog->append(msg, len);
    }
    
    void setLogging(const char* argv0) {
        muduo::Logger::setOutput(asyncOutput);
        char name[256];
        strncpy(name, argv0, 256);
        g_asyncLog.reset(new muduo::AsyncLogging(::basename(name), kRollSize));
        g_asyncLog->start();
    }
    
    
    int main(int argc, const char* argv[]) {
        // 设置日志级别
        muduo::Logger::setLogLevel(muduo::Logger::TRACE);
        
        setLogging(argv[0]);
      
        LOG_INFO << "pid = " << getpid() << ", tid = " << muduo::CurrentThread::tid();
        muduo::net::EventLoop loop;
        muduo::net::InetAddress listenAddr(8888);
        EchoServer server(&loop, listenAddr);
      
        server.start();
      
        loop.loop();
    
        return 0;
    }
    
    1. 编译:g++ main.cpp EchoServer.cpp -o test -lmuduo_base -lmuduo_net -std=c++11 -lpthread
  3. 从echo server这个例子,可知基于muduo开发服务器程序的步骤如下:
    1. 你的服务器主类中组合TcpServer对象以及EventLoop对象(这个EventLoop其实就是一个mainLoop)
    2. 在服务器主类中注册处理用户连接的创建和断开的回调函数以及处理读写事件的回调函数
    3. 设置合适的服务器线程数量,内部的EventThreadPool会根据设置的线程数量创建相应个数的subLoop。如果不设置线程数量,则服务器程序默认只有1个mainLoop负责监听客户端的连接请求以及和客户端通信。
    4. 启动server
    server.start();
    
    1. mainLoop进入循环,监听客户端的连接请求
    loop.loop();