如何用C++的类封装IO多路复用,如select

我最近在复习套接字编程,不过我打算用C++的类给一些操作封装一下

服务器套接字

  class server {
  private:
    int sockfd;
    struct sockaddr_in address;

public: server(const char ip[], unsigned int port); ~server();

public: bool run(int backlog = 5); recept accept(); bool shutdown(int option); bool close(); };

服务器accept产生的套接字 recept(招待)

  class recept {
  private:
    int listen_fd;
  public:
    recept(int fd);
    ~recept();
  public:
    int send(char buffer[], int length);
    int recv(char buffer[], int length);
    bool close();
    bool shutdown(int option);
  };

客户端套接字

  class client {
  private:
    int sockfd;
  public:
    client(int domain = AF_INET);
    ~client();

public: int field(); public: int connect(const char ip[], unsigned int port); int connect(const char path[]); int send(char buffer[], int length); int recv(char buffer[], int length); bool close(); bool shutdown(int option); };

我确定可以正常运行以后,开始着手封装select操作

  class select {
  private:
    map<int, callback> records_read, records_write, records_exception;
    fd_set read_mask, write_mask, exception_mask;
    struct timeval tv;

  public:
    select();
    ~select();

  private:
    int max_count;
    void adjust_count(int fd);
  public:
    bool bind_read(int fd, callback f);
    bool bind_write(int fd, callback f);
    bool bind_exception(int fd, callback f);
    void timeout(int second, int usecond);
    void loop();			// remember to reset time and fd_set

    void config_from(int option, int fd, fd_set & mask); // 想移除一个描述符的时候bind不起作用,只好从返回值下手,这个返回值就是option
  };

我的本意是通过bind系列函数,将一个描述符与一个回调函数(可以是闭包)绑定,当这个描述符发生事件时,调用callback

  bool bind_read(int fd, callback f);
  bool bind_write(int fd, callback f);
  bool bind_exception(int fd, callback f);
最后调用一个`loop()`函数解决

void select::loop() {
  // for reset
  fd_set all_mask;
  FD_ZERO(&all_mask);
  struct timeval __tv = {tv.tv_sec, tv.tv_usec};

while(true) { read_mask = all_mask; write_mask = all_mask; exception_mask = all_mask; tv = __tv;

int n = ::select(max_count + 1, &read_mask, &write_mask, &exception_mask, &tv);
if(n < 0) {
  perror("error in select: ");
  exit(-1);
} else if(n == 0) {
  // timeout
  continue;
}

// n > 0
int fd;
callback f;
int option;
for(pair<const int, callback> & __pair: records_read) {
  fd = __pair.first;
  f = __pair.second;

  if(FD_ISSET(fd, &read_mask)) {
option = f(fd);
config_from(option, fd, read_mask);
  }
}


for(pair<const int, callback> & __pair: records_write) {
  fd = __pair.first;
  f = __pair.second;

  if(FD_ISSET(fd, &write_mask)) {
option = f(fd);
config_from(option, fd, write_mask);
  }

}

for(pair<const int, callback> & __pair: records_exception) {
  fd = __pair.first;
  f = __pair.second;

  if(FD_ISSET(fd, &exception_mask)) {
option = f(fd);
config_from(option, fd, exception_mask);
  }

}

} }

可是我毕竟是一个人,经验不够丰富,也不知道这个设计有没有问题 (其实测试的时候卡住了,代码写得太多,又不好排错)

我不知道有没有开源项目封装过select,或者有哪位大佬尝试过,在这里向各位请教 :slightly_smiling_face:

多路复用是很简单的,难的是在上面的 reactor/worker 抽象。如果想学习的话,推荐看一下 libeventnginx

额,libevent的代码结构怎么看纳, 太多了

可以看下 《 Linux多线程服务端编程》:https://book.douban.com/subject/20471211/

书太贵辣!!!
这个项目有没有开源,仓库地址能发一下吗

太伸手党了吧 :rofl:

就算我找到仓库,也不知道源代码从哪下手好啊 :rofl:
书太贵了,70多大洋

谷歌 “Linux 多线程服务端编程 pdf” 一大堆啊

c++网络库那么还要造轮子啊

建议用boost asio。正在标准化的C++ Networking TS也是基于boost asio的。

这是个很复杂的领域,不建议自己造轮子。