我在 OS X 上发现了一个关于轮询的有趣的事情。让我在这里说明一下。
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <set>
#include <poll.h>
#define POLL_SIZE 32
int set_nonblock(int fd) {
int flags;
if(-1 == (flags = fcntl(fd, F_GETFL, 0))) {
flags = 0;
}
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
int main() {
int master_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
std::set<int> slave_sockets;
struct sockaddr_in SockAddr;
SockAddr.sin_family = AF_INET;
SockAddr.sin_port = htons(12345);
SockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(master_socket, (struct sockaddr *)(&SockAddr), sizeof(SockAddr));
set_nonblock(master_socket);
listen(master_socket, SOMAXCONN);
struct pollfd set[POLL_SIZE];
set[0].fd = master_socket;
set[0].events = POLL_IN;
while(true) {
unsigned int index = 1;
for(auto iter = slave_sockets.begin(); iter != slave_sockets.end(); iter++) {
set[index].fd = *iter;
set[index].events = POLL_IN;
index++;
}
unsigned int set_size = 1 + slave_sockets.size();
poll(set, set_size, -1);
for(unsigned int i = 0; i < set_size; i++) {
if(set[i].revents & POLL_IN) {
if(i) {
static char buffer[1024];
// This line works as expected.
// int recv_size = read(set[i].fd, buffer, 1024);
// This line sends messages in infinite loop?
// I'm checking this with `telnet 127.0.0.1 12345`
int recv_size = recv(set[i].fd, buffer, 1024, SO_NOSIGPIPE);
if ((recv_size == 0) && (errno != EAGAIN)) {
shutdown(set[i].fd, SHUT_RDWR);
close(set[i].fd);
slave_sockets.erase(set[i].fd);
} else if(recv_size > 0) {
send(set[i].fd, buffer, recv_size, SO_NOSIGPIPE);
}
} else {
int slave_socket = accept(master_socket, 0, 0);
set_nonblock(slave_socket);
slave_sockets.insert(slave_socket);
}
}
}
}
return 0;
}
这个程序是一个用 C++11 编写的基本回显服务器(但它更像是普通的旧 C)。
我在 Linux 上观察到的行为:应用程序启动,接受客户端套接字(我正在使用 telnet 127.0.0.1 12345
),我输入 “ping”
,然后按 RET只返回一个“ping”。
Linux 规范:
1) clang++ -v
clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
2) uname -a
Linux julian-dell 4.4.0-112-generic #135-Ubuntu SMP Fri Jan 19 11:48:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
我在 OS X 上观察到的行为:应用程序启动,接受客户端套接字,我写“ping”,按 RET 并得到无限量的 “pings”
返回。在 OS X 上阻止 poll
的唯一方法是使用 read
而不是 recv
从 socket
读取。
OS X 规范:
1) clang++ -v
Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin17.3.0
Thread model: posix
2) uname -a
Darwin Julians-MacBook-Pro.local 17.3.0 Darwin Kernel Version 17.3.0: Thu Nov 9 18:09:22 PST 2017; root:xnu-4570.31.3~1/RELEASE_X86_64 x86_64
我的问题是:它是 OS X(还有 BSD?)上的错误、预期行为,还是我只是在我的代码中犯了一个错误,Linux 以某种方式忽略了这个错误?而且我不完全理解将 recv
更改为 read
会如何影响 poll
行为 - 它们不是同一个系统调用吗。
最佳答案
SO_NOSIGPIPE 是一个 socket option
不打算传递给 recv()
系列函数。
在您的 OS X 安装中,SO_NOSIGPIPE 设置与 MSG_PEEK 相同的位,这意味着您的 recv()
调用实际上并没有耗尽套接字的数据缓冲区,而只是“查看”可用数据。因此,数据保留在套接字后面,当然,套接字会无限期地轮询可读。
(假设您打算设置 MSG_NOSIGNAL,但是,正如 @melpomene 在评论中指出的那样,此行为属于在套接字上发送数据,而不是接收数据。)
关于c++ - 在 OS X 上轮询和选择奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48468060/