accept(..) 似乎正在修改我给它的文件描述符参数
accept(...) seems to be modifying the file descriptor parameter I give it
这是我的代码的一部分,对于我尝试使用 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);
- 使用VerQueryValue检索应用程序的文件描述
- 如何在C/C++中用FD_set Unix设置套接字文件描述符
- I2C 文件描述符上的 I2C 总线可写/可读标志
- 许多文件描述符在调用sys_clone时
- AMQP-CPP >处理程序中的错误文件描述符
- 如何使用 WINAPI 和 C++ 提取可执行文件的文件描述?
- 正在等待在非阻塞文件描述符上长时间运行ioctl
- 有没有适用于Windows.lib文件的GNU二进制文件描述符(BFD)
- 如何强制文件描述符缓冲我的输出
- 如何从 boost::asio::ssl::stream<boost::asio::ip::tcp::socket> 获取本机套接字文件描述器?
- 哪个更适合从C++写入敏感的日志文件,在文件描述符上写()或文件上的fprintf()?
- 将 select() 与非基于文件描述符的输入一起使用
- accept(..) 似乎正在修改我给它的文件描述符参数
- 使用 Select 多路复用未命名的管道和其他文件描述符
- 提升 ASIO 绑定:错误的文件描述符
- 使用文件描述符移动对象
- 无法从零MQ ZMQ_SERVER套接字中获取文件描述符
- read() 上的不同行为取决于写入不可写内存时表示文件、匿名管道或套接字的文件描述符
- 我可以在不使用 FIOCANCEL 的情况下关闭 VxWorks 中的文件描述符吗?
- 将 Boost Asio 与 ZeroMQ 集成,文件描述符错误?