sylar高性能服务器-日志(P67-P68)内容记录

P68-P69TcpServer封装

这一节的内容对于之前学过网络编程的同学看起来比较简单一些,也没啥新的内容,都是使用之前完成的模块搭建了一个简单的echo服务器。

class TcpServer

成员变量
// 多监听,多网卡
std::vector<Socket::ptr> m_socks;
// 新连接的Socket工作的调度器, IOManager就是线程池
IOManager* m_worker;
// 服务器Socket接收连接的调度器 
IOManager* m_acceptWorker;
// 接收超时时间
uint64_t m_recvTimeout;
// 服务器名称
std::string m_name;
// 服务是否停止
bool m_isStop; 
构造函数
static sylar::ConfigVar<uint64_t>::ptr g_tcp_server_read_timeout =
    sylar::Config::Lookup("tcp_server.read_timeout", (uint64_t)(60 * 1000 * 2),
        "tcp server read timeout");

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) {

}
bind(绑定地址以及监听)
// 绑定单个地址
bool TcpServer::bind(sylar::Address::ptr addr) {
    // 绑定成功的地池容器
    std::vector<Address::ptr> addrs;
    // 绑定失败的地址容器
    std::vector<Address::ptr> fail;
    addrs.push_back(addr);
    return bind(addrs, fail);
}

// 绑定多个地址容器
bool TcpServer::bind(const std::vector<Address::ptr>& addrs, std::vector<Address::ptr>& fails) {
    //  m_ssl = ssl;
    // 遍历传入的地址容器
    for(auto& addr : addrs) {
        // Socket::ptr sock = ssl ? SSLSocket::CreateTCP(addr) : Socket::CreateTCP(addr);
        // 调用之前封装好的hook函数,创建一个TCP连接
        Socket::ptr sock = Socket::CreateTCP(addr);
        // 绑定失败
        if(!sock->bind(addr)) {
            SYLAR_LOG_ERROR(g_logger) << "bind fail errno="
                << errno << " errstr=" << strerror(errno)
                << " addr=[" << addr->toString() << "]";
            // 记录当前失败的地址
            fails.push_back(addr);
            // 继续下一个地址
            continue;
        }
        // 监听失败
        if(!sock->listen()) {
            SYLAR_LOG_ERROR(g_logger) << "listen fail errno="
                << errno << " errstr=" << strerror(errno)
                << " addr=[" << addr->toString() << "]";
            fails.push_back(addr);
            continue;
        }
        // 绑定、监听都成功
        m_socks.push_back(sock);
    }
    // 如果绑定失败的地址容器不为空,bind调用函数返回false,清空所有Socket
    if(!fails.empty()) {
        m_socks.clear();
        return false;
    }
    // 终端打印出绑定好的地址
    for(auto& i : m_socks) {
        // SYLAR_LOG_INFO(g_logger) << "type=" << m_type
        //     << " name=" << m_name
        //     << " ssl=" << m_ssl
        SYLAR_LOG_INFO(g_logger)  << " server bind success: " << *i;
    }
    return true;
}
startAccept
// 服务器不断监听传入的Socket对象,等待并接收客户端的连接
void TcpServer::startAccept(Socket::ptr sock) {
    // 如果服务器没有停止
    while(!m_isStop) {
        // 接受一个新的客户端连接
        Socket::ptr client = sock->accept();
        // 成功接收了一个连接
        if(client) {
            // 设置服务器等待客户端发送数据的最大时间
            client->setRecvTimeout(m_recvTimeout);
            // m_ioWorker->schedule(std::bind(&TcpServer::handleClient,
            //             shared_from_this(), client));
            // 将handleClient加入到工作线程队列m_worker中
            m_worker->schedule(std::bind(&TcpServer::handleClient,
                        shared_from_this(), client));
        } else {
            SYLAR_LOG_ERROR(g_logger) << "accept errno=" << errno
                << " errstr=" << strerror(errno);
        }
    }
}
Start
bool TcpServer::start() {
    if(!m_isStop) {
        return true;
    }
    m_isStop = false;
    for(auto& sock : m_socks) {
        // 异步执行startAccept
        m_acceptWorker->schedule(std::bind(&TcpServer::startAccept,
                    shared_from_this(), sock));
    }
    return true;
}
Stop
void TcpServer::stop() {
    // 标记服务器为停止状态
    m_isStop = true;
    // 使用shared_from_this()获取当前TcpServer对象的共享指针,并将其存储在局部变量self中。
    // 这是为了确保在异步任务执行期间TcpServer对象不会被销毁。
    auto self = shared_from_this();
    // 这个lambda表达式捕获了this指针(即当前对象的指针)和self(即TcpServer的共享指针)。
    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/bytearray.h"
#include "sylar/address.h"

static sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT();

// EchoServer继承于TcpServer
class EchoServer : public sylar::TcpServer {
public:
    EchoServer(int type);
    // 重写
    void handleClient(sylar::Socket::ptr client);

private:
    // 控制服务器行为:是否以文本或二进制形式输出接收到的数据
    int m_type = 0;
};

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

// 处理客户端连接
void EchoServer::handleClient(sylar::Socket::ptr client) {
    // 日志打印正在处理的来自某个客户端的连接
    SYLAR_LOG_INFO(g_logger) << "handleClient " << *client;   
    // 存储从客户端接收到的数据
    sylar::ByteArray::ptr ba(new sylar::ByteArray);
    // 循环接收数据
    while(true) {
        // 清空缓冲区
        ba->clear();
        /* struct iovec
            {
                void *iov_base;	 Pointer to data.  
                size_t iov_len;	 Length of data.  
            };
        */
        std::vector<iovec> iovs;
        // 用于接收客户端发送的数,这里指定了每个iovec的大小为1024字节。
        ba->getWriteBuffers(iovs, 1024);
        // 接收数据, rt保存实际接收到的字节数
        int rt = client->recv(&iovs[0], iovs.size());
        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 << " errstr=" << strerror(errno);
            break;
        }
        // 移动位置指针在接收到的数据后面,不能删除,防止新接收的数据覆盖之前的数据
        ba->setPosition(ba->getPosition() + rt);
        // 设置为0方便从头读取或处理数据
        ba->setPosition(0);
        // SYLAR_LOG_INFO(g_logger) << "recv rt=" << rt << " data=" << std::string((char*)iovs[0].iov_base, rt);
        if(m_type == 1) {//text 
            std::cout << ba->toString() << std::endl;
            // SYLAR_LOG_INFO(g_logger) << ba->toString();
        } else {
            std::cout << ba->toHexString() << std::endl;
            // SYLAR_LOG_INFO(g_logger) << ba->toHexString();
        }
        std::cout.flush();
    }
}

int type = 1;

void run() {
    SYLAR_LOG_INFO(g_logger) << "server type=" << type;
    EchoServer::ptr es(new EchoServer(type));
    // 解析传入的字符串地址 "0.0.0.0"是一个特殊的IP地址,表示服务器应该监听所有可用的网络接口。
    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) {
        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);
    iom.schedule(run);
    return 0;
}

服务器

image-20240312201551374

客户端

image-20240312201615264

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

madkeyboard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值