Linux环境SOCKET编程4:Reactor模式

1、Reactor模式简介

       Wikipedia上说:“The reactor design pattern is an event handling pattern for handling service requests delivered concurrently by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to associated request handlers.”。从这个描述中,我们知道Reactor模式首先是事件驱动的,有一个或多个并发输入源,有一个Service Handler,有多个Request Handlers;这个Service Handler会同步的将输入的请求(Event)多路复用的分发给相应的Request Handler。如果用图来表达:

      从结构上,这有点类似生产者消费者模式,即有一个或多个生产者将事件放入一个Queue中,而一个或多个消费者主动的从这个Queue中Poll事件来处理;而Reactor模式则并没有Queue来做缓冲,每当一个Event输入到Service Handler之后,该Service Handler会主动的根据不同的Event类型将其分发给对应的Request Handler来处理。 

2、改进型Reactor模式

模式1:采用基于事件驱动的设计,当有事件触发时,才会调用处理器进行数据处理

 

      Reactor:负责响应IO事件,当检测到一个新的事件,将其发送给相应的Handler去处理。

      Handler:负责处理非阻塞的行为,标识系统管理的资源;同时将handler与事件绑定。

      Reactor为单个线程,需要处理accept连接,同时发送请求到处理器中。

     由于只有单个线程,所以处理器中的业务需要能够快速处理完。

      模式2使用多线程处理业务逻辑

      将处理器的执行放入线程池,多线程进行业务处理。但Reactor仍为单个线程。

      模式3对于多个CPU的机器,为充分利用系统资源,将Reactor拆分为两部分。

       mainReactor负责监听连接,accept连接给subReactor处理,为什么要单独分一个Reactor来处理监听呢?因为像TCP这样需要经过3次握手才能建立连接,这个建立连接的过程也是要耗时间和资源的,单独分一个Reactor来处理,可以提高性能。

3、Reactor模式实战

      设计一个reactor主线程,负责等待epoll_wait消息,依据消息句柄的结果,使用条件变量通知相应的线程处理,其中,一个Acceptor线程负责新增socket连接,M个工作线程处理接收到的消息,

代码如下:

/**   
  *@desc:用reactor模式练习服务器程序,main.cpp
  *@author: zhangyl
  *@date:   2016.11.23
  */  
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>    //for htonl() and htons()
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <signal.h>    //for signal()
#include <pthread.h>
#include <semaphore.h>
#include <list>
#include <errno.h>
#include <time.h>
#include <sstream>
#include <iomanip>     //for std::setw()/setfill()
#include <stdlib.h>  

#define WORKER_THREAD_NUM   5  
#define min(a, b) ((a <= b) ? (a) : (b))   
int g_epollfd = 0;
bool g_bStop = false;
int g_listenfd = 0;
pthread_t g_acceptthreadid = 0;
pthread_t g_threadid[WORKER_THREAD_NUM] = { 0 };  
pthread_cond_t g_acceptcond;
pthread_mutex_t g_acceptmutex;  
pthread_cond_t g_cond /*= PTHREAD_COND_INITIALIZER*/;  
pthread_mutex_t g_mutex /*= PTHREAD_MUTEX_INITIALIZER*/;  
pthread_mutex_t g_clientmutex;  
std::list<int> g_listClients;  
void prog_exit(int signo)
{  
  ::signal(SIGINT, SIG_IGN);  
  ::signal(SIGKILL, SIG_IGN);  
  ::signal(SIGTERM, SIG_IGN);  

  std::cout << "program recv signal " << signo
            << " to exit." << std::endl;  

  g_bStop = true;  

  ::epoll_ctl(g_epollfd, EPOLL_CTL_DEL, g_listenfd, NULL);  

  //TODO: 是否需要先调用shutdown()一下?  
  ::shutdown(g_listenfd, SHUT_RDWR);  
  ::close(g_listenfd);  
  ::close(g_epollfd);  

  ::pthread_cond_destroy(&g_acceptcond);  
  ::pthread_mutex_destroy(&g_acceptmutex);  

  ::pthread_cond_destroy(&g_cond);  
  ::pthread_mutex_destroy(&g_mutex);  

  ::pthread_mutex_destroy(&g_clientmutex);
}  
bool create_server_listener(const char* ip, short port)
{  
  g_listenfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);  
  if (g_listenfd == -1)  
      return false;  

  int on = 1;  
  ::setsockopt(g_listenfd, SOL_SOCKET, SO_REUSEADDR,
               (char *)&on, sizeof(on));  
  ::setsockopt(g_listenfd, SOL_SOCKET, SO_REUSEPORT,
               (char *)&on, sizeof(on));  

  struct sockaddr_in servaddr;  
  memset(&servaddr, 0, sizeof(servaddr));   
  servaddr.sin_family = AF_INET;  
  servaddr.sin_addr.s_addr = inet_addr(ip);  
  servaddr.sin_port = htons(port);  
  if (::bind(g_listenfd, (sockaddr *)&servaddr,sizeof(servaddr)) == -1)  
      return false;  

  if (::listen(g_listenfd, 50) == -1)  
      return false;  

  g_epollfd = ::epoll_create(1);  
  if (g_epollfd == -1)  
      return false;  

  struct epoll_event e;  
  memset(&e, 0, sizeof(e));  
  e.events = EPOLLIN | EPOLLRDHUP;  
  e.data.fd = g_listenfd;  
  if (::epoll_ctl(g_epollfd, EPOLL_CTL_ADD, g_listenfd, &e) == -1)  
      return false;  

  return true;
}  
void release_client(int clientfd)
{  
  if (::epoll_ctl(g_epollfd, EPOLL_CTL_DEL, clientfd, NULL) == -1)  
      std::cout << "release client socket failed as call epoll_ctl failed"
                << std::endl;  

  ::close(clientfd);
}  
void* accept_thread_func(void* arg)
{     
  while (!g_bStop)  
  {  
      ::pthread_mutex_lock(&g_acceptmutex);  
      ::pthread_cond_wait(&g_acceptcond, &g_acceptmutex);  
      //::pthread_mutex_lock(&g_acceptmutex);  

      //std::cout << "run loop in accept_thread_func" << std::endl;  

      struct sockaddr_in clientaddr;  
      socklen_t addrlen;  
      int newfd = ::accept(g_listenfd,
                           (struct sockaddr *)&clientaddr, &addrlen);  
      ::pthread_mutex_unlock(&g_acceptmutex);  
      if (newfd == -1)  
          continue;  

      std::cout << "new client connected: "
                << ::inet_ntoa(clientaddr.sin_addr) << ":"
                << ::ntohs(clientaddr.sin_port) << std::endl;  

      //将新socket设置为non-blocking  
      int oldflag = ::fcntl(newfd, F_GETFL, 0);  
      int newflag = oldflag | O_NONBLOCK;  
      if (::fcntl(newfd, F_SETFL, newflag) == -1)  
      {  
          std::cout << "fcntl error, oldflag =" << oldflag
                    << ", newflag = " << newflag << std::endl;  
          continue;  
      }  

      struct epoll_event e;  
      memset(&e, 0, sizeof(e));  
      e.events = EPOLLIN | EPOLLRDHUP | EPOLLET;  
      e.data.fd = newfd;  
      if (::epoll_ctl(g_epollfd, EPOLL_CTL_ADD, newfd, &e) == -1)  
      {  
          std::cout << "epoll_ctl error, fd =" << newfd << std::endl;  
      }  
  }  

  return NULL;
}  

void* worker_thread_func(void* arg)
{     
  while (!g_bStop)  
  {  
      int clientfd;  
      ::pthread_mutex_lock(&g_clientmutex);  
      while (g_listClients.empty())  
          ::pthread_cond_wait(&g_cond, &g_clientmutex);  
      clientfd = g_listClients.front();  
      g_listClients.pop_front();    
      pthread_mutex_unlock(&g_clientmutex);  

      //gdb调试时不能实时刷新标准输出,用这个函数刷新标准输出,使信息在屏幕上实时显示出来  
      std::cout << std::endl;  

      std::string strclientmsg;  
      char buff[256];  
      bool bError = false;  
      while (true)  
      {  
          memset(buff, 0, sizeof(buff));  
          int nRecv = ::recv(clientfd, buff, 256, 0);  
          if (nRecv == -1)  
          {  
              if (errno == EWOULDBLOCK)  
                  break;  
              else  
              {  
                  std::cout << "recv error, client disconnected, fd = "
                            << clientfd << std::endl;  
                  release_client(clientfd);  
                  bError = true;  
                  break;  
              }  

          }  
          //对端关闭了socket,这端也关闭。  
          else if (nRecv == 0)  
          {  
              std::cout << "peer closed, client disconnected, fd = "
                        << clientfd << std::endl;  
              release_client(clientfd);  
              bError = true;  
              break;  
          }  

          strclientmsg += buff;  
      }  

      //出错了,就不要再继续往下执行了  
      if (bError)  
          continue;  

      std::cout << "client msg: " << strclientmsg;  

      //将消息加上时间标签后发回  
      time_t now = time(NULL);  
      struct tm* nowstr = localtime(&now);  
      std::ostringstream ostimestr;  
      ostimestr << "[" << nowstr->tm_year + 1900 << "-"   
                << std::setw(2) << std::setfill('0')
                << nowstr->tm_mon + 1 << "-"   
                << std::setw(2) << std::setfill('0')
                << nowstr->tm_mday << " "  
                << std::setw(2) << std::setfill('0')
                << nowstr->tm_hour << ":"   
                << std::setw(2) << std::setfill('0')
                << nowstr->tm_min << ":"   
                << std::setw(2) << std::setfill('0')
                << nowstr->tm_sec << "]server reply: ";  

      strclientmsg.insert(0, ostimestr.str());  

      while (true)  
      {  
          int nSent = ::send(clientfd, strclientmsg.c_str(), 
                             strclientmsg.length(), 0);  
          if (nSent == -1)  
          {  
              if (errno == EWOULDBLOCK)  
              {  
                  ::sleep(10);  
                  continue;  
              }  
              else  
              {  
                  std::cout << "send error, fd = "
                            << clientfd << std::endl;  
                  release_client(clientfd);  
                  break;  
              }  

          }            

          std::cout << "send: " << strclientmsg;  
          strclientmsg.erase(0, nSent);  

          if (strclientmsg.empty())  
              break;  
      }  
  }  

  return NULL;
}  
void daemon_run()
{  
  int pid;  
  signal(SIGCHLD, SIG_IGN);  
  //1)在父进程中,fork返回新创建子进程的进程ID;  
  //2)在子进程中,fork返回0;  
  //3)如果出现错误,fork返回一个负值;  
  pid = fork();  
  if (pid < 0)  
  {  
      std:: cout << "fork error" << std::endl;  
      exit(-1);  
  }  
  //父进程退出,子进程独立运行  
  else if (pid > 0) {  
      exit(0);  
  }  
  //之前parent和child运行在同一个session里,parent是会话(session)的领头进程,  
  //parent进程作为会话的领头进程,如果exit结束执行的话,那么子进程会成为孤儿进程,并被init收养。  
  //执行setsid()之后,child将重新获得一个新的会话(session)id。  
  //这时parent退出之后,将不会影响到child了。  
  setsid();  
  int fd;  
  fd = open("/dev/null", O_RDWR, 0);  
  if (fd != -1)  
  {  
      dup2(fd, STDIN_FILENO);  
      dup2(fd, STDOUT_FILENO);  
      dup2(fd, STDERR_FILENO);  
  }  
  if (fd > 2)  
      close(fd);  
 }  

int main(int argc, char* argv[])
{    
  short port = 0;  
  int ch;  
  bool bdaemon = false;  
  while ((ch = getopt(argc, argv, "p:d")) != -1)  
  {  
      switch (ch)  
      {  
      case 'd':  
          bdaemon = true;  
          break;  
      case 'p':  
          port = atol(optarg);  
          break;  
      }  
  }  

  if (bdaemon)  
      daemon_run();  


  if (port == 0)  
      port = 12345;  

  if (!create_server_listener("0.0.0.0", port))  
  {  
      std::cout << "Unable to create listen server: ip=0.0.0.0, port="
                << port << "." << std::endl;  
      return -1;  
  }  


  //设置信号处理  
  signal(SIGCHLD, SIG_DFL);  
  signal(SIGPIPE, SIG_IGN);  
  signal(SIGINT, prog_exit);  
  signal(SIGKILL, prog_exit);  
  signal(SIGTERM, prog_exit);  

  ::pthread_cond_init(&g_acceptcond, NULL);  
  ::pthread_mutex_init(&g_acceptmutex, NULL);  

  ::pthread_cond_init(&g_cond, NULL);  
  ::pthread_mutex_init(&g_mutex, NULL);  

  ::pthread_mutex_init(&g_clientmutex, NULL);  

  ::pthread_create(&g_acceptthreadid, NULL, accept_thread_func, NULL);  
  //启动工作线程  
  for (int i = 0; i < WORKER_THREAD_NUM; ++i)  
  {  
      ::pthread_create(&g_threadid[i], NULL, worker_thread_func, NULL);  
  }  

  while (!g_bStop)  
  {         
      struct epoll_event ev[1024];  
      int n = ::epoll_wait(g_epollfd, ev, 1024, 10);  
      if (n == 0)  
          continue;  
      else if (n < 0)  
      {  
          std::cout << "epoll_wait error" << std::endl;  
          continue;  
      }  

      int m = min(n, 1024);  
      for (int i = 0; i < m; ++i)  
      {  
          //通知接收连接线程接收新连接  
          if (ev[i].data.fd == g_listenfd)  
              pthread_cond_signal(&g_acceptcond);  
          //通知普通工作线程接收数据  
          else  
          {                 
              pthread_mutex_lock(&g_clientmutex);                
              g_listClients.push_back(ev[i].data.fd);  
              pthread_mutex_unlock(&g_clientmutex);  
              pthread_cond_signal(&g_cond);  
              //std::cout << "signal" << std::endl;  
          }  

      }  

  }  

  return 0;
} 

     将以上代码改成纯C++11版本,使用CMake编译,为了支持编译必须加上这-std=c++11:

    CMakeLists.txt代码如下:

cmake_minimum_required(VERSION 2.8)  
PROJECT(myreactorserver)  
AUX_SOURCE_DIRECTORY(./ SRC_LIST)
SET(EXECUTABLE_OUTPUT_PATH ./)  
ADD_DEFINITIONS(-g -W -Wall -Wno-deprecated
                -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64
                -DAC_HAS_INFO -DAC_HAS_WARNING -DAC_HAS_ERROR 
                -DAC_HAS_CRITICAL -DTIXML_USE_STL
                -DHAVE_CXX_STDHEADERS ${CMAKE_CXX_FLAGS}
                -std=c++11)  
INCLUDE_DIRECTORIES(  ./  )
LINK_DIRECTORIES(  ./  )  
set(  main.cpp  myreator.cpp  )  
ADD_EXECUTABLE(myreactorserver ${SRC_LIST})  
TARGET_LINK_LIBRARIES(myreactorserver pthread)  

myreactor.h文件

/**
@desc: myreactor头文件, myreactor.h
@author: zhangyl
@date: 2016.12.03
 */
#ifndef __MYREACTOR_H__
#define __MYREACTOR_H__  
#include <list>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>  
#define WORKER_THREAD_NUM   5  
class CMyReactor
{
public:  
  CMyReactor();  
  ~CMyReactor();  

  bool init(const char* ip, short nport);  
  bool uninit();  

  bool close_client(int clientfd);  

  static void* main_loop(void* p);  
private:  
  //no copyable  
  CMyReactor(const CMyReactor& rhs);  
  CMyReactor& operator = (const CMyReactor& rhs);  

  bool create_server_listener(const char* ip, short port);  

  static void accept_thread_proc(CMyReactor* pReatcor);  
  static void worker_thread_proc(CMyReactor* pReatcor);  
private:  
  //C11语法可以在这里初始化  
  int                          m_listenfd = 0;  
  int                          m_epollfd  = 0;  
  bool                         m_bStop    = false;  

  std::shared_ptr<std::thread> m_acceptthread;  
  std::shared_ptr<std::thread> m_workerthreads[WORKER_THREAD_NUM];  

  std::condition_variable      m_acceptcond;  
  std::mutex                   m_acceptmutex;  

  std::condition_variable      m_workercond ;  
  std::mutex                   m_workermutex;  

  std::list<int>                 m_listClients;
};  
#endif //!__MYREACTOR_H__ 

myreactor.cpp文件

/**
*@desc: myreactor实现文件, myreactor.cpp
*@author: zhangyl
*@date: 2016.12.03
*/  #include "myreactor.h"
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>  //for htonl() and htons()
#include <fcntl.h>
#include <sys/epoll.h>
#include <list>
#include <errno.h>
#include <time.h>
#include <sstream>
#include <iomanip>   //for std::setw()/setfill()
#include <unistd.h>  
#define min(a, b) ((a <= b) ? (a) : (b))  
CMyReactor::CMyReactor()
{  
  //m_listenfd = 0;  
  //m_epollfd = 0;  
  //m_bStop = false;
}  
CMyReactor::~CMyReactor()
{  
}  
bool CMyReactor::init(const char* ip, short nport)
{  
  if (!create_server_listener(ip, nport))  
  {  
      std::cout << "Unable to bind: " << ip
                << ":" << nport << "." << std::endl;  
      return false;  
  }  


  std::cout << "main thread id = " << std::this_thread::get_id()
            << std::endl;  

  //启动接收新连接的线程  
  m_acceptthread.reset(new std::thread(CMyReactor::accept_thread_proc, this));  

  //启动工作线程  
  for (auto& t : m_workerthreads)  
  {  
      t.reset(new std::thread(CMyReactor::worker_thread_proc, this));  
  }  


  return true;
}  
bool CMyReactor::uninit()
{  
  m_bStop = true;  
  m_acceptcond.notify_one();  
  m_workercond.notify_all();  

  m_acceptthread->join();  
  for (auto& t : m_workerthreads)  
  {  
      t->join();  
  }  

  ::epoll_ctl(m_epollfd, EPOLL_CTL_DEL, m_listenfd, NULL);  

  //TODO: 是否需要先调用shutdown()一下?  
  ::shutdown(m_listenfd, SHUT_RDWR);  
  ::close(m_listenfd);  
  ::close(m_epollfd);  

  return true;
}  
bool CMyReactor::close_client(int clientfd)
{  
  if (::epoll_ctl(m_epollfd, EPOLL_CTL_DEL, clientfd, NULL) == -1)  
  {  
      std::cout << "close client socket failed as call epoll_ctl failed"
                << std::endl;  
      //return false;  
  }  


  ::close(clientfd);  

  return true;
}  

void* CMyReactor::main_loop(void* p)
{  
  std::cout << "main thread id = "
            << std::this_thread::get_id() << std::endl;  

  CMyReactor* pReatcor = static_cast<CMyReactor*>(p);  

  while (!pReatcor->m_bStop)  
  {  
      struct epoll_event ev[1024];  
      int n = ::epoll_wait(pReatcor->m_epollfd, ev, 1024, 10);  
      if (n == 0)  
          continue;  
      else if (n < 0)  
      {  
          std::cout << "epoll_wait error" << std::endl;  
          continue;  
      }  

      int m = min(n, 1024);  
      for (int i = 0; i < m; ++i)  
      {  
          //通知接收连接线程接收新连接  
          if (ev[i].data.fd == pReatcor->m_listenfd)  
              pReatcor->m_acceptcond.notify_one();  
          //通知普通工作线程接收数据  
          else  
          {  
              {  
                  std::unique_lock<std::mutex> guard(pReatcor->m_workermutex);  
                  pReatcor->m_listClients.push_back(ev[i].data.fd);  
              }  

              pReatcor->m_workercond.notify_one();  
              //std::cout << "signal" << std::endl;  
          }// end if  

      }// end for-loop  
  }// end while  

  std::cout << "main loop exit ..." << std::endl;  

  return NULL;
}  
void CMyReactor::accept_thread_proc(CMyReactor* pReatcor)
{  
  std::cout << "accept thread, thread id = "
            << std::this_thread::get_id() << std::endl;  

  while (true)  
  {  
      int newfd;  
      struct sockaddr_in clientaddr;  
      socklen_t addrlen;  
      {  
          std::unique_lock<std::mutex> guard(pReatcor->m_acceptmutex);  
          pReatcor->m_acceptcond.wait(guard);  
          if (pReatcor->m_bStop)  
              break;  

          //std::cout << "run loop in accept_thread_proc" << std::endl;  

          newfd = ::accept(pReatcor->m_listenfd,
                            (struct sockaddr *)&clientaddr, &addrlen);  
      }  
      if (newfd == -1)  
          continue;  

      std::cout << "new client connected: "
                << ::inet_ntoa(clientaddr.sin_addr) << ":"      
                << ::ntohs(clientaddr.sin_port) << std::endl;  

      //将新socket设置为non-blocking  
      int oldflag = ::fcntl(newfd, F_GETFL, 0);  
      int newflag = oldflag | O_NONBLOCK;  
      if (::fcntl(newfd, F_SETFL, newflag) == -1)  
      {  
          std::cout << "fcntl error, oldflag =" << oldflag
                    << ", newflag = " << newflag << std::endl;  
          continue;  
      }  

      struct epoll_event e;  
      memset(&e, 0, sizeof(e));  
      e.events = EPOLLIN | EPOLLRDHUP | EPOLLET;  
      e.data.fd = newfd;  
      if (::epoll_ctl(pReatcor->m_epollfd, 
          EPOLL_CTL_ADD, newfd, &e) == -1)  
      {  
          std::cout << "epoll_ctl error, fd =" << newfd << std::endl;  
      }  
  }  

  std::cout << "accept thread exit ..." << std::endl;
}  
void CMyReactor::worker_thread_proc(CMyReactor* pReatcor)
{  
  std::cout << "new worker thread, thread id = "
            << std::this_thread::get_id() << std::endl;  

  while (true)  
  {  
      int clientfd;  
      {  
          std::unique_lock<std::mutex> guard(pReatcor->m_workermutex);  
          while (pReatcor->m_listClients.empty())  
          {  
              if (pReatcor->m_bStop)  
              {  
                  std::cout << "worker thread exit ..." << std::endl;  
                  return;  
              }  

              pReatcor->m_workercond.wait(guard);  
          }  

          clientfd = pReatcor->m_listClients.front();  
          pReatcor->m_listClients.pop_front();  
      }  

      //gdb调试时不能实时刷新标准输出,用这个函数刷新标准输出,使信息在屏幕上实时显示出来  
      std::cout << std::endl;  

      std::string strclientmsg;  
      char buff[256];  
      bool bError = false;  
      while (true)  
      {  
          memset(buff, 0, sizeof(buff));  
          int nRecv = ::recv(clientfd, buff, 256, 0);  
          if (nRecv == -1)  
          {  
              if (errno == EWOULDBLOCK)  
                  break;  
              else  
              {  
                  std::cout << "recv error, client disconnected, fd = "
                            << clientfd << std::endl;  
                  pReatcor->close_client(clientfd);  
                  bError = true;  
                  break;  
              }  

          }  
          //对端关闭了socket,这端也关闭。  
          else if (nRecv == 0)  
          {  
              std::cout << "peer closed, client disconnected, fd = "
                        << clientfd << std::endl;  
              pReatcor->close_client(clientfd);  
              bError = true;  
              break;  
          }  

          strclientmsg += buff;  
      }  

      //出错了,就不要再继续往下执行了  
      if (bError)  
          continue;  

      std::cout << "client msg: " << strclientmsg;  

      //将消息加上时间标签后发回  
      time_t now = time(NULL);  
      struct tm* nowstr = localtime(&now);  
      std::ostringstream ostimestr;  
      ostimestr << "[" << nowstr->tm_year + 1900 << "-"  
          << std::setw(2) << std::setfill('0') << nowstr->tm_mon + 1 << "-"  
          << std::setw(2) << std::setfill('0') << nowstr->tm_mday << " "  
          << std::setw(2) << std::setfill('0') << nowstr->tm_hour << ":"  
          << std::setw(2) << std::setfill('0') << nowstr->tm_min << ":"  
          << std::setw(2) << std::setfill('0') << nowstr->tm_sec << "]server reply: ";  

      strclientmsg.insert(0, ostimestr.str());  

      while (true)  
      {  
          int nSent = ::send(clientfd, strclientmsg.c_str(), 
                             strclientmsg.length(), 0);  
          if (nSent == -1)  
          {  
              if (errno == EWOULDBLOCK)  
              {  
                  std::this_thread::sleep_for(std::chrono::milliseconds(10));  
                  continue;  
              }  
              else  
              {  
                  std::cout << "send error, fd = "
                            << clientfd << std::endl;  
                  pReatcor->close_client(clientfd);  
                  break;  
              }  

          }  

          std::cout << "send: " << strclientmsg;  
          strclientmsg.erase(0, nSent);  

          if (strclientmsg.empty())  
              break;  
      }  
  }
}  
bool CMyReactor::create_server_listener(const char* ip, short port)
{  
  m_listenfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);  
  if (m_listenfd == -1)  
      return false;  

  int on = 1;  
  ::setsockopt(m_listenfd, SOL_SOCKET, SO_REUSEADDR,
              (char *)&on, sizeof(on));  
  ::setsockopt(m_listenfd, SOL_SOCKET, SO_REUSEPORT,
              (char *)&on, sizeof(on));  

  struct sockaddr_in servaddr;  
  memset(&servaddr, 0, sizeof(servaddr));  
  servaddr.sin_family = AF_INET;  
  servaddr.sin_addr.s_addr = inet_addr(ip);  
  servaddr.sin_port = htons(port);  
  if (::bind(m_listenfd, (sockaddr *)&servaddr, 
       sizeof(servaddr)) == -1)  
      return false;  

  if (::listen(m_listenfd, 50) == -1)  
      return false;  

  m_epollfd = ::epoll_create(1);  
  if (m_epollfd == -1)  
      return false;  

  struct epoll_event e;  
  memset(&e, 0, sizeof(e));  
  e.events = EPOLLIN | EPOLLRDHUP;  
  e.data.fd = m_listenfd;  
  if (::epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_listenfd, &e) == -1)  
      return false;  

  return true;
}  

main.cpp文件

/**  
 *@desc:   用reactor模式练习服务器程序 
 *@author: zhangyl 
 *@date:   2016.12.03 
 */  

#include <iostream>  
#include <signal.h>     //for signal()  
#include<unistd.h>  
#include <stdlib.h>       //for exit()  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include "myreactor.h"  

CMyReactor g_reator;  

void prog_exit(int signo)  
{  
    std::cout << "program recv signal " << signo
              << " to exit." << std::endl;   

    g_reator.uninit();  
}  

void daemon_run()  
{  
    int pid;  
    signal(SIGCHLD, SIG_IGN);  
    //1)在父进程中,fork返回新创建子进程的进程ID;  
    //2)在子进程中,fork返回0;  
    //3)如果出现错误,fork返回一个负值;  
    pid = fork();  
    if (pid < 0)  
    {  
        std:: cout << "fork error" << std::endl;  
        exit(-1);  
    }  
    //父进程退出,子进程独立运行  
    else if (pid > 0)
   {  
        exit(0);  
    }  
    //之前parent和child运行在同一个session里,parent是会话(session)的领头进程,  
    //parent进程作为会话的领头进程,如果exit结束执行的话,那么子进程会成为孤儿进程,并被init收养。  
    //执行setsid()之后,child将重新获得一个新的会话(session)id。  
    //这时parent退出之后,将不会影响到child了。  
    setsid();  
    int fd;  
    fd = open("/dev/null", O_RDWR, 0);  
    if (fd != -1)  
    {  
        dup2(fd, STDIN_FILENO);  
        dup2(fd, STDOUT_FILENO);  
        dup2(fd, STDERR_FILENO);  
    }  
    if (fd > 2)  
        close(fd);  
}  


int main(int argc, char* argv[])  
{    
    //设置信号处理  
    signal(SIGCHLD, SIG_DFL);  
    signal(SIGPIPE, SIG_IGN);  
    signal(SIGINT, prog_exit);  
    signal(SIGKILL, prog_exit);  
    signal(SIGTERM, prog_exit);  

    short port = 0;  
    int ch;  
    bool bdaemon = false;  
    while ((ch = getopt(argc, argv, "p:d")) != -1)  
    {  
        switch (ch)  
        {  
        case 'd':  
            bdaemon = true;  
            break;  
        case 'p':  
            port = atol(optarg);  
            break;  
        }  
    }  

    if (bdaemon)  
        daemon_run();  


    if (port == 0)  
        port = 12345;  


    if (!g_reator.init("0.0.0.0", 12345))  
        return -1;  

    g_reator.main_loop(&g_reator);  

    return 0;  
}  

参考:

1、什么是reactor模式?

2、Reactor模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值