2

I'm studing C++ and sockets from Bgnet guide. I made a simple server which only sends a text for who connects using child processes and select() ways. Now I'm trying to create one using poll() and std::vector to store sockets.

The problem is that it compiles normally, but when I run it and try to connect from browser, unlike the others I have made, this send no response and browser stay forever waiting.

I checked many times my code and found no errors or logic problems. The source's here:

#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <vector>
#include <algorithm>
#include <netdb.h>
#include <cstdio>
#include <string.h>
#include <unistd.h>

#define PORT "19456"
#define BACKLOG 10

using namespace std;

int main() {

    sockaddr_storage remote_addr;
    socklen_t remote_addr_size;
    addrinfo settings, *result_info, *info;
    int srv_socket, conn_socket, gai_result, send_status, yes = 1;
    vector <pollfd> fds;
    vector <pollfd>::iterator fd;
    vector <pollfd>::const_iterator last_fd;
    pollfd listener_fd, new_fd;

    memset(&settings, 0, sizeof settings);
    settings.ai_family = AF_INET;
    settings.ai_socktype = SOCK_STREAM;
    settings.ai_flags = AI_PASSIVE;

    if((gai_result = getaddrinfo(NULL, PORT, &settings, &result_info)) != 0) {
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai_result));
            return 1;
    }

    for(info = result_info; info != NULL; info = info->ai_next) {
            if((srv_socket = socket(info->ai_family, info->ai_socktype, info->ai_protocol)) == -1) {
                    perror("Error on socket()");
                    continue;
            }

            if(setsockopt(srv_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
                    close(srv_socket);
                    perror("Error on setsockopt()");
                    return 1;
            }

            if(bind(srv_socket, info->ai_addr, info->ai_addrlen) == -1) {
                    close(srv_socket);
                    perror("Error on bind()");
                    continue;
            }

            break;
    }

    if(info == NULL) {
            fprintf(stderr, "Failed on all bind()");
            return 1;
    }

    freeaddrinfo(result_info);

    if(listen(srv_socket, BACKLOG) == -1) {
            perror("Error on listen()");
            return 1;
    }

    listener_fd.fd = srv_socket;
    listener_fd.events = POLLIN;
    fds.push_back(listener_fd);

    while(1) {

            pollfd fds_array[fds.size()];
            copy(fds.begin(), fds.end(), fds_array);

            poll(fds_array, (nfds_t) fds.size(), 0);

            if(listener_fd.revents & POLLIN) {
                    remote_addr_size = sizeof remote_addr;

                    if((conn_socket = accept(srv_socket, (sockaddr *) &remote_addr, &remote_addr_size)) == -1) {
                            close(srv_socket);
                            perror("Error on accept()");
                            return 1;
                    }

                    new_fd.fd = conn_socket;
                    new_fd.events = POLLOUT;
                    fds.push_back(new_fd);
            }

            for(fd = fds.begin() + 1, last_fd = fds.end(); fd != last_fd; ++fd) {
                    if(fd->revents & POLLOUT) {
                            send_status = send(fd->fd, "Test", 4, 0);
                            close(fd->fd);
                            fds.erase(fd);
                            if(send_status == -1) {
                                    perror("Error on send()");
                                    return 1;
                            }

                    }
            }
    }

    return 0;

}

I searched and found no related asks, in all found, the problem was with syntax.

Can someone help me to find the error with this? I will be very grateful.

EDIT


I have changed some things following Ben's answers. Now the only problems are connection reset, which I think be caused by closing socket without read request and other is the server using more than 80% of CPU. With select() or child processes ways this not occurs. The current source:

#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <vector>
#include <algorithm>
#include <netdb.h>
#include <cstdio>
#include <string.h>
#include <unistd.h>

#define PORT "19456"
#define BACKLOG 10

using namespace std;

int main() {

    sockaddr_storage remote_addr;
    socklen_t remote_addr_size;
    addrinfo settings, *result_info, *info;
    int srv_socket, conn_socket, gai_result, send_status, fd_index, fds_num, yes = 1;
    vector <pollfd> fds;
    pollfd listener_fd, new_fd;

    memset(&settings, 0, sizeof settings);
    settings.ai_family = AF_INET;
    settings.ai_socktype = SOCK_STREAM;
    settings.ai_flags = AI_PASSIVE;

    if((gai_result = getaddrinfo(NULL, PORT, &settings, &result_info)) != 0) {
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai_result));
            return 1;
    }

    for(info = result_info; info != NULL; info = info->ai_next) {
            if((srv_socket = socket(info->ai_family, info->ai_socktype, info->ai_protocol)) == -1) {
                    perror("Error on socket()");
                    continue;
            }

            if(setsockopt(srv_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
                    close(srv_socket);
                    perror("Error on setsockopt()");
                    return 1;
            }

            if(bind(srv_socket, info->ai_addr, info->ai_addrlen) == -1) {
                    close(srv_socket);
                    perror("Error on bind()");
                    continue;
            }

            break;
    }

    if(info == NULL) {
            fprintf(stderr, "Failed on all bind()");
            return 1;
    }

    freeaddrinfo(result_info);

    if(listen(srv_socket, BACKLOG) == -1) {
            perror("Error on listen()");
            return 1;
    }

    listener_fd.fd = srv_socket;
    listener_fd.events = POLLIN;
    fds.push_back(listener_fd);

    while(1) {

            pollfd fds_array[fds.size()];
            copy(fds.begin(), fds.end(), fds_array);

            poll(fds_array, (nfds_t) fds.size(), 0);

            if(fds_array[0].revents & POLLIN) {
                    remote_addr_size = sizeof remote_addr;

                    if((conn_socket = accept(srv_socket, (sockaddr *) &remote_addr, &remote_addr_size)) == -1) {
                            close(srv_socket);
                            perror("Error on accept()");
                            return 1;
                    }

                    new_fd.fd = conn_socket;
                    new_fd.events = POLLOUT;
                    fds.push_back(new_fd);
            }

            for(fd_index = 1, fds_num = fds.size(); fd_index < fds_num; fd_index++) {
                    if(fds_array[fd_index].revents & POLLOUT) {
                            send_status = send(fds_array[fd_index].fd, "Test", 4, 0);
                            close(fds_array[fd_index].fd);
                            fds.erase(fds.begin() + fd_index);
                            if(send_status == -1) {
                                    perror("Error on send()");
                                    return 1;
                            }

                    }
            }
    }

    return 0;

}

What should be the problem now?

Tiago.SR
  • 349
  • 4
  • 16

2 Answers2

1

Your test for whether something is available to accept uses listener_fd which is not modified by poll(). You have to use the copy of it in fds_array which you passed in to poll(). Similarly in your send loop you need to loop over the values in the array you passed to poll().

Ben Jackson
  • 90,079
  • 9
  • 98
  • 150
  • Thanks, I don't know how not seen this. I made some changes however now I'm getting segmentation fault, dump do core. Again, I don't know what is causing this. I updated the original post adding the new code. – Tiago.SR May 12 '13 at 05:15
  • @Tiago.SR `sizeof(array)` is in bytes, not in elements. In this case, `sizeof(pointer)` is the size of the pointer, not the number of elements. Instead save a copy of `fds.size()` before `poll()` – Ben Jackson May 12 '13 at 05:26
  • Thanks man :D I thought C++'s sizeof works equals PHP's sizeof. I assigned `fds.size()` for `fds_num` and segmentation fault stopped. However, now I'm getting connection interrupted/redefined in most connect attempts in Chromium. I think this is because I'm not reading the request and will fix this later. Other problem is that server is using more than 80% of CPU, what not occurs using select() or child processes. This I don't know the reason. – Tiago.SR May 12 '13 at 05:55
0

Regarding the 80% CPU load, I suspect that you're not setting the timeout parameter quite right. From the manual:

Specifying a timeout of zero causes poll() to return immediately, even if no file descriptors are ready.

If your server is working fine otherwise, there's no reason not to put a fairly large number in as the timeout. I use 60s (so 60000ms) on my poll / epoll / etc based systems.

I also don't see the reason that you are copying the array each time. With a std::vector, you should be able to get rid of the fds_array entirely, and just do:

poll( &fds[0], (nfds_t) fds.size(), 60000 );

(Along with corresponding changes to the rest of the code.)

AnthonyFoiani
  • 504
  • 5
  • 7
  • Thanks for reply. About the `timeout` parameter: setting it for 60s will not freeze the server in `poll()` for 60s until the function returns? This will not prevent server from accepting new requests for 60s? About the `&fds[0]` instead of `copy()`: this only sets a pointer for first element. Isn't guaranteed that all `std::vector` elements are in consecutive memory, so using this isn't guaranteed to work all the time. At least, it's what ISO 14882 says. This was changed? – Tiago.SR May 12 '13 at 23:48
  • @Tiago.SR -- `timeout` is the *maximum* time the call will wait before returning *if* nothing else happens. If something happens before the time runs out, it will return immediately. And std::vector uses contiguous storage, so it *is* guaranteed to work (until the vector is modified, at least). See: http://stackoverflow.com/questions/1733143/converting-between-c-stdvector-and-c-array-without-copying – AnthonyFoiani May 13 '13 at 04:21
  • @Tiago.SR -- glad to hear that it's working. If the replies were helpful, please upvote or select as answer. Happy hacking! – AnthonyFoiani May 14 '13 at 00:44