gpt4 book ai didi

c - 与阻塞select()一起使用时,非阻塞套接字真的是非阻塞的吗?

转载 作者:行者123 更新时间:2023-12-03 11:56:58 24 4
gpt4 key购买 nike

这是理论上的问题。如果将套接字I/O(readwrite)设置为O_NONBLOCK,但是将此套接字在fd_set中设置为select(),它将阻塞(等待事件文件描述符变为可读或可写),则该套接字无论如何都会阻塞(由于select())?
为什么即使将阻塞(默认)版本一旦变为可读(或可写)(由于select())也不会阻塞,为什么我将套接字设置为非阻塞,因为select()表示它具有要读取(或写入)的数据),因此套接字可以对该数据执行其操作而不会阻塞。那么,为什么无论如何都要在select()阻塞时设置套接字非阻塞状态呢?
一个具有non-block套接字,和阻止select()的示例:

#include <unp.h>

void str_cli(FILE *fp, int sockfd)
{
int flags, maxfd, stdineof;

fd_set rset, wset;

ssize_t nbytes, nactual;
//struct bufpos { char *read_ptr, *write_ptr; };
struct bufpos ipos, opos;
char inbuf[BSIZE], outbuf[BSIZE];

//--//

//set nonblocking flag for these fds:
int nblkFds[3] = {sockfd, STDIN_FILENO, STDOUT_FILENO};
for (int i = 0; i < 3; i++)
{
flags = Fcntl(nblkFds[i], F_GETFL, 0);
Fcntl(nblkFds[i], F_SETFL, flags | O_NONBLOCK);
}

//initialize buffer positions
ipos.write_ptr = ipos.read_ptr = inbuf;
opos.write_ptr = opos.read_ptr = outbuf;

stdineof = 0; //stdin
maxfd = max(STDOUT_FILENO, sockfd) + 1;

while (1)
{
FD_ZERO(&rset);
FD_ZERO(&wset);
//can read from stdin and readptr is not at the end of buffer
if (stdineof == 0 && opos.read_ptr < &outbuf[BSIZE])
{
FD_SET(STDIN_FILENO, &rset);
}
//can read from socket and the readptr is not at then end of buffer
if (ipos.read_ptr < &inbuf[BSIZE])
{
FD_SET(sockfd, &rset);
}
//difference in outbuf == data to write to socket
if (opos.read_ptr != opos.write_ptr)
{
FD_SET(sockfd, &wset);
}
//difference in inbuf == data to write to file
if (ipos.read_ptr != ipos.write_ptr)
{
FD_SET(STDOUT_FILENO, &wset);
}

Select(maxfd, &rset, &wset, NULL, NULL);

if (FD_ISSET(STDIN_FILENO, &rset))
{
switch ((nbytes = read(STDIN_FILENO, opos.read_ptr, &outbuf[BSIZE] - opos.read_ptr)))
{
case -1:
perror("read");
if (errno != EWOULDBLOCK)
{
die("read");
}

case 0:
fprintf(stderr, "%s: EOF on stdin\n", nowtime());
stdineof = 1;
if (opos.write_ptr == opos.read_ptr)
{
//everything was written to socket -> we won't be writing enything else -> close the connection by sending FIN
Shutdown(sockfd, SHUT_WR);
}
break;

default:
fprintf(stderr, "%s: read %ld bytes from stdin\n", nowtime(), nbytes);
//move the read pointer with bytes writen
opos.read_ptr += nbytes;
//now those bytes could be writen to socket
FD_SET(sockfd, &wset);
}
}

if (FD_ISSET(sockfd, &rset))
{
switch ((nbytes = read(sockfd, ipos.read_ptr, &inbuf[BSIZE] - ipos.read_ptr)))
{
case -1:
perror("read");
if (errno != EWOULDBLOCK)
{
die("read");
}

case 0:
fprintf(stderr, "%s: EOF on socket\n", nowtime());
if (stdineof)
{
//normal termination (client EOF)
return;
}
else
{
//RST from peer
die("str_cli: server terminated prematurely");
}
break;

default:
fprintf(stderr, "%s: read %ld bytes from socket\n", nowtime(), nbytes);
//move the read pointer with bytes read
ipos.read_ptr += nbytes;
//those bytes could be writen to file
FD_SET(STDOUT_FILENO, &wset);
}
}

if (FD_ISSET(STDOUT_FILENO, &wset) && (nbytes = ipos.read_ptr - ipos.write_ptr) > 0)
{
//the stdout is writeable and there are some bytes to write
switch ((nactual = write(STDOUT_FILENO, ipos.write_ptr, nbytes)))
{
case -1:
perror("write");
if (errno != EWOULDBLOCK)
{
die("write");
}
default:
fprintf(stderr, "%s: wrote %ld bytes to stdout\n", nowtime(), nactual);
ipos.write_ptr += nactual;
if (ipos.write_ptr == ipos.read_ptr)
{
//back to beginning buffer if all was writen to stdout
ipos.write_ptr = ipos.read_ptr = inbuf;
}
}
}

if (FD_ISSET(sockfd, &wset) && ((nbytes = opos.read_ptr - opos.write_ptr) > 0))
{
//the socket is writeable and there are some bytes to write
switch ((nactual = write(sockfd, opos.write_ptr, nbytes)))
{
case -1:
perror("write");
if (errno != EWOULDBLOCK)
{
die("write");
}

default:
fprintf(stderr, "%s wrote %ld bytes to socket\n", nowtime(), nactual);
opos.write_ptr += nactual;
if (opos.write_ptr == opos.read_ptr)
{
//back to beginning buffer if all was send/writen to socket
opos.read_ptr = opos.write_ptr = outbuf;
if (stdineof)
{
//EOF, could send its FIN
Shutdown(sockfd, SHUT_WR);
}
}
}
}
}
}

最佳答案

This is rather theoretical question. If sockets I/O (either read or write) is set to O_NONBLOCK, but then this socket is set in fd_set to select() which blocks (waiting for an event the file descriptor become either readable or writable), then that socket is blocking anyway (due to the select())?

select正在阻止。套接字仍处于非阻塞状态。

Why would I set the socket to be non-blocking, when even the blocking (default) version once become readable (or writable) (thanks to select()), won't block, because the select() said it has data to read (or write) and thus the socket is able to perform its operation with that data without blocking.


不不不!这不是一个安全的假设。无法保证后续的 readwrite不会被阻止。如果以后需要确保以后的操作不会阻塞,则必须将套接字设置为非阻塞。

So why to bother setting socket non-blocking when select() blocks anyway?


因为您不希望套接字上的操作阻塞。 select函数不能保证将来的操作不会受阻,并且过去做出这样的假设会导致人们被烧死。
例如,您在UDP套接字上执行 select,它说接收不会阻塞。但是,在调用 recv之前,管理员将启用以前禁用的UDP校验和。猜猜是什么,现在如果唯一接收到的数据报的校验和不正确,您的 recv将被阻止。
除非您认为自己已经预见到可能会发生类似的事情,而您绝对不会,否则,如果您不希望套接字阻塞,则必须将其设置为非阻塞。

关于c - 与阻塞select()一起使用时,非阻塞套接字真的是非阻塞的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65985455/

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