(十一)发送数据

1. Channel

LT 在需要关注writable 事件才关注以防造成busy loop;

+ bool isWriting() const { return _events & kWriteEvent; }

2. TcpConnection

+ void sendInLoop(const std::string&);
+ void shutdownInLoop();
+ Buffer _outBuffer;

在这里插入图片描述
目前TcpConnection 有四个状态,状态间的转移条件如上;

shutdown 关闭写通道,底层的系统调用如下:

::shutdown(sockfd, SHUT_WR);

TcpConnection 监听的channel 如不在发送数据,即可调用shutdown 关闭;

void TcpConnection::shutdown() {
    if (_state == kConnected) {
        setState(kDisconnecting);
        _loop->runInLoop(std::bind(&TcpConnection::shutdownInLoop, this));
    }
}

void TcpConnection::shutdownInLoop() {
    _loop->assertInLoopThread();
    if (!_channel->isWriting()) {
        _socket->shutdownWrite();
    }
}

因为新增了TcpConnection 状态,handleClose 和 connectDestroyed assert 也要改变条件;

send() 在IO 线程中可以直接调用,在非IO 线程中要queueInLoop 从而产生复制开销;

void TcpConnection::send(const std::string &message) {
    if (_state == kConnected) {
        if (_loop->isInLoopThread()) {
            sendInLoop(message);
        } else {
            _loop->runInLoop(
                std::bind(&TcpConnection::sendInLoop, this, message));
        }
    }
}

void TcpConnection::sendInLoop(const std::string &message) {
    _loop->assertInLoopThread();
    ssize_t nwrote = 0;
    if (!_channel->isWriting() && _outputBuffer.readableBytes() == 0) {
        nwrote = ::write(_channel->fd(), message.data(), message.size());
        if (nwrote >= 0) {
            if (implicit_cast<size_t>(nwrote) < message.size()) {
                LOG_TRACE << "I am going to write more data";
            }
        } else {
            nwrote = 0;
            if (errno != EWOULDBLOCK) {
                LOG_SYSERR << "TcpConnection::sendInLoop";
            }
        }
    }
    assert(nwrote >= 0);
    if (implicit_cast<size_t>(nwrote) < message.size()) {
        _outputBuffer.append(message.data() + nwrote, message.size() - nwrote)
        if (!_channel->isWriting()) {
            _channel->enableWriting();
        }
    }
}

注意到sendInLoop 只调用一次write(未开始写并且没有残余数据方可进行),不然会造成乱序;
没有发送完的或者前面有数据残留,保存到Buffer 中开启_channel 写通道,使poller 轮询到writeCallback 将Buffer 中的数据写完;(在TcpConnection 构造也要设置)

_channel->setWriteCallback(std::bind(&TcpConnection::handleWrite, this));
void TcpConnection::handleWrite() {
    _loop->assertInLoopThread();
    if (_channel->isWriting()) {
        ssize_t n = ::write(_channel->fd(), _outputBuffer.peek(),
                            _outputBuffer.readableBytes());
        if (n > 0) {
            _outputBuffer.retrieve(n);
            if (_outputBuffer.readableBytes() == 0) {
                _channel->disableWriting();
                if (_state == kDisconnecting) {
                    shutdownInLoop();
                }
            } else {
                LOG_TRACE << "I am going to write more data";
            }
        } else {
            LOG_SYSERR << "TcpConnection::handleWrite";
        }
    } else {
        LOG_TRACE << "Connection is down, no more writing";
    }
}

handleWrite 只要不被关闭就可以持续发完Buffer 中的数据,shutdown 可以关闭写通道但要保证Buffer 中的数据全部发送完;

当Buffer 数据读完即使通道禁止发送,若此时连接正在关闭则调用shutdown 改变socket 属性;(禁止发送后不影响socket 通讯,当又有send 调用时即可恢复)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值