void epollEngine::MessageLoop() { const static int maxevents = 1024; struct epoll_event events[1024]; int nfds, i; BaseSocket * s; while(m_running) { nfds = epoll_wait(epoll_fd, events, maxevents, 1000); for(i = 0; i < nfds; ++i) { s = fds[events[i].data.fd]; if(s == 0) { printf("epoll returned invalid fd %u\n", events[i].data.fd); continue; } if(events[i].events & EPOLLHUP || events[i].events & EPOLLERR) { s->OnError(errno); } else if(events[i].events & EPOLLIN) { s->OnRead(0); if(s->Writable() && !s->m_writeLock) { ++s->m_writeLock; WantWrite(s); } } else if(events[i].events & EPOLLOUT) { s->OnWrite(0); if(!s->Writable()) { /* change back to read state */ struct epoll_event ev; memset(&ev, 0, sizeof(epoll_event)); ev.data.fd = s->GetFd(); ev.events = EPOLLIN | EPOLLET; epoll_ctl(epoll_fd, EPOLL_CTL_MOD, s->GetFd(), &ev); --s->m_writeLock; } } } } }
void SelectEngine::Shutdown() { m_running = false; m_socketLock.Acquire(); BaseSocket * s; for(map<int, BaseSocket*>::iterator itr = m_sockets.begin(); itr != m_sockets.end(); ) { s = itr->second; ++itr; s->Delete(); } m_socketLock.Release(); /* shutdown the socket deleter */ sSocketDeleter.Kill(); /* delete the socket deleter */ delete SocketDeleter::getSingletonPtr(); /* delete us */ delete this; }
int main(int argc, char *argv[]) { int port = 8888; string ip = "127.0.0.1"; BaseSocket toserver; #ifdef TEST_TCP if (!toserver.Create()) { cerr << "client create error." << endl; return -1; } string host; int peer; if (!toserver.Connect(ip, port)) { cerr << "connect to server error." << endl; return -1; } if (!toserver.GetPeer(host, peer)) { cerr << "server get peer error." << endl; //return -1; } cout << "connected to " << host << " : " << peer << endl; cout << toserver.Send("this is the client sending msg...") << endl; cout << toserver.Recv() << endl; return 0; #endif #ifdef TEST_UDP if (!toserver.Create(true)) { cerr << "client create error." << endl; return -1; } toserver.SendTo("udp test from client...", ip, port); cout << toserver.RecvFrom(ip, port) << endl; return 0; #endif }
void kqueueEngine::MessageLoop() { const static int maxevents = MAX_DESCRIPTORS; timespec timeout; timeout.tv_sec = 1; timeout.tv_nsec = 0; struct kevent events[MAX_DESCRIPTORS]; struct kevent ev; int nfds, i; BaseSocket * s; while(m_running) { nfds = kevent(kq, 0, 0, events, maxevents, &timeout); for(i = 0; i < nfds; ++i) { s = fds[events[i].ident]; if(s == 0) { printf("kqueue returned invalid fd %u\n", events[i].ident); continue; } if(events[i].flags & EV_EOF || events[i].flags & EV_ERROR) { s->OnError(events[i].fflags); continue; } if(events[i].filter == EVFILT_READ) { s->OnRead(0); if(s->Writable() && !s->m_writeLock) { ++s->m_writeLock; WantWrite(s); } } else if(events[i].filter == EVFILT_WRITE) { s->OnWrite(0); if(!s->Writable()) { --s->m_writeLock; EV_SET(&ev, s->GetFd(), EVFILT_READ, EV_ADD, 0, 0, NULL); if(kevent(kq, &ev, 1, NULL, 0, NULL) < 0) printf("!! could not modify kevent (to read) for fd %u\n", s->GetFd()); } else { EV_SET(&ev, s->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL); if(kevent(kq, &ev, 1, NULL, 0, NULL) < 0) printf("!! could not modify kevent (to write) for fd %u\n", s->GetFd()); } } else { printf("Unknwon filter: %u Fflags: %u, fd: %u, flags: %u\n", events[i].filter, events[i].fflags, events[i].ident, events[i].flags); } } } }
void pollEngine::MessageLoop() { const static int maxevents = 1024; int nfds, i; BaseSocket * s; timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 50000; while(m_running) { nfds = poll(poll_events, highest_fd + 1, 50); if(!m_running) return; if(nfds > 0) { for(i = 0; i <= highest_fd; ++i) { if(poll_events[i].fd > 0) { s = fds[i]; if(s == 0) { printf("poll returned invalid fd %u\n", i); poll_events[i].fd = -1; poll_events[i].events = 0; poll_events[i].revents = 0; continue; } if(poll_events[i].revents & POLLERR || poll_events[i].revents & POLLHUP) { s->Disconnect(); continue; } if(poll_events[i].revents & POLLIN) { s->OnRead(0); /* are we writable now? */ if(s->Writable() && !s->m_writeLock) { ++s->m_writeLock; poll_events[i].events = POLLOUT; } } if(poll_events[i].revents & POLLOUT) { s->OnWrite(0); /* are we readable now? */ if(!s->Writable()) { --s->m_writeLock; poll_events[i].events = POLLIN; } } } } } } }
// tunnel data from fdfrom to fdto (unfiltered) // return false if throughput larger than target throughput bool FDTunnel::tunnel(BaseSocket &sockfrom, BaseSocket &sockto, bool twoway, off_t targetthroughput, bool ignore) { if (targetthroughput == 0) { #ifdef DGDEBUG std::cout << "No data expected, tunnelling aborted." << std::endl; #endif return true; } #ifdef DGDEBUG if (targetthroughput < 0) std::cout << "Tunnelling without known content-length" << std::endl; else std::cout << "Tunnelling with content length " << targetthroughput << std::endl; #endif if ((sockfrom.bufflen - sockfrom.buffstart) > 0) { #ifdef DGDEBUG std::cout << "Data in fdfrom's buffer; sending " << (sockfrom.bufflen - sockfrom.buffstart) << " bytes" << std::endl; #endif if (!sockto.writeToSocket(sockfrom.buffer + sockfrom.buffstart, sockfrom.bufflen - sockfrom.buffstart, 0, 120, false)) throw std::runtime_error(std::string("Can't write to socket: ") + strerror(errno)); throughput += sockfrom.bufflen - sockfrom.buffstart; sockfrom.bufflen = 0; sockfrom.buffstart = 0; } int maxfd, rc, fdfrom, fdto; fdfrom = sockfrom.getFD(); fdto = sockto.getFD(); maxfd = fdfrom > fdto ? fdfrom : fdto; // find the maximum file // descriptor. As Linux normally allows each process // to have up to 1024 file descriptors, maxfd // prevents the kernel having to look through all // 1024 fds each fdSet could contain char buff[32768]; // buffer for the input timeval timeout; // timeval struct timeout.tv_sec = 120; // modify the struct so its a 120 sec timeout timeout.tv_usec = 0; fd_set fdSet; // file descriptor set FD_ZERO(&fdSet); // clear the set FD_SET(fdto, &fdSet); // add fdto to the set FD_SET(fdfrom, &fdSet); // add fdfrom to the set timeval t; // we need a 2nd copy used later fd_set inset; // we need a 2nd copy used later fd_set outset; // we need a 3rd copy used later bool done = false; // so we get past the first while while (!done && (targetthroughput > -1 ? throughput < targetthroughput : true)) { done = true; // if we don't make a sucessful read and write this // flag will stay true and so the while() will exit inset = fdSet; // as select() can modify the sets we need to take t = timeout; // a copy each time round and use that if (ignore && !twoway) FD_CLR(fdto, &inset); if (selectEINTR(maxfd + 1, &inset, NULL, NULL, &t) < 1) { break; // an error occured or it timed out so end while() } if (FD_ISSET(fdfrom, &inset)) // fdfrom is ready to be read from { if (targetthroughput > -1) // we have a target throughput - only read in the exact amount of data we've been told to // plus 2 bytes to "solve" an IE post bug with multipart/form-data forms: // adds an extra CRLF on certain requests, that it doesn't count in reported content-length rc = sockfrom.readFromSocket(buff, (((int)sizeof(buff) < ((targetthroughput - throughput)/*+2*/)) ? sizeof(buff) : (targetthroughput - throughput)/* + 2*/), 0, 0, false); else rc = sockfrom.readFromSocket(buff, sizeof(buff), 0, 0, false); // read as much as is available if (rc < 0) { break; // an error occured so end the while() } else if (!rc) { done = true; // none received so pipe is closed so flag it } else // some data read { throughput += rc; // increment our counter used to log outset = fdSet; // take a copy to work with FD_CLR(fdfrom, &outset); // remove fdfrom from the set // as we are only interested in writing to fdto t = timeout; // take a copy to work with if (selectEINTR(fdto + 1, NULL, &outset, NULL, &t) < 1) { break; // an error occured or timed out so end while() } if (FD_ISSET(fdto, &outset)) // fdto ready to write to { if (!sockto.writeToSocket(buff, rc, 0, 0, false)) // write data { break; // was an error writing } done = false; // flag to say data still to be handled } else { break; // should never get here } } } if (FD_ISSET(fdto, &inset)) // fdto is ready to be read from { if (!twoway) { // since HTTP works on a simple request/response basis, with no explicit // communications from the client until the response has been completed // (just TCP cruft, which is of no interest to us here), tunnels only // need to be one way. As soon as the client tries to send data, break // the tunnel, as it will be a new request, possibly to an entirely // different webserver. This is important for proper filtering when // persistent connection support gets implemented. PRA 2005-11-14 #ifdef DGDEBUG std::cout << "fdto is sending data; closing tunnel. (This must be a persistent connection.)" << std::endl; #endif break; } // read as much as is available rc = sockto.readFromSocket(buff, sizeof(buff), 0, 0, false); if (rc < 0) { break; // an error occured so end the while() } else if (!rc) { done = true; // none received so pipe is closed so flag it break; } else // some data read { outset = fdSet; // take a copy to work with FD_CLR(fdto, &outset); // remove fdto from the set // as we are only interested in writing to fdfrom t = timeout; // take a copy to work with if (selectEINTR(fdfrom + 1, NULL, &outset, NULL, &t) < 1) { break; // an error occured or timed out so end while() } if (FD_ISSET(fdfrom, &outset)) // fdfrom ready to write to { if (!sockfrom.writeToSocket(buff, rc, 0, 0, false)) // write data { break; // was an error writing } done = false; // flag to say data still to be handled } else { break; // should never get here } } } } #ifdef DGDEBUG if ((throughput >= targetthroughput) && (targetthroughput > -1)) std::cout << "All expected data tunnelled. (expected " << targetthroughput << "; tunnelled " << throughput << ")" << std::endl; else std::cout <<"Tunnel closed."<< std::endl; #endif return (targetthroughput > -1) ? (throughput <= targetthroughput) : true; }