本文是个人对本问题的分析(Muduo库),如有不对的地方请指正😄
主动关闭连接,如何确保对端收到数据?
问题:如果不是长连接的话,发送完毕数据之后应该是关闭连接,因此关闭连接需要确保数据发送完成
- 暂时使用
sleep(1)
等待数据发送完毕是可以的,但是这是绝对不允许的。用其他 同步机制? or 别的方法 - 不使用shutdown(…WR)而是关闭写端也是可以的,但是。。。。。
send
发送数据的内部会确保数据发送成功的,shutdown同时确认一下是否发送成功✅
解决:
首先发送数据是调用的
TcpConnection::send()
函数进行发送发送的流程:
- 如果是第一次发送,缓冲区中没有数据,调用write进行发送,剩没发送完的余的数据放置到outputBuffer中(注册socket的写事件)
- 触发了写事件的回调(如果有数据没发送完),也就是将缓冲区的数据发送出去,直到发送完毕将写事件注销掉
发送数据的代码
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();
}
}
如何保证对端收到数据?
- 在关闭的时候会 判断一下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();
}
}