[sylar]C++高性能服务器框架——TcpServer模块

417 阅读1分钟

TcpServer模块概述

封装了一个TCP服务器,然后基于TcpServer实现了一个EchoServer

详解

class TcpServer

mumber(成员变量)

// 多监听,多网卡
std::vector<Socket::ptr> m_socks;
// 新连接的Socket工作的调度器
IOManager* m_worker;
// 服务器Socket接收连接的调度器
IOManager* m_acceptWorker;
// 接收超时时间
uint64_t m_recvTimeout;
// 服务器名称
std::string m_name;
// 服务是否停止
bool m_isStop;

TcpServer(构造函数)

TcpServer::TcpServer(sylar::IOManager* worker, sylar::IOManager* accept_worker)
    :m_worker(worker)
    ,m_acceptWorker(accept_worker)
    ,m_recvTimeout(g_tcp_server_read_timeout->getValue())
    ,m_name("sylar/1.0.0")
    ,m_isStop(true) {
}

~TcpServer(析构函数)

TcpServer::~TcpServer() {
    for (auto& i : m_socks) {
        i->close();
    }
    m_socks.clear();
}

bind(绑定地址以及监听)

可以获取绑定失败的地址

// 绑定单个地址
bool TcpServer::bind(sylar::Address::ptr address) {
    std::vector<Address::ptr> addrs;
    std::vector<Address::ptr> fails;
    addrs.push_back(address);
    return bind(addrs, fails);
}
​
// 绑定多个地址
bool TcpServer::bind(const std::vector<Address::ptr>& addrs
                , std::vector<Address::ptr>& fails) {
    for (auto& addr : addrs) {
        // 创建TCPsocket
        Socket::ptr sock = Socket::CreateTCP(addr);
        // bind
        if (!sock->bind(addr)) {
            SYLAR_LOG_DEBUG(g_logger) << "bind fail errno = "
                << errno << "strerr = " << strerror(errno)
                << " addr = [" << addr->toString() << "]";
            // bind失败放入失败数组
            fails.push_back(addr);
            continue;
        }
        // 监听
        if (!sock->listen()) {
            SYLAR_LOG_ERROR(g_logger) << "listen fali errno = "
                << errno << " errstr = " << strerror(errno)
                << " addr = [" << addr->toString() << "]";
            // 监听失败放入失败数组
            fails.push_back(addr);
            continue;
        }
        m_socks.push_back(sock);
    }
    // 有绑定失败的地址,清空监听socket数组
    if (!fails.empty()) {
        m_socks.clear();
        return false;
    }
    
    // 绑定成功
    for (auto& i : m_socks) {
        SYLAR_LOG_INFO(g_logger) << "server bind success: " << *i;
    }
​
    return true;
}

startAccept(开始接受连接)

void TcpServer::startAccept(Socket::ptr sock) {
    // 服务器不停止,一直接收连接
    while (!m_isStop) {
        // accept
        Socket::ptr client = sock->accept();
        // 连接成功
        if (client) {
            SYLAR_LOG_DEBUG(g_logger) << "accept client" << client;
            // 设置接收超时时间
            client->setRecvTimeout(m_recvTimeout);
            
            // handleClient 结束之前, TcpServer不能结束,shared_from_this,把自己传进去
            m_worker->schedule(std::bind(&TcpServer::handleClient
                                    , shared_from_this(), client));
        } else {
            SYLAR_LOG_ERROR(g_logger) << "accept errno = " << errno
                << " strerr = " << strerror(errno);
        }
    }
}

start(启动服务)

bool TcpServer::start() {
    if (!m_isStop) {
        return true;
    }
    m_isStop = false;
    // 每个socket接收连接任务放入任务队列中
    for (auto& sock : m_socks) {
        m_acceptWorker->schedule(std::bind(&TcpServer::startAccept
                                    , shared_from_this(), sock));
    }
    return true;
}

stop(停止服务器)

void TcpServer::stop() {
    m_isStop = true;
    auto self = shared_from_this();
    // 将this和self作为参数传递给异步任务的lambda函数,以确保异步任务执行期间当前对象的shared_ptr一直有效
    m_acceptWorker->schedule([this, self]() {
        for (auto& sock : m_socks) {
            sock->cancelAll();
            sock->close();
        }
        m_socks.clear();
    });
}

echo_server

#include "../sylar/tcp_server.h"
#include "../sylar/log.h"
#include "../sylar/iomanager.h"
#include "../sylar/socket.h"
#include "../sylar/bytearray.h"
#include "../sylar/streams/socket_stream.h"

static SYLAR__ROOT__LOG(g_logger);

class EchoServer: public sylar::TcpServer {
public:
    EchoServer(int type);
    void handleClient(sylar::Socket::ptr client) override;
    
private:
    int m_type = 0;
};


EchoServer::EchoServer(int type) 
    :m_type(type){
    
}

void EchoServer::handleClient(sylar::Socket::ptr client) {
    SYLAR_LOG_INFO(g_logger) << "handllClient " << *client;
    sylar::SocketStream::ptr tcp(new sylar::SocketStream(client));
    sylar::ByteArray::ptr ba(new sylar::ByteArray);
    while (true) {
        SYLAR_LOG_INFO(g_logger) << "==== while ====";
        ba->clear();
        int rt = tcp->read(ba, 1024);
        SYLAR_LOG_INFO(g_logger) << "read rt = " << rt;

        if (rt == 0) {
            SYLAR_LOG_INFO(g_logger) << "client close: " << *client;
            break;
        } else if (rt < 0) {
            SYLAR_LOG_INFO(g_logger) << "client error rt = " << rt
                << " errno = " << errno << " strerr = " << strerror(errno);
            break;
        }
        
        ba->setPosition(0);
        if (m_type == 1) {  //text
            SYLAR_LOG_INFO(g_logger) << "\n" << ba->toString();
        } else {
            SYLAR_LOG_INFO(g_logger) << "\n" << ba->toHexString();
        }
    }
}

int type = 1;

void run() {
    EchoServer::ptr es(new EchoServer(type));
    auto addr = sylar::Address::LookupAny("0.0.0.0:8020");
    while (!es->bind(addr)) {
        sleep(2);
    }
    es->start();
}

int main(int argc, char** argv) {
    if (argc < 2 || (strcmp(argv[1], "-b") && strcmp(argv[1], "-t"))) {
        SYLAR_LOG_INFO(g_logger) << "used as[" << argv[0] << "-t] or [" << argv[0] << "-b]";
        return 0;
    }

    if (!strcmp(argv[1], "-b")) {
        type = 2;
    }

    sylar::IOManager iom(2);
    g_logger->setLevel(sylar::LogLevel::INFO);
    iom.schedule(run);
    
    return 0;
}