一、IO多路復用
/**
* @brief 封裝select的IO多路復用函數
*/
void FIO::Select(int timeoutMs)
{
struct timeval timeout;
timeout.tv_sec = timeoutMs / 1000;
timeout.tv_usec = (timeoutMs % 1000) * 1000;
fd_set rfds, wfds;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
int max_fd = -1;
for (auto& fd : rset_)
{
FD_SET(fd, &rfds);
max_fd = std::max(max_fd, fd);
}
for (auto& fd : wset_)
{
FD_SET(fd, &wfds);
max_fd = std::max(max_fd, fd);
}
int nready = select(max_fd + 1, &rfds, &wfds, nullptr, timeoutMs == -1 ? nullptr : &timeout);
if(nready < 0)
{
if(errno == EINTR) return; //中斷
perror("select");
abort();
}
if(FD_ISSET(pipe_fd_[0], &rfds)) ReadEvent(); //讀事件
if(nready <= 0) return;
for (auto& fd : rset_)
{
if(FD_ISSET(fd, &rfds)) readCallback_(fd);
}
for (auto& fd : wset_)
{
if(FD_ISSET(fd, &wfds)) writeCallback_(fd);
}
}
fastlio2使用IO多路復用技術,代碼中提供了封裝select的IO多路復用函數Select。Select可以同時得到讀寫事件的通知。在Select函數中,先對讀寫事件分別進行了set,取得當前連接數最大的fd,然後在select內部會阻塞至少timeoutSecs秒等待有事件發生。如果一直等下去,但沒有任何一個事件發生,那麼就會超時。
二、連接管理
/**
* @brief 為連接設置回調函數,然後以此維持連接的生命周期
*/
void Connection::SetCallback(const closure_t& read_cb, const closure_t& write_cb, const callback_t& error_cb, bool use_mutex)
{
read_cb_ = read_cb;
write_cb_ = write_cb;
error_cb_ = error_cb;
events_ |= kPollIn;
if (write_cb_) events_ |= kPollOut;
if (use_mutex) mutex_.lock();
conn_stats_.active = true;
if (use_mutex) mutex_.unlock();
loop_->AddEvents(fd_, events_);
}
Connect是fastlio2中重要的連接類,其錄了連接的流程和生命周期的維護。代碼中SetCallback函數會傳入三個回調函數,並且設置讀事件,在傳入的寫事件回調函數不為空時,同時設置讀寫兩個事件。
三、事件循環
/**
* @brief 註冊新事件
*/
void EventLoop::AddEvents(int fd, int events, int clear_events)
{
struct epoll_event event;
event.data.fd = fd;
event.events = events | EPOLLET | EPOLLERR | EPOLLHUP;
if(clear_events != 0) event.events |= clear_events;
if(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event) < 0)
{
perror("eventloop epol_ctl add failed");
abort();
}
else
{
total_events_++;
}
}
/**
* @brief 刪除事件
*/
void EventLoop::RemoveEvents(int fd)
{
if(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) < 0)
{
perror("eventloop epol_ctl del failed");
abort();
}
else
{
total_events_--;
}
}
EventLoop作為fastlio2的核心之一,負責事件循環和事件的註冊和刪除。代碼中AddEvents函數通過epoll_ctl函數註冊事件,只有在註冊成功後,total_events_計數器才會加一。RemoveEvents函數則是從epoll中刪除事件,並在刪除成功後將計數器減一。
四、信號處理
/**
* @brief 註冊信號處理函數
*/
void FSignal::Register(int signo, const signal_callback_t& callback)
{
sa_.sa_handler = handler;
sigfillset(&sa_.sa_mask);
sa_.sa_flags = 0;
sigaction(signo, &sa_, nullptr);
}
/**
* @brief 處理信號中斷
*/
void FSignal::handler(int sig)
{
void* array[1024];
auto size = backtrace(array, 1024);
fprintf(stderr, "signal %d caught:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
_exit(1);
}
FSignal類利用了*nu_signal.h*庫處理程序的信號。Register函數直接調用了sigaction函數,通過註冊信號處理函數來處理特定的信號。handler函數針對了信號的中斷,當接收到SIGABRT信號時,程序會列印出當前函數調用棧的dump,然後終止程序的執行。
五、緩存池
/**
* @brief 讀取一行請求(只限HTTP協議頭)
*/
int ByteBuffer::ReadHttpLine(char* buf, int maxlen)
{
int begin_pos = pos_;
char c = 0;
int len = 0;
while (pos_ < limit_ && len begin_pos ? len : -1;
}
ByteBuffer負責對讀取的數據進行緩存,用於最大化的減少IO次數。代碼中,ReadHttpLine函數從緩存中讀取標準的HTTP協議頭,讀取到socket的下一個請求頭後,循環立即退出。如果在maxlen的最大長度內沒有定位到HTTP頭,則返回-1。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/196015.html
微信掃一掃
支付寶掃一掃