• 服务端主动关闭连接,如何确保对端能够收到全部的数据?


    本文是个人对本问题的分析(Muduo库),如有不对的地方请指正😄

    主动关闭连接,如何确保对端收到数据?

    问题:如果不是长连接的话,发送完毕数据之后应该是关闭连接,因此关闭连接需要确保数据发送完成

    1. 暂时使用sleep(1)等待数据发送完毕是可以的,但是这是绝对不允许的。用其他 同步机制? or 别的方法
    2. 不使用shutdown(…WR)而是关闭写端也是可以的,但是。。。。。
    3. send发送数据的内部会确保数据发送成功的,shutdown同时确认一下是否发送成功✅

    解决:

    首先发送数据是调用的TcpConnection::send()函数进行发送

    发送的流程:

    1. 如果是第一次发送,缓冲区中没有数据,调用write进行发送,剩没发送完的余的数据放置到outputBuffer中(注册socket的写事件)
    2. 触发了写事件的回调(如果有数据没发送完),也就是将缓冲区的数据发送出去,直到发送完毕将写事件注销掉

    发送数据的代码

    void TcpConnection::send(const string& message) {
        int remain = message.size();
        int send_size = 0;
        // channel_第一次写数据(epoll没关注写时间),缓冲区不能有待发送的数据
        if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0) {
            send_size = static_cast<int>(::write(channel_->fd(), message.data(), message.size()));
            if (send_size >= 0) {
                remain -= send_size;
            } else {
                if (errno != EWOULDBLOCK) {
                    printf("TcpConnection::Send write failed\n");
                }
                return ;
            }
        }
    
        assert(remain <= message.size());
        if (remain > 0) {
            // 将剩余的数据写入buffer中, 要注意添加到之前还剩余的数据的后面
            outputBuffer_.append((char*)message.c_str() + send_size, remain);
            if (!channel_->isWriting()) {   // 如果没有关注写的事件
                printf("数据没发送完,放到buffer中,注册写事件\n");
                channel_->enableWriting();  // 注册写事件
            }
        }
    }
    
    void TcpConnection::send(Buffer* buffer) {    
        if (state_ == kConnected) {
            send(std::move(string(buffer->peek(), buffer->readableBytes())));
            buffer->retrieveAll();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    如何保证对端收到数据?

    1. 在关闭的时候会 判断一下socket是否是还是在EPOLLER中可写的状态(如果是可写的状态,那么久代表缓冲区还没写完呢), 如果不可写那么就代表缓冲区残留的没发送出去的数据(如果有)也发送完毕了

    连接关闭的代码

    void TcpConnection::shutdown()
    {
        if(state_ == kConnected)
        {
            setState(kDisconnecting);
            // 在自己的线程中执行回调
            loop_->runInLoop(std::bind(&TcpConnection::shutdownInLoop, this));
        }
    }
    
    void TcpConnection::shutdownInLoop() {
        // 如果不可写,说明已经将发送缓冲区outputBuffer的数据发送完了, 保证数据发送完
        if(!channel_->isWriting())
        {
            // 关闭写端
            socket_->shutdownWrite();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    大话设计模式——4.装饰模式(Decorator Pattern)
    动态规划算法(3)--0-1背包、石子合并、数字三角形
    Leetcode 142. 环形链表 II
    德语翻译器在线翻译中文-德语翻译器支持各大语言翻译
    C++笔记之vector的初始化以及assign()方法
    CentOS的安装与网络配置
    周报 | 24.6.3-24.6.9文章汇总
    java如何进阶?
    吴恩达《机器学习》9-4-9-6:实现注意:展开参数、梯度检验、随机初始化
    ESP8266智能家居(1)——开发环境的搭建
  • 原文地址:https://blog.csdn.net/qq_52245648/article/details/126529907