【发布时间】:2023-03-19 13:03:02
【问题描述】:
这是一个相当理论的问题。如果套接字 I/O(read 或 write)设置为 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);
}
}
}
}
}
}
【问题讨论】:
-
select忽略O_NONBLOCK但它并没有改变套接字实际上仍然是非阻塞的事实。 当 select() 阻塞时为什么要设置套接字非阻塞。为什么确实如此。你见过这样做的代码吗?可能有这样的用例,但也可能不常见。 -
@kaylum 好吧,我可以给你一个来自 Addison Wesley 的例子:UNIXNetwork ProgrammingVolume 1。见编辑。我认为它的例子相当复杂,但这个例子是我问的原因
-
@kaylum 所有使用非阻塞套接字的代码都必须这样做,否则套接字会阻塞。看我的回答。
标签: c sockets select nonblocking