libev + 非阻塞socket 持续调用回调
libev + non-blocking socket continuously invokes callback
我正在使用 libev + 非阻塞套接字向服务器发送请求。我正在使用 Keep Alive,因为我需要通过同一连接向目的地发送未来的请求。
行为
运行 程序,它获取 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.1\r\n"
<< "Host: jsonplaceholder.typicode.com\r\n"
<< "Accept: application/json\r\n"
<< "\r\n";
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] != '[=10=]') {
std::cout << buf << std::endl;
}
// clear buf
buf[0] = '[=10=]';
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 将套接字设置为 "closed mode",其中(至少在 linux 下)总是 准备好读取,随后对 recv 的调用将总是 return 0.
所以您需要做的是检查该条件,在文件描述符上调用 close(fd);
(可能之前使用 shutdown
)并在关联的观察者上调用 ev_io_stop
。如果你想在那一点继续,那么你必须打开一个新的套接字和 eo_io_start
新的观察者。
我正在使用 libev + 非阻塞套接字向服务器发送请求。我正在使用 Keep Alive,因为我需要通过同一连接向目的地发送未来的请求。
行为 运行 程序,它获取 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.1\r\n"
<< "Host: jsonplaceholder.typicode.com\r\n"
<< "Accept: application/json\r\n"
<< "\r\n";
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] != '[=10=]') {
std::cout << buf << std::endl;
}
// clear buf
buf[0] = '[=10=]';
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 将套接字设置为 "closed mode",其中(至少在 linux 下)总是 准备好读取,随后对 recv 的调用将总是 return 0.
所以您需要做的是检查该条件,在文件描述符上调用 close(fd);
(可能之前使用 shutdown
)并在关联的观察者上调用 ev_io_stop
。如果你想在那一点继续,那么你必须打开一个新的套接字和 eo_io_start
新的观察者。