accept(...)似乎正在修改我给它的文件描述符参数

时间:2018-06-01 18:22:33

标签: c++ linux sockets networking

这是我的代码的一部分,对于一个简单的echo服务器,我试图使用linux套接字系统调用来编写:

while (true) {
    std::cout << "Fd before accept: " << fd << std::endl;
    if ((current_socket = accept(fd, (struct sockaddr*)&addr, (socklen_t*)&addrlen)) < 0) {
        std::perror("accept");
        exit(EXIT_FAILURE);
    }
    std::cout << "Accepted (fd) = " << fd << std::endl;

    amount_read = read(current_socket, in_buf, 1024);
    std::cout << "Amount read: " << amount_read << std::endl;
    std::cout << "In from client: " << in_buf << std::endl;
    std::cout << "What would you like to say back: ";
    std::getline(std::cin, out_buf);
    send(current_socket, out_buf.c_str(), out_buf.length(), 0);
    std::cout << "Sent." << std::endl;

    std::cout << "fd before looping = " << fd << std::endl;
}

在这段代码之前,我设置了套接字描述符fd,然后,在while循环开始之前:

listen(fd, 4);

我编译它然后运行它,然后运行我编写的客户端程序来向服务器发送消息("Hello from the client")。

我的问题

无论如何,我的问题在于,正如标题所示,对accept(...)的调用正在修改fd的值。首先,我无法理解这是如何可能 - fd不是作为指针传递的,所以怎么可能被函数调用修改?但这一点主要是有意义的 - 这个问题的主要含义将在下一节中介绍。

我确定fd的价值已被更改,因为此处有一些来自服务器的示例输出:

Attempting to listen
Listening
Fd before accept: 3
Accepted (fd) = 0
Amount read: 17
In from client: Hello from client
What would you like to say back: ?
Sent.
fd before looping = 0
Fd before accept: 0
accept: Socket operation on non-socket

正如您所看到的,fd在致电接受之前是 3 ,但在通话之后它变为0

为什么这是一个问题

返回服务器的输出,如上所示:

accept: Socket operation on non-socket

这显然是fd的新值不是有效套接字的结果。

我尝试了什么

还包含listen(...)循环中的while(true) {...}调用,以防万一我需要再次监听每个客户端。我怀疑这会起作用,但事实并非如此。我完全没有想法。

其他无法帮助的问题

我在某个地方发现了一个问题,询问为什么accept(...) 返回更改fd参数)值为0.我从中了解到那个0是一个有效的套接字描述符,但显然它不是。而且,这不应该发生,对吧?

我的问题

总结一下:为什么accept(...)修改了一个非指针参数,我该如何解决这个问题?

完整代码

#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cstdint>
#include <iostream>

#include <sys/socket.h>
#include <netinet/in.h>

int create_socket(uint16_t port, struct sockaddr_in* addr) {
    int server_fd;
    int opt = 1;

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        std::perror("Socket create failed.");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        std::perror("setsockopt failed");
        exit(EXIT_FAILURE);
    }

    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = INADDR_ANY;
    addr->sin_port = htons(port);

    // now bind it to a port
    if (bind(server_fd, (struct sockaddr*)addr, sizeof(*addr)) < 0) {
        std::perror("bind failed");
    }

    return server_fd;
}

int serve(int fd, struct sockaddr_in* addr, int backlog=4) {
    std::string to_send;
    int addrlen = sizeof(*addr), amount_read;
    int current_socket;
    char in_buf[1024];
    std::string out_buf;

    std::cout << "Attempting to listen" << std::endl;
    if (listen(fd, backlog) < 0) {
        std::perror("listen failed");
        exit(EXIT_FAILURE);
    }
    std::cout << "Listening" << std::endl;

    while (true) {
        std::cout << "Fd before accept: " << fd << std::endl;
        if ((current_socket = accept(fd, (struct sockaddr*)&addr, (socklen_t*)&addrlen)) < 0) {
            std::perror("accept");
            exit(EXIT_FAILURE);
        }
        std::cout << "Accepted (fd) = " << fd << std::endl;

        amount_read = read(current_socket, in_buf, 1024);
        std::cout << "Amount read: " << amount_read << std::endl;
        std::cout << "In from client: " << in_buf << std::endl;
        std::cout << "What would you like to say back: ";
        std::getline(std::cin, out_buf);
        send(current_socket, out_buf.c_str(), out_buf.length(), 0);
        std::cout << "Sent." << std::endl;

        std::cout << "fd before looping = " << fd << std::endl;
    }

    return 0;
}

int main(int argc, char* argv[]) {
    struct sockaddr_in addr;
    int fd = create_socket(5555, &addr);

    serve(fd, &addr);

    close(fd);
}

1 个答案:

答案 0 :(得分:2)

您的代码具有未定义的行为,如预期的那样。

int addrlen = sizeof(*addr), amount_read;
...
accept(fd, (struct sockaddr*)&addr, (socklen_t*)&addrlen));

首先,您正在获取addr的地址,该地址已经是指针。你不应该这样做。

另外,我的小水晶球告诉我你在64位平台上,因此你的socklen_t是64位整数。

要解决您的问题,请不要使用指针的地址并取消令人讨厌的演员并使用正确的类型。

socklen_t addrlen = sizeof(*addr);
...
accept(fd, (struct sockaddr*)addr, &addrlen);