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

accept(...) seems to be modifying the file descriptor parameter I give it

本文关键字:文件 描述 参数 修改 accept      更新时间:2023-10-16

这是我的代码的一部分,对于我尝试使用 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);
}

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

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