LIBEV 非阻滞套接字连续调用回调



我正在使用libev 非阻止插座将请求发送到服务器。我正在使用保持活力,因为我需要通过相同的连接将未来的请求发送到目的地。

行为运行程序,并按照预期获取URL并进行登录到控制台。执行此操作后,请等待,不要推Ctrl C退出程序。

预期应用程序应保持打开状态,因为事件循环正在等待将来的响应,但不应在初始响应后记录任何内容。

实际让应用程序运行。30秒以上后,它将开始一遍又一遍地记录相同的响应,而无需结束。

问题当未发送新请求并且未收到新的响应数据时,为什么libev反复拨打我的回调(example_cb(?我该如何解决?

#include <ev.h>
#include <stdio.h>
#include <iostream>
#include <ctype.h>
#include <cstring>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sstream>
#include <fstream>
#include <string>
using namespace std;
void sendRequest(int sockfd)
{
  puts("------");
  puts("sendRequest() was called");
  stringstream ss;
  ss << "GET /posts/11 HTTP/1.1rn"
      << "Host: jsonplaceholder.typicode.comrn"
      << "Accept: application/jsonrn"
      << "rn";
  string request = ss.str();
  if (send(sockfd, request.c_str(), request.length(), 0) != (int)request.length()) {
    cout << "Error sending request." << endl;
    exit(1);
  }
  cout << "Request sent. No err occured." << endl;
}
static void delay_cb(EV_P_ ev_timer *w, int revents)
{
  puts("------");
  puts("delay_cb() was called");
  sendRequest(3);
}

static void example_cb(EV_P_ ev_io *w, int revents)
{
  puts("------");
  puts("example_cb() was called");
  int sockfd = 3;
  size_t len = 80*1024, nparsed; // response must be <= 80 Kb
  char buf[len];
  ssize_t recved;
  recved = recv(sockfd, &buf, len, 0);
  if (recved < 0) {
    perror("recved was <1");
  }
  // don't process keep alives
  if (buf[0] != '') {
    std::cout << buf << std::endl;
  }
  // clear buf
  buf[0] = '';
  std::cout << "buf after clear attempt: " << buf << std::endl;
}
int example_request()
{
  std::string hostname = "jsonplaceholder.typicode.com";
  int PORT = 80;
  struct sockaddr_in client;
  struct hostent * host = gethostbyname(hostname.c_str());
  if (host == NULL || host->h_addr == NULL) {
    cout << "Error retrieving DNS information." << endl;
    exit(1);
  }
  bzero(&client, sizeof(client));
  client.sin_family = AF_INET;
  client.sin_port = htons( PORT );
  memcpy(&client.sin_addr, host->h_addr, host->h_length);
  // create a socket
  int sockfd = socket(PF_INET, SOCK_STREAM, 0);
  if (sockfd < 0) {
    cout << "Error creating socket." << endl;
    exit(1);
  }
  cout << "Socket created" << endl;
  // enable keep alive
  int val = 1;
  setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof val);
  if (connect(sockfd, (struct sockaddr *)&client, sizeof(client)) < 0) {
    close(sockfd);
    cout << "Could not connect" << endl;
    exit(1);
  }
  cout << "Socket connected" << endl;
  // make non-blocking
  int status = fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
  if (status == -1) {
    perror("ERROR making socket non-blocking");
  }
  std::cout << "Socket set to non-blocking" << std::endl;
  std::cout << "Sockfd is: " << sockfd << std::endl;
  return sockfd;
}
int main(void)
{
  // establish socket connection
  int sockfd = example_request();
  struct ev_loop *loop = EV_DEFAULT;
  ev_io example_watcher;
  ev_io_init(&example_watcher, example_cb, sockfd, EV_READ);
  ev_io_start(loop, &example_watcher);
  // used to send the request 2 sec later
  ev_timer delay_watcher;
  ev_timer_init(&delay_watcher, delay_cb, 2, 0.0);
  ev_timer_start(loop, &delay_watcher);
  ev_run(loop, 0);
  return 0;
}

编辑:已更新的代码,并带有注释中的建议

问题的来源是,您不检查与关闭连接的另一侧相对应的recved == 0条件。发生这种情况时,OS将插座设置为"封闭模式",该模式(至少在Linux下(为始终准备阅读,随后呼叫recv将始终 return 0。/p>

因此,您需要做的是检查该条件,请在文件描述符上调用close(fd);(可能是以前使用shutdown(和关联观察者上的ev_io_stop。如果您想在此时继续,那么您必须打开一个新的插座和eo_io_start新观察者。

最新更新