Advertisement
Guest User

sockets.h

a guest
Oct 15th, 2015
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.14 KB | None | 0 0
  1. #include <sys/epoll.h>
  2. #include <netdb.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <string>
  6. #include <signal.h>
  7. #include <queue>
  8. #include <map>
  9. #include <set>
  10.  
  11. class SOCKETS {
  12.     public:
  13.     SOCKETS()  {}
  14.     ~SOCKETS() {deinit();}
  15.  
  16.     static const int EPOLL_MAX_EVENTS;
  17.  
  18.     inline bool init(std::string *error) {
  19.         events = nullptr;
  20.         sfd    = -1;
  21.         efd    = -1;
  22.  
  23.         if (sigfillset(&sigset_all) == -1) {
  24.             if (error) error->append("sigfillset failed\n");
  25.             return false;
  26.         }
  27.         if (sigemptyset(&sigset_none) == -1) {
  28.             if (error) error->append("sigemptyset failed\n");
  29.             return false;
  30.         }
  31.         return true;
  32.     }
  33.  
  34.     inline bool open(const char *port, std::string *error) {
  35.         /*static struct sockaddr_in sa_zero;
  36.         struct sockaddr_in sa;
  37.         int x = 1;
  38.         int fd;
  39.  
  40.         if ( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ) {
  41.             printf( "Init_tcp_socket: socket" );
  42.             exit( 1 );
  43.         }
  44.  
  45.         if ( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,(char *) &x, sizeof(x) ) < 0 ) {
  46.             printf( "Init_tcp_socket: SO_REUSEADDR" );
  47.             ::close(fd);
  48.             exit( 1 );
  49.         }
  50.  
  51.         int ppp = atoi(port);
  52.         sa              = sa_zero;
  53.         sa.sin_family   = AF_INET;
  54.         sa.sin_port     = htons( ppp );
  55.  
  56.         if ( bind( fd, (struct sockaddr *) &sa, sizeof(sa) ) < 0 ) {
  57.             printf("Init socket: bind (port %d)", ppp );
  58.             ::close(fd);
  59.             exit(1);
  60.         }
  61.         sfd = fd;*/
  62.         // Uncomment code above and comment out the next line to make SO_REUSEADDR work.
  63.         sfd = create_and_bind(port, error);
  64.         if (sfd == -1) return false;
  65.  
  66.         s = make_socket_non_blocking(sfd, error);
  67.         if (s == -1) return false;
  68.  
  69.         s = ::listen(sfd, SOMAXCONN);
  70.         if (s == -1) return false;
  71.  
  72.         efd = epoll_create1(0);
  73.         if (efd == -1) return false;
  74.  
  75.         event.data.fd = sfd;
  76.         event.events = EPOLLIN|EPOLLET;
  77.         s = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);
  78.         if (s == -1) return false;
  79.  
  80.         // Buffer where events are returned:
  81.         events = (struct epoll_event *) calloc(EPOLL_MAX_EVENTS, sizeof event);
  82.         if (events == nullptr) {
  83.             if (error) error->append("unable to allocate memory for events\n");
  84.             return false;
  85.         }
  86.  
  87.         return true;
  88.     }
  89.  
  90.     inline bool close(std::string *error) {
  91.         if (sfd == -1) return false;
  92.         if (!close(sfd, error)) return false;
  93.         return true;
  94.     }
  95.    
  96.     inline void disconnect(std::string *error) {
  97.         for (auto d : descriptors) {
  98.             if (disconnections.count(d) > 0) continue;
  99.             disconnections.insert(d);
  100.             if (shutdown(d, SHUT_WR) == -1) {
  101.                 if (error) {
  102.                     char buf[64];
  103.                     sprintf(buf, "shutdown(%d, SHUT_WR): ", d);
  104.                     error->append(buf);
  105.                     error->append(strerror(errno));
  106.                     error->append("\n");
  107.                 }
  108.             }
  109.         }
  110.     }
  111.  
  112.     inline bool wait(std::string *error) {    
  113.         if (efd == -1 || !events) {
  114.             if (error) error->append("sockets uninitialized\n");
  115.             return false;
  116.         }
  117.        
  118.         if (!new_descs.empty()) {
  119.             if (error) error->append("new descriptor queue full\n");
  120.             return false;
  121.         }
  122.        
  123.         if (!del_descs.empty()) {
  124.             if (error) error->append("deleted descriptor queue full\n");
  125.             return false;
  126.         }
  127.  
  128.         int n, i;
  129.        
  130.         if ((n = epoll_pwait(efd, events, EPOLL_MAX_EVENTS, -1, &sigset_none)) == -1) {
  131.             if (errno == EINTR) goto Disconnect;
  132.             if (error) {
  133.                 error->append("epoll_pwait: ");
  134.                 error->append(strerror(errno));
  135.                 error->append("\n");
  136.             }
  137.             return false;
  138.         }
  139.  
  140.         new_hosts.clear();
  141.         new_ports.clear();
  142.         new_descs = {};
  143.         del_descs = {};
  144.  
  145.         for (i=0; i<n; i++) {
  146.             if ((  events[i].events & EPOLLERR )
  147. //          ||  (  events[i].events & EPOLLHUP )
  148.             ||  (!(events[i].events & EPOLLIN) )) {
  149.                 int d = events[i].data.fd;
  150.                 // An error has occured on this fd, or the socket is not
  151.                 // ready for reading (why were we notified then?)
  152.                 if (error) {
  153.                     char buf[64];
  154.                     sprintf(buf, "epoll error on descriptor %d\n", d);
  155.                     error->append(buf);
  156.                 }
  157.                 if (!close(d, error)); return false;
  158.                 continue;
  159.             }
  160.             else if (sfd == events[i].data.fd) {
  161.                 // We have a notification on the listening socket, which means one or more incoming connections.
  162.                 while (1) {
  163.                     struct sockaddr in_addr;
  164.                     socklen_t in_len;
  165.                     int infd;
  166.                     char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
  167.  
  168.                     in_len = sizeof in_addr;
  169.                     infd = accept(sfd, &in_addr, &in_len);
  170.                     if (infd == -1) {
  171.                         if ((errno == EAGAIN     )
  172.                         ||  (errno == EWOULDBLOCK)) {
  173.                             break; // We have processed all incoming connections.
  174.                         }
  175.                         else {
  176.                             error->append("accept: ");
  177.                             error->append(strerror(errno));
  178.                             error->append("\n");
  179.                             break;
  180.                         }
  181.                     }
  182.  
  183.                     s = getnameinfo(&in_addr, in_len, hbuf, sizeof hbuf, sbuf, sizeof sbuf, NI_NUMERICHOST|NI_NUMERICSERV);
  184.                     if (s == 0) {
  185.                         new_hosts[infd] = hbuf;
  186.                         new_ports[infd] = sbuf;
  187.                     }
  188.                     else {
  189.                         error->append("getnameinfo: ");
  190.                         error->append(gai_strerror(s));
  191.                         error->append("\n");
  192.                     }
  193.  
  194.                     // Make the incoming socket non-blocking and add it to the list of fds to monitor.
  195.                     s = make_socket_non_blocking(infd, error);
  196.                     if (s == -1) return false;
  197.  
  198.                     event.data.fd = infd;
  199.                     event.events = EPOLLIN|EPOLLET;
  200.                     s = epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event);
  201.                     if (s == -1) {
  202.                         error->append("epoll_ctl: ");
  203.                         error->append(strerror(errno));
  204.                         error->append("\n");
  205.                         return false;
  206.                     }
  207.                    
  208.                     new_descs.push(infd);
  209.                     descriptors.insert(infd);
  210.                 }
  211.                 continue;
  212.             }
  213.             else {
  214.                 // We have data on the fd waiting to be read. Read and
  215.                 // display it. We must read whatever data is available
  216.                 // completely, as we are running in edge-triggered mode
  217.                 // and won't get a notification again for the same
  218.                 // data.
  219.                 int done = 0;
  220.  
  221.                 while (1) {
  222.                     ssize_t count;
  223.                     char buf[512];
  224.  
  225.                     count = read(events[i].data.fd, buf, sizeof buf);
  226.                     if (count == -1) {
  227.                         // If errno == EAGAIN, that means we have read all data. So go back to the main loop.
  228.                         if (errno != EAGAIN) {
  229.                             error->append("read: ");
  230.                             error->append(strerror(errno));
  231.                             error->append("\n");
  232.                             done = 1;
  233.                         }
  234.                         break;
  235.                     }
  236.                     else if (count == 0) {
  237.                         // End of file. The remote has closed the connection.
  238.                         done = 1;
  239.                         break;
  240.                     }
  241.  
  242.                     // Write the buffer to standard output.
  243.                     s = write(1, buf, count);
  244.                     if (s == -1) {
  245.                         error->append("write: ");
  246.                         error->append(strerror(errno));
  247.                         error->append("\n");
  248.                         return false;
  249.                     }
  250.                 }
  251.  
  252.                 if (done) {
  253.                     int fd = events[i].data.fd;
  254.                     // Closing the descriptor will make epoll remove it
  255.                     // from the set of descriptors which are monitored.
  256.                     if (!close(fd, error)) {
  257.                         return false;
  258.                     }
  259.                 }
  260.             }
  261.         }        
  262.        
  263.         Disconnect:
  264.         while (!disconnections.empty()) {
  265.             int d = *disconnections.begin();
  266.             disconnections.erase(d);
  267.             if (descriptors.count(d) > 0) {
  268.                 if (!close(d, error)) return false;
  269.                 del_descs.push(d);
  270.                 descriptors.erase(d);
  271.             }
  272.         }
  273.  
  274.         return true;
  275.     }
  276.    
  277.     inline bool send_to_desc(int d, const char *text, std::string *error) {
  278.         size_t len = strlen(text);
  279.         if (len == 0) return true;
  280.         s = write(d, text, len);
  281.         if (s == -1) {
  282.             if (error) {
  283.                 error->append("write: ");
  284.                 error->append(strerror(errno));
  285.                 error->append("\n");
  286.                 return false;
  287.             }
  288.         }
  289.         return true;
  290.     }
  291.  
  292.     inline int get_new_desc(std::string *host, std::string *port) {
  293.         if (new_descs.empty()) return -1;
  294.         int d = new_descs.front();
  295.         new_descs.pop();
  296.        
  297.         if (host != nullptr) {
  298.             if (new_hosts.find(d) != new_hosts.end()) {
  299.                 host->assign(new_hosts[d]);
  300.                 new_hosts.erase(d);
  301.             }
  302.             else host->assign("unknown");
  303.         }
  304.        
  305.         if (port != nullptr) {
  306.             if (new_ports.find(d) != new_ports.end()) {
  307.                 port->assign(new_ports[d]);
  308.                 new_ports.erase(d);
  309.             }
  310.             else port->assign("unknown");
  311.         }        
  312.        
  313.         return d;
  314.     }
  315.  
  316.     inline int get_del_desc() {
  317.         if (del_descs.empty()) return -1;
  318.         int d = del_descs.front();
  319.         del_descs.pop();
  320.         return d;
  321.     }
  322.  
  323.     private:
  324.     int sfd, s;
  325.     int efd;
  326.     struct epoll_event event;
  327.     struct epoll_event *events;
  328.     sigset_t sigset_all;
  329.     sigset_t sigset_none;
  330.     sigset_t sigset_orig;
  331.     std::queue<int> new_descs; // New descriptors.
  332.     std::queue<int> del_descs; // Deleted descriptors.
  333.     std::map<int, std::string> new_hosts;
  334.     std::map<int, std::string> new_ports;
  335.     std::set<int> descriptors;
  336.     std::set<int> disconnections; // Descriptors to be disconnected.
  337.  
  338.     inline void deinit() {
  339.         if (events) {
  340.             free(events);
  341.             events=nullptr;
  342.         }
  343.     }
  344.    
  345.     inline bool close(int fd, std::string *error) {
  346.         // Blocking all signals before ::close will guarantee
  347.         // that it will not fail having errno set to EINTR.
  348.         printf("Closing descriptor %d.\n", fd);
  349.         if (sigprocmask(SIG_SETMASK, &sigset_all, &sigset_orig) == -1) {
  350.             if (error) {
  351.                 error->append("sigprocmask: ");
  352.                 error->append(strerror(errno));
  353.                 error->append("\n");
  354.             }
  355.             return false;
  356.         }
  357.         int c = ::close(fd);
  358.         int e = errno;
  359.         if (sigprocmask(SIG_SETMASK, &sigset_orig, nullptr) == -1) {
  360.             if (error) {
  361.                 error->append("sigprocmask: ");
  362.                 error->append(strerror(errno));
  363.                 error->append("\n");
  364.             }
  365.             return false;
  366.         }
  367.  
  368.         if (c == -1) {
  369.             if (error) {
  370.                 error->append("close: ");
  371.                 error->append(strerror(e));
  372.                 error->append("\n");
  373.             }
  374.             if (e != EINTR) return false;
  375.         }
  376.  
  377.         if (descriptors.count(fd) > 0) {
  378.             del_descs.push(fd);
  379.             descriptors.erase(fd);
  380.             disconnections.erase(fd);
  381.         }
  382.  
  383.         return true;
  384.     }
  385.  
  386.     inline int create_and_bind(const char *port, std::string *error) {
  387.         struct addrinfo hints;
  388.         struct addrinfo *result, *rp;
  389.         int s, sfd, x;
  390.        
  391.         memset(&hints, 0, sizeof(struct addrinfo));
  392.         hints.ai_family   = AF_UNSPEC;   // Return IPv4 and IPv6 choices
  393.         hints.ai_socktype = SOCK_STREAM; // We want a TCP socket
  394.         hints.ai_flags    = AI_PASSIVE;  // All interfaces
  395.  
  396.         s = getaddrinfo(nullptr, port, &hints, &result);
  397.         if (s != 0) {
  398.             if (error) {
  399.                 error->append("getaddrinfo: ");
  400.                 error->append(gai_strerror(s));
  401.                 error->append("\n");
  402.             }
  403.             return -1;
  404.         }
  405.  
  406.         for (rp = result; rp != nullptr; rp = rp->ai_next) {
  407.             sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
  408.             if (sfd == -1) continue;
  409.  
  410.             if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,(char *) &x, sizeof(x)) == -1) {
  411.                 if (error) {
  412.                     error->append("setsockopt: ");
  413.                     error->append(strerror(errno));
  414.                     error->append("\n");
  415.                 }
  416.             }
  417.             else {
  418.                 s = bind(sfd, rp->ai_addr, rp->ai_addrlen);
  419.                 if (s == 0) break; // We managed to bind successfully!
  420.                 if (error) {
  421.                     char buf[64];
  422.                     sprintf(buf, "bind(%d, ?, %d): ", sfd, rp->ai_addrlen);
  423.                     error->append(buf);
  424.                     error->append(strerror(errno));
  425.                     error->append("\n");
  426.                 }
  427.             }
  428.  
  429.             if (!close(sfd, error)) {
  430.                 sfd = -1;
  431.                 goto Fail;
  432.             }
  433.         }
  434.        
  435.         if (rp == nullptr) sfd = -1;
  436.  
  437.         Fail:
  438.         freeaddrinfo(result);
  439.         return sfd;        
  440.     }
  441.  
  442.     inline int make_socket_non_blocking(int sfd, std::string *error) {
  443.         int flags, s;
  444.  
  445.         flags = fcntl(sfd, F_GETFL, 0);
  446.         if (flags == -1) {
  447.             if (error) {
  448.                 error->append("fcntl: ");
  449.                 error->append(strerror(errno));
  450.                 error->append("\n");
  451.             }
  452.             return -1;
  453.         }
  454.  
  455.         flags |= O_NONBLOCK;
  456.         s = fcntl (sfd, F_SETFL, flags);
  457.         if (s == -1) {
  458.             error->append("fcntl: ");
  459.             error->append(strerror(errno));
  460.             error->append("\n");
  461.             return -1;
  462.         }
  463.  
  464.         return 0;
  465.     }
  466. };
  467.  
  468. const int SOCKETS::EPOLL_MAX_EVENTS = 64;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement