提问人:Habib Ayode 提问时间:11/16/2023 更新时间:11/16/2023 访问量:30
当服务器向 tcp 客户端发送数据时,epoll 未收到“EPOLLIN”信号
epoll not receiving `EPOLLIN` signal when server sends data to tcp client
问:
当我创建一个TCP客户端并将其连接到服务器时,它能够检测到它何时连接(通过)。服务器应该向客户端发送消息,但客户端从未通过 epoll 接收到信号。我使用了 Wireshark,可以确认服务器发送了数据,所以我不知道问题出在哪里。EPOLLOUT
代码:
/** A structure representing a TCP client. */
struct tcp_client
{
/** The socket file descriptor. */
socket_t sockfd;
/** The address of the server to connect to. */
struct sockaddr sockaddr;
/** Whether or not the client is polling. */
int listening;
#ifndef _WIN32
/** The polling file descriptor. */
int pfd;
#endif
/** User defined data to be passed to the event callbacks. */
void *data;
/** The callback for when the client has connected to the server. */
void (*on_connect)(struct tcp_client *client);
/** The callback for when the client has received a message from the server. */
void (*on_data)(struct tcp_client *client);
/** The callback for when the client has disconnected from the server. */
void (*on_disconnect)(struct tcp_client *client, int is_error);
};
int tcp_client_main_loop(struct tcp_client *client)
{
/** The client socket should be nonblocking when listening for events. */
socket_set_non_blocking(client->sockfd);
client->listening = 1;
while (client->listening)
{
#ifdef __linux__
int pfd = client->pfd;
struct epoll_event events[1];
int nev = epoll_wait(pfd, events, 1, -1);
if (nev == -1) return netc_error(POLL_FD);
#elif _WIN32
WSAPOLLFD events[1];
events[0].fd = client->sockfd;
events[0].events = POLLIN | POLLOUT | POLLERR | POLLHUP;
int nev = WSAPoll(events, sizeof(events), -1);
if (nev == -1) return netc_error(POLL_FD);
#elif __APPLE__
int pfd = client->pfd;
struct kevent events[1];
int nev = kevent(pfd, NULL, 0, events, 1, NULL);
if (nev == -1) return netc_error(POLL_FD);
#endif
if (client->listening == 0) break;
#ifdef __linux__
struct epoll_event ev = events[0];
socket_t sockfd = ev.data.fd;
if (ev.events & EPOLLIN && client->on_data != NULL)
client->on_data(client);
else if (ev.events & EPOLLOUT)
{
int error = 0;
socklen_t len = sizeof(error);
int result = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
if (result == -1 || error != 0)
return netc_error(HANGUP);
else if (client->on_connect != NULL)
client->on_connect(client);
ev.events = EPOLLOUT;
if (epoll_ctl(pfd, EPOLL_CTL_DEL, sockfd, &ev) == -1) return netc_error(POLL_FD);
}
else if (ev.events & EPOLLERR || ev.events & EPOLLHUP)
{
if (tcp_client_close(client, ev.events & EPOLLERR) != 0) return netc_error(CLOSE);
}
#elif _WIN32
WSAPOLLFD event = events[0];
SOCKET sockfd = event.fd;
if (event.revents & POLLIN && client->on_data != NULL)
client->on_data(client);
else if (event.revents & POLLOUT)
{
int error = 0;
socklen_t len = sizeof(error);
int result = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
if (result == -1 || error != 0)
return netc_error(HANGUP);
else if (client->on_connect != NULL)
client->on_connect(client);
}
else if (event.revents & POLLERR || event.revents & POLLHUP)
{
if (tcp_client_close(client, event.revents & POLLERR) != 0) return netc_error(CLOSE);
};
#elif __APPLE__
struct kevent ev = events[0];
socket_t sockfd = ev.ident;
if (ev.flags & EV_ERROR || ev.flags & EV_EOF)
{
if (tcp_client_close(client, ev.flags & EV_ERROR) != 0) return netc_error(CLOSE);
}
else if (ev.filter == EVFILT_READ && client->on_data != NULL)
client->on_data(client);
else if (ev.filter == EVFILT_WRITE)
{
int error = 0;
socklen_t len = sizeof(error);
int result = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
if (result == -1 || error != 0)
return netc_error(HANGUP);
else if (client->on_connect != NULL) client->on_connect(client);
// deregister event
EV_SET(&ev, sockfd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
if (kevent(pfd, &ev, 1, NULL, 0, NULL) == -1) return netc_error(POLL_FD);
}
#endif
};
return 0;
};
int tcp_client_init(struct tcp_client *client, struct sockaddr addr, int non_blocking)
{
if (client == NULL) return -1;
client->sockaddr = addr;
int protocol = addr.sa_family;
client->sockfd = socket(protocol, SOCK_STREAM, 0); // IPv4, TCP, 0
if (client->sockfd == -1) return netc_error(SOCKET_C);
client->listening = 0;
if (non_blocking == 0) return 0;
if (socket_set_non_blocking(client->sockfd) != 0) return netc_error(FD_CTL);
/** Register events for a nonblocking socket. */
#ifdef __linux__
client->pfd = epoll_create1(0);
if (client->pfd == -1) return netc_error(EVCREATE);
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP;
ev.data.fd = client->sockfd;
if (epoll_ctl(client->pfd, EPOLL_CTL_ADD, client->sockfd, &ev) == -1) return netc_error(POLL_FD);
#elif _WIN32
#elif __APPLE__
client->pfd = kqueue();
if (client->pfd == -1) return netc_error(EVCREATE);
struct kevent ev[2];
int events = 0;
EV_SET(&ev[events++], client->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL);
EV_SET(&ev[events++], client->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
if (kevent(client->pfd, ev, events, NULL, 0, NULL) == -1) return netc_error(POLL_FD);
#endif
return 0;
};
int tcp_client_connect(struct tcp_client *client)
{
socket_t sockfd = client->sockfd;
struct sockaddr addr = client->sockaddr;
socklen_t addrlen = sizeof(addr);
int result = connect(sockfd, &addr, addrlen);
if (result == -1 && errno != EINPROGRESS) return netc_error(CONNECT);
return 0;
};
int tcp_client_send(struct tcp_client *client, char *message, size_t msglen, int flags)
{
socket_t sockfd = client->sockfd;
int result = send(sockfd, message, msglen, flags);
if (result == -1) netc_error(BADSEND);
return result;
};
int tcp_client_receive(struct tcp_client *client, char *message, size_t msglen, int flags)
{
socket_t sockfd = client->sockfd;
int result = recv(sockfd, message, msglen, flags);
if (result == -1) netc_error(BADRECV);
return result;
};
int tcp_client_close(struct tcp_client *client, bool is_error)
{
if (client->on_disconnect != NULL) client->on_disconnect(client, is_error);
socket_t sockfd = client->sockfd;
client->listening = 0;
#ifdef _WIN32
int result = closesocket(sockfd);
#else
int result = close(sockfd);
#endif
if (result == -1) return netc_error(CLOSE);
return 0;
};
我运行了这个程序,希望由于传入数据事件而返回,但即使发送了传入数据,它仍然会阻止。epoll_wait()
答: 暂无答案
评论