vendredi 13 novembre 2020

When do I consider the writable option in sock_state_cb of C-ares DNS?

The sock_state_cb is function pointer which defines in ares_options. Its signature is as follows:

void (*sock_state_cb)(void *data, ares_socket_t socket_fd, int readable, int writable)

I have no idea that in what scenario the parameter writable will become non-zero. When using ip to serach the domain name? Or when cache the resolving result in local DNS cache? I wrote some code to test. My code is as follows:

namespace {
double getSeconds(struct timeval* tv) {
  if (tv)
    return static_cast<double>(tv->tv_sec) +
           static_cast<double>(tv->tv_usec) / 1000000.0;
  else
    return -1.0;
}

const char* getSocketType(int type) {
  if (type == SOCK_DGRAM)
    return "UDP";
  else if (type == SOCK_STREAM)
    return "TCP";
  else
    return "Unknown";
}

const bool kDebug = false;
}  // namespace

class Resolver : tmuduo::noncopyable {
 public:
  using Callback = std::function<void(const tmuduo::net::InetAddress&)>;
  enum class Option {
    kDNSandHostsFile,
    kDNSonly,
  };

  explicit Resolver(tmuduo::net::EventLoop* loop,
                    Option opt = Option::kDNSandHostsFile): loop_(loop), ctx_(NULL), timerActive_(false) 
  {
    static char lookups[] = "b";
    struct ares_options options;
    int optmask = ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB;
    options.flags = ARES_FLAG_NOCHECKRESP | ARES_FLAG_STAYOPEN | ARES_FLAG_IGNTC;
    options.sock_state_cb = &Resolver::ares_sock_state_callback;
    options.sock_state_cb_data = this;
    optmask |= ARES_OPT_TIMEOUT;
    options.timeout = 2;
    if (opt == Option::kDNSonly) {
      optmask |= ARES_OPT_LOOKUPS;
      options.lookups = lookups;
    }
    int status = ares_init_options(&ctx_, &options, optmask);
    if (status != ARES_SUCCESS) {
      assert(0);
    }
    ares_set_socket_callback(ctx_, &Resolver::ares_sock_create_callback, this);
  }
  ~Resolver(){ ares_destroy(ctx_); }
  bool resolve(tmuduo::StringArg hostname, const Callback& cb);

 private:
  struct QueryData {
    Resolver* owner;
    Callback callback;
    QueryData(Resolver* o, const Callback& cb) : owner(o), callback(cb) {}
  };
  tmuduo::net::EventLoop* loop_;
  ares_channel ctx_;
  bool timerActive_;
  using ChannelList = std::map<int, std::unique_ptr<tmuduo::net::Channel>>;
  ChannelList channels_;
  /* omitted some other code*/
  void onSockStateChange(int sockfd, bool read, bool write) {
    auto it = channels_.find(sockfd);
    assert(it != channels_.end());
    if (read) {
      printf("read function has been call\n");
    } else if(write){
      printf("wirte function has been call\n");
    }else {
      it->second->disableAll();
      it->second->remove();
      channels_.erase(it);
    }
  }
  /* omitted some other code */
  static void ares_host_callback(void* data, int status, int timeous,
                                 struct hostent* hostent){
    QueryData* query = static_cast<QueryData*>(data);
    query->owner->onQueryResult(status, hostent, query->callback);
    delete query;
  }
  static int ares_sock_create_callback(int sockfd, int type, void* data){
    printf("sockfd= %d, type=%s\n",sockfd,getSocketType(type);
    static_cast<Resolver*>(data)->onSockCreate(sockfd, type);
    return 0;
  }
  static void ares_sock_state_callback(void* data, int sockfd, int read,int write){
    printf("sockfd = %d, read = %d, write = %d",sockfd, read, write)
    static_cast<Resolver*>(data)->onSockStateChange(sockfd, read, write);
  }
};

The main file:

void resolveCallback(const string& host, const InetAddress& addr) {
  printf("resolveCallback %s -> %s\n", host.c_str(), addr.toIpPort().c_str());
}
void resolve(Resolver* res, const string& host) {
  res->resolve(host, std::bind(&resolveCallback, host, _1));
}
int main(int argc, char* argv[]){
  ...
  Resolver resolver(&loop, argc == 1 ? Resolver::Option::kDNSonly
                                     : Resolver::Option::kDNSandHostsFile);
  resolve(&resolver, "www.hacker-cube.com");
  resolve(&resolver, "www.baidu.com");
  resolve(&resolver, "www.jianshu.com");
  ...
}

For saving the space, I omitted some Irrelevant code. The code shown above can work perfectly. Its output is :

read function has been call
resolveCallback www.baidu.com -> 14.215.177.39:0
resolveCallback www.jianshu.com -> 47.92.108.93:0
resolveCallback www.hacker-cube.com -> 172.67.137.218:0

So my question is what scenario can lead my program to execute the statement printf("wirte function has been call\n"); in function onSockStateChange?

Aucun commentaire:

Enregistrer un commentaire