问题排查|记录一次基于mymuduo库开发的服务器错误排查(回响服务器无法正常工作)

问题背景:

服务器程序如下:

cpp 复制代码
#include <mymuduo/TcpServer.h>
#include <mymuduo/Logger.h>

#include <string>
#include <functional>

class EchoServer
{
public:
    EchoServer(EventLoop *loop,
            const InetAddress &addr, 
            const std::string &name)
        : server_(loop, addr, name)
        , loop_(loop)
    {
        // 注册回调函数
        server_.setConnectionCallback(
            std::bind(&EchoServer::onConnection, this, std::placeholders::_1)
        );

        server_.setMessageCallback(
            std::bind(&EchoServer::onMessage, this,
                std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)
        );

        // 设置合适的loop线程数量 loopthread
        server_.setThreadNum(2);
    }
    void start()
    {
        server_.start();
    }
private:
    // 连接建立或者断开的回调
    void onConnection(const TcpConnectionPtr &conn)
    {
        if (conn->connected())
        {
            LOG_INFO("Connection UP : %s", conn->peerAddress().toIpPort().c_str());
        }
        else
        {
            LOG_INFO("Connection DOWN : %s", conn->peerAddress().toIpPort().c_str());
        }
    }

    // 可读写事件回调
    void onMessage(const TcpConnectionPtr &conn,
                Buffer *buf,
                Timestamp time)
    {
        std::string msg = buf->retrieveAllAsString();
        conn->send(msg);
        conn->shutdown(); // 写端   EPOLLHUP =》 closeCallback_
    }

    EventLoop *loop_;
    TcpServer server_;
};

int main()
{
    EventLoop loop;
    InetAddress addr(8000);
    EchoServer server(&loop, addr, "EchoServer-01"); // Acceptor non-blocking listenfd  create bind 
    server.start(); // listen  loopthread  listenfd => acceptChannel => mainLoop =>
    loop.loop(); // 启动mainLoop的底层Poller

    return 0;
}

另起一个终端,其中使用命令:

sh 复制代码
sudo telnet 127.0.0.1 8000

会导致服务器端有以下错误:

sh 复制代码
[INFO]2024/05/31 21:51:16: channel handleEvent revents: 1
[INFO]2024/05/31 21:51:16: 1 events happened 

[INFO]2024/05/31 21:51:16: channel handleEvent revents: 1
[INFO]2024/05/31 21:51:16: 1 events happened 

[INFO]2024/05/31 21:51:16: channel handleEvent revents: 1
[INFO]2024/05/31 21:51:16: 1 events happened 

[INFO]2024/05/31 21:51:16: channel handleEvent revents: 1
[INFO]2024/05/31 21:51:16: 1 events happened 

[INFO]2024/05/31 21:51:16: channel handleEvent revents: 1
[INFO]2024/05/31 21:51:16: 1 events happened 

[INFO]2024/05/31 21:51:16: channel handleEvent revents: 1
[INFO]2024/05/31 21:51:16: 1 events happened 

[INFO]2024/05/31 21:51:16: channel handleEvent revents: 1
[INFO]2024/05/31 21:51:16: 1 events happened 

[INFO]2024/05/31 21:51:16: channel handleEvent revents: 1
[INFO]2024/05/31 21:51:16: 1 events happened 

[INFO]2024/05/31 21:51:16: channel handleEvent revents: 1
[INFO]2024/05/31 21:51:16: 1 events happened 

服务器一直循环打印如下内容

问题分析

epoll_wait 一直返回并触发 EPOLLIN 事件,这通常是因为某个文件描述符一直处于可读状态,但没有正确处理。这种情况最常见的原因是未正确读取客户端发送的数据,导致 epoll 一直认为有数据可读,从而不断触发可读事件。

分析和调试步骤

首先使用gdb调试,然后把整个服务跑起来,随后起另一个终端:

sh 复制代码
sudo telnet 127.0.0.1 8000

连接之后复现出问题,

随后在gdb中中断程序,成功捕捉到出问题的地方,这里大概就是在EventLoop::loop(),然后在EpollPoller::poll()处,

现在我们可以分析问题了,本来应该在新连接建立后,就由TcpConnection来进行连接的管理了,现在在循环打印poll中的内容,说明epoll_wait根本就没有呗阻塞。

再一个,就算我们把telnet连接断开,服务器仍然会不断打印EpollPoller::poll()的内容,那么抓到问题了,肯定是channel的原因。

那么我们就开始差channel吧,首先需要关注的是文件描述符是否正常,设置的感兴趣的事件是否正常,最后就是被激活的事件是否正常。

打印之后发现确实没啥毛病。

这么说的话,我们需要重新审视该问题,如果一个channel不停得在循环打印EpollPoller::poll()得内容,并且channel中封装的文件描述符也都正常,那最主要的问题就在于channel相关的回调函数没有被正确设置,或者逻辑有重大问题。

后来顺着channel执行回调的地方顺逻辑,也没发现什么问题;

然后我突然想到新连接建立的时候,那个channel是由Acceptor打包的,它的主要任务不就是监听新用户连接,打包成channel,然后把它们分发给subloop吗?

直接先看Acceptor的构造函数:

cpp 复制代码
acceptChannel_.setErrorCallback(std::bind(&Acceptor::handleRead, this));

在构造函数中我竟然没有设置读回调,直接大错特错,而且Acceptor::handleRead本来就是它的读回调啊!!!

把它改成:

cpp 复制代码
acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));

至此,问题解决。

相关推荐
tntlbb14 分钟前
Ubuntu20.4 VPN+Docker代理配置
运维·ubuntu·docker·容器
如若12317 分钟前
对文件内的文件名生成目录,方便查阅
java·前端·python
PyAIGCMaster19 分钟前
文本模式下成功。ubuntu P104成功。
服务器·数据库·ubuntu
滚雪球~1 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语1 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport1 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg1 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全
胡西风_foxww1 小时前
【es6复习笔记】rest参数(7)
前端·笔记·es6·参数·rest
m0_748254881 小时前
vue+elementui实现下拉表格多选+搜索+分页+回显+全选2.0
前端·vue.js·elementui
Linux运维技术栈1 小时前
Ansible(自动化运维)环境搭建及ansible-vault加密配置
运维·自动化·ansible