C++ C++;被Linux系统和异步线程上的epoll和socket fd弄糊涂了
我试图理解如何将epoll库(Linux)与套接字文件描述符结合使用。据我所知,网上关于epoll的信息有限。到目前为止,我发现的最有用的资源是: 它给出了一个完整的epoll工作示例,但是它与C++ C++;被Linux系统和异步线程上的epoll和socket fd弄糊涂了,c++,linux,sockets,asynchronous,epoll,C++,Linux,Sockets,Asynchronous,Epoll,我试图理解如何将epoll库(Linux)与套接字文件描述符结合使用。据我所知,网上关于epoll的信息有限。到目前为止,我发现的最有用的资源是: 它给出了一个完整的epoll工作示例,但是它与stdin文件描述符一起使用。(我在别处读到EPOL不能与stdin、stdout和stderr一起使用。我认为信息是错误的。) 我已经试着用MWE来建立这个 #include <string> #include <sstream> #include <iostream&g
stdin
文件描述符一起使用。(我在别处读到EPOL不能与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;
}
假设您已经打开了服务器套接字,您可以使用下面的代码。它基于
man epoll
可访问性
//假设您已正确打开listen\u sock
//int listen_sock;
//
int epollfd=epoll_create1(0);
如果(epollfd==-1){
出口(1);
}
结构epoll_事件ev;
ev.events=依泊林;
ev.data.fd=监听_sock;
if(epoll\u ctl(epollfd、epoll\u ctl\u ADD、listen\u sock和ev)=-1){
出口(2);
}
对于(;;){
#定义最大事件64
结构epoll_事件事件[最大事件];
int events_count=epoll_wait(epollfd,events,MAX_events,-1);
如果(n==-1){
出口(3);
}
对于(int n=0;n
使用epoll
与使用poll
没有太大区别。棘手的事情是跟踪哪个客户机拥有epoll返回的文件描述符(如果有很多客户机)
如何使用
epoll
异步处理套接字文件(在本例中为TCP套接字服务器)。
epoll\u create(2)
打开一个epoll文件描述符socket(2)
、bind(2)
和listen(2)
创建TCP套接字epoll\u ctl
+epoll\u ctl\u Add
将主TCP套接字文件描述符添加到epollepoll\u wait
,程序将在epoll\u wait
上睡眠,当有来自受监控文件描述符的事件时,内核将唤醒程序或当达到超时时epoll\u wait
返回的值大于零,则需要确定epoll返回的文件描述符5.1。如果它是主TCP文件描述符,则需要
接受(2)
。然后将由accept(2)
返回的客户端文件描述符添加到带有epoll\u ctl
+epoll\u ctl\u add
的epoll中。其他
5.2。如果它是客户机文件描述符,那么您需要调用recv(2)
并对该客户机执行任何操作
在步骤5.2,如果在事件[i].events
中看到EPOLLHUP
,则客户端已关闭其连接,您需要调用epoll\u ctl
+epoll\u ctl\u DEL
并关闭客户端文件描述符(不调用epoll\u ctl
+epoll\u ctl\u DEL
并关闭客户端文件描述符可能是安全的,但我更愿意先从epoll中删除它)
server.c
code
流程图 流程图可以使它更清楚。
你可以在家尝试的工作示例 服务器.c
/*
* https://stackoverflow.com/questions/66916835/c-confused-by-epoll-and-socket-fd-on-linux-systems-and-async-threads
*/
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义PRERF“(错误号=%d)%s\n”
#定义前置(NUM)NUM、strerror(NUM)
#定义EPOLL\u映射到\u NOP(0u)
#定义EPOLL\u MAP\u SHIFT(1u)/*SHIFT以覆盖保留值MAP\u到NOP*/
结构客户端\u插槽{
使用布尔;
国际客户联合会;
char src_ip[sizeof(“xxx.xxx.xxx.xxx”);
uint16\u t src\u端口;
uint16_t我的索引;
};
结构tcp_状态{
布尔停止;
int-tcp_-fd;
int epoll_fd;
uint16_t客户_c;
struct client_slot clients[10];
/*
*将文件描述符映射到客户端\u插槽数组索引
*注意:我们假设没有大于10000的文件描述符。
*
*你必须在生产中调整这个。
*/
uint32客户地图[10000];
};
静态int my_epoll_add(int epoll_fd、int fd、uint32_t事件)
{
INTERR;
结构epoll_事件;
/*闭嘴*/
memset(&event,0,sizeof(struct-epoll_-event));
event.events=事件;
event.data.fd=fd;
if(epoll\u ctl(epoll\u fd、epoll\u ctl\u ADD、fd和event)<0){
err=errno;
printf(“epoll_ctl(epoll_ctl_ADD):“PRERF,PREAR(err));
返回-1;
}
返回0;
}
静态int my_epoll_delete(int epoll_fd,int fd)
{
INTERR;
if(epoll\u ctl(epoll\u fd,epoll\u ctl\u DEL,fd,NULL)<0){
err=errno;
printf(“epoll_ctl(epoll_ctl_DEL):“PRERF,PREAR(err));
返回-1;
}
返回0;
}
静态常量char*convert\u addr\u ntop(结构sockaddr\u in*addr,char*src\u ip\u buf)
{
INTERR;
常量字符*ret;
in\u addr\u t saddr=addr->sin\u addr.s\u addr;
ret=inet_ntop(AF_inet,&saddr,src_ip_buf,sizeof(“xxx.xxx.xxx.xxx”);
if(ret==NULL){
err=errno;
err=err?err:EINVAL;
printf(“inet_ntop():”PRERF,PREAR(err));
返回NULL;
}
返回ret;
}
静态int-accept\u-new\u客户端(int-tcp\u-fd,struct-tcp\u-state*state)
{
INTERR;
国际客户联合会;
地址中的结构sockaddr\u;
socklen\u t addr\u len=sizeof(addr);
uint16\u t src\u端口;
const char*src_ip;
char src_ip_buf[sizeof(“xxx.xxx.xxx.xxx”);
常量大小\u t客户端\u插槽
// 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
// assume you have opened listen_sock properly
// int listen_sock;
//
int epollfd = epoll_create1(0);
if (epollfd == -1) {
exit(1);
}
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
exit(2);
}
for (;;) {
#define MAX_EVENTS 64
struct epoll_event events[MAX_EVENTS];
int events_count = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (n == -1) {
exit(3);
}
for (int n = 0; n < events_count; ++ n) {
if (events[n].data.fd == listen_sock) {
struct sockaddr_un addr;
socklen_t addrlen;
int socket = accept(listen_sock, (struct sockaddr *) &addr, &addrlen);
}
}
}
ammarfaizi2@integral:~$ for i in {1..10}; do php test.php & done;
[1] 14214
[2] 14215
[3] 14216
[4] 14217
[5] 14218
[6] 14219
[7] 14220
[8] 14221
[9] 14222
[10] 14223
ammarfaizi2@integral:~$
ammarfaizi2@integral:~/ex$ ./server
Initializing epoll_fd...
Creating TCP socket...
Listening on 0.0.0.0:1234...
Entering event loop...
Client 127.0.0.1:60866 has been accepted!
Client 127.0.0.1:60866 sends: "AAAAAAAA"
Client 127.0.0.1:60868 has been accepted!
Client 127.0.0.1:60868 sends: "AAAAAAAA"
Client 127.0.0.1:60870 has been accepted!
Client 127.0.0.1:60872 has been accepted!
Client 127.0.0.1:60870 sends: "AAAAAAAA"
Client 127.0.0.1:60872 sends: "AAAAAAAA"
Client 127.0.0.1:60874 has been accepted!
Client 127.0.0.1:60874 sends: "AAAAAAAA"
Client 127.0.0.1:60876 has been accepted!
Client 127.0.0.1:60878 has been accepted!
Client 127.0.0.1:60878 sends: "AAAAAAAA"
Client 127.0.0.1:60876 sends: "AAAAAAAA"
Client 127.0.0.1:60880 has been accepted!
Client 127.0.0.1:60880 sends: "AAAAAAAA"
Client 127.0.0.1:60882 has been accepted!
Client 127.0.0.1:60882 sends: "AAAAAAAA"
Client 127.0.0.1:60884 has been accepted!
Client 127.0.0.1:60884 sends: "AAAAAAAA"
Client 127.0.0.1:60866 sends: "BBBBBBBB"
Client 127.0.0.1:60868 sends: "BBBBBBBB"
Client 127.0.0.1:60870 sends: "BBBBBBBB"
Client 127.0.0.1:60872 sends: "BBBBBBBB"
Client 127.0.0.1:60874 sends: "BBBBBBBB"
Client 127.0.0.1:60878 sends: "BBBBBBBB"
Client 127.0.0.1:60876 sends: "BBBBBBBB"
Client 127.0.0.1:60880 sends: "BBBBBBBB"
Client 127.0.0.1:60882 sends: "BBBBBBBB"
Client 127.0.0.1:60884 sends: "BBBBBBBB"
Client 127.0.0.1:60866 sends: "CCCCCCCC"
Client 127.0.0.1:60868 sends: "CCCCCCCC"
Client 127.0.0.1:60870 sends: "CCCCCCCC"
Client 127.0.0.1:60872 sends: "CCCCCCCC"
Client 127.0.0.1:60874 sends: "CCCCCCCC"
Client 127.0.0.1:60878 sends: "CCCCCCCC"
Client 127.0.0.1:60876 sends: "CCCCCCCC"
Client 127.0.0.1:60880 sends: "CCCCCCCC"
Client 127.0.0.1:60882 sends: "CCCCCCCC"
Client 127.0.0.1:60884 sends: "CCCCCCCC"
Client 127.0.0.1:60866 has closed its connection
Client 127.0.0.1:60868 has closed its connection
Client 127.0.0.1:60870 has closed its connection
Client 127.0.0.1:60872 has closed its connection
Client 127.0.0.1:60874 has closed its connection
Client 127.0.0.1:60878 has closed its connection
Client 127.0.0.1:60876 has closed its connection
Client 127.0.0.1:60880 has closed its connection
Client 127.0.0.1:60882 has closed its connection
Client 127.0.0.1:60884 has closed its connection