void myEpoll::start() { // sock.Socket(AF_INET, SOCK_STREAM, 0); listen_sock = sock.getListen(); sock.Bind(listen_sock); sock.Listen(listen_sock); // FIXME Я не считаю таки обертки (своя ОО обертка вокруг каждой C-функции) правильными... // Сделать из пары C++ и glibc подобие Python - не является смоцелью, а просто тратой времени // и усложнение кода. // Проще в одном методе (типа этого) позвать сразу все Сишные функции с проверками результата // и выбросом исклчений - так Си код изолируется от C++ кода без потери 'красоты', но // экономией времени. // Если нужны OO сокеты, то есть Boost::asio... Хуже того, ОО сокеты вообще никому не нужны // на самом деле, а нужны уже готовые паттерны типа реактора и проактора ( с уже реализованным // epoll и пр.) ev.events = EPOLLIN; ev.data.fd = listen_sock; epollfd = Epoll_create(MAX_EVENTS); Epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev); }
void myEpoll::loop() { if(!handler) { perror("loop: do_use_fd no defined"); } for (;;) { nfds = Epoll_wait(epollfd, events, MAX_EVENTS, -1); int n; for (n = 0; n < nfds; ++n) { if (events[n].data.fd == listen_sock) { conn_sock = sock.Accept(listen_sock); std::cout << "new client: " << sock.Sock_ntop() << std::endl; sock.setnonblocking(conn_sock); write(conn_sock, hello, strlen(hello) ); //!!aghtung send helloworld!! // do_use_fd(conn_sock); //! ololo del ev.events = EPOLLIN | EPOLLET; ev.data.fd = conn_sock; if( Epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) < 0) { perror("loop: Epoll_ctl"); } } else { // by analogy with True branch, epoll ok printf("READY\n"); //! do use fd (*handler) (events[n].data.fd); // FIXME да, звать прикладную логику через хендлер - очень хорошее решение. // Примерно то, что делает Boost::asio и, может быть, libevent // Хотя... Не всегда это нужно... // Суть в том, что для маленьких проектов важна скорость разработки без // потери качества (легкости, надежности и производительности) кода. // Такие же generic-решения хороши для намного больших проектов. // Предугадываться же какие части будут использоватсья в другом проекти // или расширяться в существующем - это не всегда тревиальный вопрос. } } } }
void Server::StringServerByEpollET() { int connfd, epollfd, maxindex; struct epoll_event ev; struct epoll_event events[MAX_EVENTS]; maxindex = -1; const int listenfd = m_ListenFd; epollfd = Epoll_create(MAX_EVENTS); memset(&ev, 0, sizeof(struct epoll_event)); memset(events, 0, sizeof(struct epoll_event) * MAX_EVENTS); ev.events = EPOLLIN | EPOLLET; ev.data.fd = listenfd; SetNonBlocking(listenfd); Epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev); for (;;) { int i, j, nready; /* * Need to use the original epoll_wait(), rather * than the wrapped Epoll_wait(), the same reason * as select() above */ nready = epoll_wait(epollfd, events, MAX_EVENTS, -1); if ((nready == -1) && (errno == EINTR) && (m_ExitFlag == 1)) break; else if ((nready == -1) && (errno == EINTR) && (m_ExitFlag == 0)) continue; else if (nready == -1) { std::cout << "epoll_wait error: "; std::cout << strerror(errno) << std::endl; exit(-1); } for (i = 0; i < nready; i++) { /* new connection request */ if (events[i].data.fd == listenfd && events[i].events & EPOLLIN) { sockaddr_in cliaddr; socklen_t clilen = sizeof(cliaddr); // need to use the original 'accept' to handle EWOULDBLOCK if ((connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen)) < 0) { if (connfd == EWOULDBLOCK) continue; else { std::cout << "accept error: "; std::cout << strerror(errno) << std::endl; return; } } std::cout << "a connection has been established on fd: " << connfd << std::endl; if (m_ClientCount >= MAX_EVENTS) { std::cout << "too many clients" << std::endl; Close(connfd); continue; } for (j = 0; j < MAX_EVENTS; j++) { if (m_ClientFds[j] == -1) { m_ClientFds[j] = connfd; m_ClientsBuffers[j] = new char[BUFFER_SIZE]; m_ClientCount++; break; } } if (j > maxindex) maxindex = j; ev.events = EPOLLIN | EPOLLOUT | EPOLLET; ev.data.fd = connfd; SetNonBlocking(connfd); Epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev); } else { char* buf; int sockfd; ssize_t nread, nwrite; // pointers for buffer control char *pbufi, *pbufo; /* check all clients for data */ for (j = 0; j <= maxindex; j++) { if (m_ClientFds[j] == -1) continue; sockfd = m_ClientFds[j]; if (sockfd == events[i].data.fd) { buf = m_ClientsBuffers[j]; pbufi = pbufo = buf; break; } } // read event on client[j] if (events[i].events & EPOLLIN) { // need to use the original 'read' to handle EWOULDBLOCK if ((nread = read(sockfd, pbufi, (buf + BUFFER_SIZE - pbufi))) < 0) { if (errno != EWOULDBLOCK) { std::cout << "read socket error: "; std::cout << strerror(errno) << std::endl; // terminate return; } } // handle 'EOF' on sockfd else if (nread == 0) { Epoll_ctl(epollfd, EPOLL_CTL_DEL, sockfd, &ev); std::cout << "client has closed connection" << std::endl; Close(m_ClientFds[j]); m_ClientFds[j] = -1; delete [] m_ClientsBuffers[j]; m_ClientsBuffers[j] = NULL; m_ClientCount--; } // normal state else pbufi += nread; } // write event on client[j] if (events[i].events & EPOLLOUT && (pbufi - pbufo > 0)) { int bytestowrite = pbufi - pbufo; // need to use the original 'write' to handle EWOULDBLOCK if ((nwrite = write(sockfd, pbufo, bytestowrite)) < 0) { if (errno != EWOULDBLOCK) { std::cout << "write socket error: "; std::cout << strerror(errno) << std::endl; // terminate return; } } else { pbufo += nwrite; if(pbufo == pbufi) pbufi = pbufo = buf; } } } } } // exiting Close(epollfd); }
void Server::StringServerByEpollLT() { int connfd, epollfd, maxindex; struct epoll_event ev; struct epoll_event events[MAX_EVENTS]; maxindex = -1; const int listenfd = m_ListenFd; epollfd = Epoll_create(MAX_EVENTS); memset(&ev, 0, sizeof(struct epoll_event)); memset(events, 0, sizeof(struct epoll_event) * MAX_EVENTS); ev.events = EPOLLIN; ev.data.fd = listenfd; Epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev); for (;;) { int i, j, nready; /* * Need to use the original epoll_wait(), rather * than the wrapped Epoll_wait(), the same reason * as select() above */ nready = epoll_wait(epollfd, events, MAX_EVENTS, -1); if ((nready == -1) && (errno == EINTR) && (m_ExitFlag == 1)) break; else if ((nready == -1) && (errno == EINTR) && (m_ExitFlag == 0)) continue; else if (nready == -1) { std::cout << "epoll_wait error: "; std::cout << strerror(errno) << std::endl; exit(-1); } for (i = 0; i < nready; i++) { /* new connection request */ if (events[i].data.fd == listenfd) { sockaddr_in cliaddr; socklen_t clilen = sizeof(cliaddr); connfd = Accept(listenfd, (struct sockaddr*)&cliaddr, &clilen); std::cout << "a connection has been established on fd: " << connfd << std::endl; if (m_ClientCount >= MAX_EVENTS) { std::cout << "too many clients" << std::endl; Close(connfd); continue; } for (j = 0; j < MAX_EVENTS; j++) { if (m_ClientFds[j] == -1) { m_ClientFds[j] = connfd; m_ClientsBuffers[j] = new char[BUFFER_SIZE]; m_ClientCount++; break; } } if (j > maxindex) maxindex = j; ev.events = EPOLLIN | EPOLLET; ev.data.fd = connfd; Epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev); } else { char* buf; int sockfd; ssize_t nread; /* check all clients for data */ for (j = 0; j <= maxindex; j++) { if (m_ClientFds[j] == -1) continue; sockfd = m_ClientFds[j]; if (sockfd == events[i].data.fd) { buf = m_ClientsBuffers[j]; break; } } if ((nread = Read(sockfd, buf, BUFFER_SIZE)) == 0) { Epoll_ctl(epollfd, EPOLL_CTL_DEL, sockfd, &ev); std::cout << "client has closed connection" << std::endl; Close(m_ClientFds[j]); m_ClientFds[j] = -1; delete [] m_ClientsBuffers[j]; m_ClientsBuffers[j] = NULL; m_ClientCount--; } else if(nread > 0) Write(sockfd, buf, nread); } } } // exiting Close(epollfd); }
myEpoll::~myEpoll() { // Epoll_ctl(epollfd, EPOLL_CTL_DEL, 0, NULL); Epoll_ctl(epollfd, EPOLL_CTL_DEL, listen_sock, &ev); close(epollfd); }