如何将TCP套接字更改为非阻塞?

Sac*_*iya 34 c sockets

你如何使套接字无阻塞?

我知道这个fcntl()功能,但我听说它并不总是可靠的.

Jer*_*ner 84

fcntl()一直对我可靠.在任何情况下,这是我用来启用/禁用套接字阻塞的函数:

#include <fcntl.h>

/** Returns true on success, or false if there was an error */
bool SetSocketBlockingEnabled(int fd, bool blocking)
{
   if (fd < 0) return false;

#ifdef _WIN32
   unsigned long mode = blocking ? 0 : 1;
   return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? true : false;
#else
   int flags = fcntl(fd, F_GETFL, 0);
   if (flags == -1) return false;
   flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
   return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
#endif
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*att 46

你被误导的fcntl()并不总是可靠.这是不真实的.

要将套接字标记为非阻塞,代码就像下面这样简单:

// where socketfd is the socket you want to make non-blocking
int status = fcntl(socketfd, F_SETFL, fcntl(socketfd, F_GETFL, 0) | O_NONBLOCK);

if (status == -1){
  perror("calling fcntl");
  // handle the error.  By the way, I've never seen fcntl fail in this way
}
Run Code Online (Sandbox Code Playgroud)

在Linux下,在内核> 2.6.27上,您还可以使用socket()和从头开始创建非阻塞套接字accept4().

例如

   // client side
   int socketfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);

   // server side - see man page for accept4 under linux 
   int socketfd = accept4( ... , SOCK_NONBLOCK);
Run Code Online (Sandbox Code Playgroud)

它节省了一些工作,但便携性较差,所以我倾向于设置它fcntl().

  • 可能很旧,但仍然很有帮助.你的答案似乎正是我所寻找的. (4认同)

unw*_*ind 20

"不总是可靠"是什么意思?如果系统成功设置套接字非阻塞,则它将是非阻塞的.EWOULDBLOCK如果阻塞需要阻塞(例如,如果输出缓冲区已满并且您经常调用发送/写入),则套接字操作将返回.

使用非阻塞调用时,此论坛帖子有一些优点.

  • 我不认为这是一个很好的答案.你告诉他它应该是可靠的然后发布一个很快就会过时的论坛帖子.只是我的意见,但有人与你的代表我会认为会足够周到,提供一个比懒惰的链接和快速评论更彻底的答案. (14认同)
  • @Matt很抱歉让人失望.请注意,这个答案是四年半.我想我的回答表明我对这个问题缺乏细节感到沮丧.我很难说"我听说以明显的方式做这件事并不总是可靠的".我仍然这么认为,不知道如何改进这一点. (8认同)
  • 是的,我后来意识到这个问题有多久了.认为它是新的,因为其他人更新了它,它出现在活动列表中......哎呀.没关系. (2认同)

Siv*_*iva 16

fcntl()ioctl()用于设置文件流的属性.当您使用此功能,使插座非阻塞,功能类似accept(),recv()和等,在自然界中阻止将返回错误并errno会被设置为EWOULDBLOCK.您可以轮询文件描述符集以在套接字上轮询.

  • **是**将套接字转换为非阻塞的方式.如果您可以具体了解"过去的经历"以及为什么您确信这不起作用,也许我们可以帮助您. (10认同)

小智 10

在 Linux 和 BSD 上,您可以直接以非阻塞模式创建套接字(https://www.man7.org/linux/man-pages/man7/socket.7.html):

int fd = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC, res->ai_protocol);
if (fd == -1) {
    perror("socket");
    return -1;
}
Run Code Online (Sandbox Code Playgroud)

接受连接时,可以使用该accept4函数以非阻塞模式直接接受新连接(https://man7.org/linux/man-pages/man2/accept.2.html):

int fd = accept4(lfd, NULL, 0, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (fd == -1) {
    perror("accept4");
    return -1;
}
Run Code Online (Sandbox Code Playgroud)

我不知道为什么接受的答案没有提到这一点。

  • Accept4() 是一个非标准的 Linux 扩展。 (3认同)

мал*_*ров 5

我知道这是一个老问题,但是对于在 google 上最终在这里寻找有关如何处理阻塞和非阻塞套接字的信息的每个人来说,这里深入解释了如何处理套接字 I/O 模式的不同方法- http://dwise1.net/pgm/sockets/blocking.html

快速总结:

  • 那么为什么套接字会阻塞呢?

  • 处理阻塞套接字的基本编程技术是什么?

    • 有一个不关心阻塞的设计
    • 使用选择
    • 使用非阻塞套接字。
    • 使用多线程或多任务


Omk*_*kar 5

如果你想将套接字更改为非阻塞状态,则精确地accept()非阻塞状态然后

    int flags=fcntl(master_socket, F_GETFL);
        fcntl(master_socket, F_SETFL,flags| O_NONBLOCK); /* Change the socket into non-blocking state  F_SETFL is a command saying set flag and flag is 0_NONBLOCK     */                                          

while(1){
    if((newSocket = accept(master_socket, (struct sockaddr *) &address, &addr_size))<0){
                if(errno==EWOULDBLOCK){
                       puts("\n No clients currently available............ \n");
                       continue;
               }
    }else{
          puts("\nClient approched............ \n");
    }

}
Run Code Online (Sandbox Code Playgroud)