Reputation: 369
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?
Upvotes: 3
Views: 2610
Reputation: 514
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 causespoll()
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.)
Upvotes: 1
Reputation: 93690
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()
.
Upvotes: 2