我正在尝试理解如何将epoll库(Linux)与socket文件描述符结合使用。据我所知,关于epoll的信息很有限。到目前为止,我发现最有用的资源是这个:
https://www.suchprogramming.com/epoll-in-3-easy-steps/
这提供了一个完整的工作示例,展示了如何使用 epoll 与 stdin
文件描述符配合使用。(我在其他地方读到 epoll 不能与 stdin
、stdout
和 stderr
配合使用。我认为这个信息是错误的。)
我尝试使用 MWE 进一步完善。
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <string.h>
#include <unistd.h>
#include <future>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
int thread_function(int accept_sock_fd)
{
char *read_buffer = new char[1024];
unsigned int read_length = 0;
std::string read_string = "";
for(;;)
{
unsigned int t_read_length = read(accept_sock_fd, read_buffer, 1024 - 1);
//std::cout << "t_read_length=" << t_read_length << std::endl;
//std::cout << read_buffer << std::endl;
read_string += std::string(read_buffer);
read_length += t_read_length;
// dodgy ?
// tries to detect end of data using new lines
// but what if client does not send new line char?
if(read_buffer[t_read_length - 1] == '\n')
{
break;
}
}
std::cout << read_string << std::endl;
//std::cout << read_length << std::endl;
//std::cout << read_buffer << std::endl;
std::string status_line = "HTTP/1.0 200 OK\r\n\r\n";
std::string html_lines = "Hello World\r\n\r\n";
std::string write_string = status_line + html_lines;
//std::cout << write_string << std::endl;
unsigned int write_length = write(accept_sock_fd, write_string.data(), write_string.length());
//std::cout << "write_length=" << write_length << std::endl;
close(accept_sock_fd);
delete [] read_buffer;
return 0;
}
int main()
{
// read configuration file
//Config config;
//config.ReadFromFile("config.txt");
//std::cout << config << std::endl;
unsigned int num_threads = 4; //config.GetNumThreads();
///////////////////////////////////////////////////////////////////////////
// init epoll
//
//
int epoll_fd = epoll_create(num_threads);
if(epoll_fd < 0)
{
std::cout << "epoll error" << std::endl;
}
struct epoll_event event;
struct epoll_event *events = new struct epoll_event[num_threads * sizeof(struct epoll_event)];
///////////////////////////////////////////////////////////////////////////
// create socket fd
//
//
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if(sock_fd == 0)
{
std::cout << "Error creating sock_fd" << std::endl;
}
// int setsockopt(sockfd,
struct sockaddr_in server_address;
memset(&server_address, 0, sizeof(struct sockaddr_in));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons(55555 /*config.GetPort()*/);
bind(sock_fd, (struct sockaddr *)&server_address, sizeof(server_address));
if(bind < 0)
{
std::cout << "Error: bind" << std::endl;
}
if(listen(sock_fd, 10) < 0)
{
std::cout << "Error: listen" << std::endl;
}
///////////////////////////////////////////////////////////////////////////
// continue epoll setup
//
//
event.events = EPOLLIN; // | EPOLLPRI | EPOLLERR | EPOLLHUP;
event.data.fd = 0; //client_sock;
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock_fd, &event)) // not sure about this
{
std::cout << "Failed to add epoll" << std::endl;
return 1;
}
for(;;)
{
int n_fd = epoll_wait(epoll_fd, events, num_threads, 0);
if(n_fd < 0)
{
std::cout << "die" << std::endl;
break;
}
for(int i = 0; i < n_fd; ++ i)
{
int fd = events[i].data.fd;
// accpt connection
int accept_sock_fd = 0;
struct sockaddr client_address;
int addrlen = sizeof(client_address);
accept_sock_fd = accept(fd, NULL, NULL); // not sure what goes here
accept_sock_fd = accept(sock_fd, NULL, NULL); // not sure what goes here
if(accept_sock_fd < 0)
{
std::cout << "Error: accept" << std::endl;
}
// do something to handle data on accept_sock_fd
std::future<int> thread_future = std::async(thread_function, accept_sock_fd);
int ret = thread_future.get();
std::cout << ret << std::endl;
// close connection
close(accept_sock_fd);
}
}
delete [] events;
close(sock_fd);
if(close(epoll_fd))
{
std::cout << "error: failed to close epoll fd" << std::endl;
}
return 0;
}
我已经广泛搜索了有关如何将 epoll 与套接字结合使用的进一步信息,但是关于这方面的信息并不多。我想这是一个相当小众的领域。
理想情况下,如果有人知道任何好的资源(甚至一本书),在那里我可以找到更多有关此内容的信息,从而自己解决问题,那就太棒了,否则,希望能提供一些关于如何继续的建议。
我最困惑的是如何监听套接字 fd 上的事件,以及如何接受事件并处理由 accept
返回的新 fd。
请参见上述代码中的这些行:
// correct fd
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, /*the_server_socket_fd*/, &event);
...
// not sure about this one, particularly the waiting time of 0
for(;;)
epoll_wait(epoll_fd, events, /*num_threads*/, 0);
for(i ...)
fd = events[i].data.fd; // what fd is this? what does it mean?
accept(/*something here, but what*/ fd OR the_server_socket_fd ?, NULL, NULL);
// launch thread with retuned value from accept, probably
man epoll
中看到 epoll 与套接字的简单示例,例如:https://man7.org/linux/man-pages/man7/epoll.7.html。 - bartop