Buffer类的接口如下,可以看到,该类是线程安全的,支持多个线程向Buffer中写入数据而不会发生Race Condition。
//Buffer.h
#ifndef BUFFER_H
#define BUFFER_H
#include <string>
#include <algorithm>
#include "common.h"
#include <mutex>
class Buffer{
private:
std::string buff_;//buff数据
public:
std::mutex mut_;//用于保护缓冲区
void append(char *data,int size);//往buffer中添加数据
int size() ;//返回buff大小
const char *ptr();//返回首地址指针
void clear();
void pop(int size);
bool getMsg(std::string &msg);
};
#endif
//Buffer.cpp
#include "Buffer.h"
//往buffer中添加数据
void Buffer::append(char *data,int size){
{
std::lock_guard<std::mutex> guard(mut_);
buff_.append(data,size);
}
}
//返回buff大小
int Buffer::size() {
std::lock_guard<std::mutex> guard(mut_);
return buff_.size();
}
//返回首地址指针
const char *Buffer::ptr(){
std::lock_guard<std::mutex> guard(mut_);
return buff_.data();
}
void Buffer::clear(){
std::lock_guard<std::mutex> guard(mut_);
buff_.clear();
}
void Buffer::pop(int size){
std::lock_guard<std::mutex> guard(mut_);
buff_.erase(0,size);
}
bool Buffer::getMsg(std::string &msg){
std::lock_guard<std::mutex> guard(mut_);
if(buff_.size()>=4){
int len;
memcpy(&len,buff_.data(),sizeof(int));
if(buff_.size()>=4+len){
msg = std::string(buff_.data()+4,len);
buff_.erase(0,4+len);
return true;
}
}
return false;
}
下面展示的是用于处理客户端消息完毕,相应客户端时的操作。由于Reactor模型中关于具体业务处理与对客户的IO操作要分离,即可能有多个业务线程处理完业务响应客户时,调用writeOutBuff往outBuff_ 中加入数据,而IO线程则会根据**outBuff_**中是否存在数据,将数据发送给客户端。
//TCPSERVER 类往输出缓冲区中添加数据
void Connection::writeOutBuff(std::string &data){
char buff_[128];
bzero(buff_,sizeof(buff_));
int len = data.size();
if(len<=0) //没有数据需要加入输出缓冲区
return;
memcpy(buff_,&len,sizeof(int));
memcpy(buff_+sizeof(int),data.data(),len);
outBuff_.append(buff_,len+4);
if((connectionChannel_->events()&EPOLLOUT)==false)//若epoll未监听写事件
connectionChannel_->enableWriting();//让epollloop监听写事件
}
//Channel类监听到写事件,回调Connection发送数据
void Connection::onMessageOut(){
if(outBuff_.size()>0){
int num = send(getCliSocket()->fd(),outBuff_.ptr(),outBuff_.size(),0);//将outBuff_中的数据尽可能多的发送出去
if(num>0){
outBuff_.pop(num);
}
}else{//输出缓冲区发送完毕,取消监听写事件
sendCompleteCallBack_(this);
//这里有问题,先不管
//多线程调用writeOutBuff函数时可能刚刚启用监听写事件,这里马上就禁用写事件
//特别是在数据发送末尾阶段可能发生,导致outBuff_中还有数据未发送就被禁用写事件
//即outBuff_.size()==0 与下面这个操作不能一气呵成导致的
connectionChannel_->disableWriting();
}
}
问题出在这里onMessageOut这里,当IO线程检测到outBuff_ 中没有数据时,而业务线程刚好调用writeOutBuff往 outBuff_ 中加入数据,这个时候 oubBuff_ 还存在数据,却直接被禁用写事件了,而且往往发生业务线程最后一次调用 writeOutBuff 时。