gpt4 book ai didi

sockets - select() + 非阻塞 write() 是否可以在阻塞管道或套接字上实现?

转载 作者:行者123 更新时间:2023-12-02 20:55:04 27 4
gpt4 key购买 nike

情况是我有一个阻塞管道或套接字fd,我想在不阻塞的情况下write(),所以我做了一个select() 首先,但这仍然不能保证 write() 不会阻塞。

这是我收集的数据。即使 select() 表明写入是可能的,写入超过 PIPE_BUF 字节可能会阻塞。但是,最多写入 PIPE_BUF 字节似乎不会阻塞实践,但 POSIX spec 没有强制要求.

这仅指定原子行为。 Python(!) documentation指出:

Files reported as ready for writing by select(), poll() or similar interfaces in this module are guaranteed to not block on a write of up to PIPE_BUF bytes. This value is guaranteed by POSIX to be at least 512.

在下面的测试程序中,将 BUF_BYTES 设置为 100000 来阻止Linux、FreeBSD 或 Solaris 上的 write() 成功选择后。我假设命名管道与匿名管 Prop 有类似的行为。

不幸的是,阻塞套接字也会发生同样的情况。称呼main() 中的 test_socket() 并使用较大的 BUF_BYTES (100000 很好这里也)。目前尚不清楚是否存在像这样的安全缓冲区大小PIPE_BUF 用于套接字。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>

#define BUF_BYTES PIPE_BUF
char buf[BUF_BYTES];

int
probe_with_select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds)
{
struct timeval timeout = {0, 0};
int n_found = select(nfds, readfds, writefds, exceptfds, &timeout);
if (n_found == -1) {
perror("select");
}
return n_found;
}

void
check_if_readable(int fd)
{
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
printf("select() for read on fd %d returned %d\n",
fd, probe_with_select(fd + 1, &fdset, 0, 0));
}

void
check_if_writable(int fd)
{
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
int n_found = probe_with_select(fd + 1, 0, &fdset, 0);
printf("select() for write on fd %d returned %d\n", fd, n_found);
/* if (n_found == 0) { */
/* printf("sleeping\n"); */
/* sleep(2); */
/* int n_found = probe_with_select(fd + 1, 0, &fdset, 0); */
/* printf("retried select() for write on fd %d returned %d\n", */
/* fd, n_found); */
/* } */
}

void
test_pipe(void)
{
int pipe_fds[2];
size_t written;
int i;
if (pipe(pipe_fds)) {
perror("pipe failed");
_exit(1);
}
printf("read side pipe fd: %d\n", pipe_fds[0]);
printf("write side pipe fd: %d\n", pipe_fds[1]);
for (i = 0; ; i++) {
printf("i = %d\n", i);
check_if_readable(pipe_fds[0]);
check_if_writable(pipe_fds[1]);
written = write(pipe_fds[1], buf, BUF_BYTES);
if (written == -1) {
perror("write");
_exit(-1);
}
printf("written %d bytes\n", written);
}
}

void
serve()
{
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;

listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));

serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(5000);

bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

listen(listenfd, 10);

connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);

sleep(10);
}

int
connect_to_server()
{
int sockfd = 0, n = 0;
struct sockaddr_in serv_addr;

if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
exit(-1);
}

memset(&serv_addr, '0', sizeof(serv_addr));

serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(5000);

if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
perror("inet_pton");
exit(-1);
}

if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("connect");
exit(-1);
}

return sockfd;
}

void
test_socket(void)
{
if (fork() == 0) {
serve();
} else {
int fd;
int i;
int written;
sleep(1);
fd = connect_to_server();

for (i = 0; ; i++) {
printf("i = %d\n", i);
check_if_readable(fd);
check_if_writable(fd);
written = write(fd, buf, BUF_BYTES);
if (written == -1) {
perror("write");
_exit(-1);
}
printf("written %d bytes\n", written);
}
}
}

int
main(void)
{
test_pipe();
/* test_socket(); */
}

最佳答案

除非您希望在 select() 表示 fd 已准备好写入时一次发送一个字节,否则实际上无法知道您将能够发送多少字节,即使如此,理论上也是可能的(至少在文档中(如果不是在现实世界中),让 select 表示它已准备好进行写入,然后条件在 select() 和 write() 之间的时间内发生变化。

非阻塞发送是这里的解决方案,如果您从使用 write() 更改为 send(),则无需将文件描述符更改为非阻塞模式即可以非阻塞形式发送一条消息。您需要更改的唯一一件事是将 MSG_DONTWAIT 标志添加到发送调用中,这将使发送非阻塞,而无需更改套接字的属性。在这种情况下,您甚至根本不需要使用 select(),因为 send() 调用将为您提供返回码中所需的所有信息 - 如果您得到的返回码为 -1 并且 errno 为 EAGAIN或 EWOULDBLOCK 那么您就知道无法再发送了。

关于sockets - select() + 非阻塞 write() 是否可以在阻塞管道或套接字上实现?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30066812/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com