1

我正在学习Bgnet guide中的 C++ 和套接字。我制作了一个简单的服务器,它只为使用子进程和 select() 方式连接的人发送文本。现在我正在尝试使用poll()和 std::vector 创建一个来存储套接字。

问题是它可以正常编译,但是当我运行它并尝试从浏览器连接时,与我所做的其他不同,这不会发送任何响应并且浏览器会永远等待。

我检查了很多次我的代码,没有发现任何错误或逻辑问题。来源在这里:

#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;

}

我搜索并没有发现相关的问题,总的来说,问题出在语法上。

有人可以帮我找出这个错误吗?我将不胜感激。

编辑


根据本的回答,我改变了一些事情。现在唯一的问题是连接重置,我认为这是由于在没有读取请求的情况下关闭套接字引起的,其他是服务器使用超过 80% 的 CPU。使用 select() 或子进程的方式不会发生这种情况。当前来源:

#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;

}

现在应该是什么问题?

4

2 回答 2

1

您测试是否可以accept使用listener_fd未修改的东西poll()。您必须使用fds_array您传入的副本poll()。同样,在您的发送循环中,您需要遍历传递给的数组中的值poll()

于 2013-05-12T04:39:07.430 回答
0

关于 80% CPU 负载,我怀疑您的timeout参数设置不正确。从手册:

指定一个timeout零会 poll()立即返回,即使没有准备好文件描述符。

如果您的服务器工作正常,则没有理由不将相当大的数字作为超时。我在基于 poll / epoll / etc 的系统上使用 60 秒(所以 60000 毫秒)。

我也看不到您每次都复制数组的原因。使用 a std::vector,您应该能够完全摆脱它fds_array,只需执行以下操作:

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

(以及对其余代码的相应更改。)

于 2013-05-12T10:00:28.890 回答