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

时间:2009-10-09 12:25:15

标签: c sockets

如何使套接字无阻塞?

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

10 个答案:

答案 0 :(得分:79)

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
}

答案 1 :(得分:45)

你被误导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
}

在Linux下,在内核上&gt; 2.6.27您还可以使用socket()accept4()从一开始就创建非阻塞套接字。

e.g。

   // 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);

它节省了一些工作,但便携性较差,因此我倾向于使用fcntl()进行设置。

答案 2 :(得分:20)

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

使用非阻塞调用时,

This forum thread有一些好处。

答案 3 :(得分:16)

fcntl()ioctl()用于设置文件流的属性。当您使用此函数使套接字无阻塞时,accept()recv()等函数(本质上是阻塞函数)将返回错误,errno将设置为{{1 }}。您可以轮询文件描述符集以在套接字上轮询。

答案 4 :(得分:2)

通常,您可以使用select(2)poll(2)或其他系统使用正常的阻止IO 多路复用多个IO操作来实现相同的效果您系统上可用的通话。

有关可扩展IO多路复用方法的比较,请参阅The C10K problem

答案 5 :(得分:2)

我知道这是一个老问题,但是对于所有在这里结束搜索的Google的人来说,这里都是有关如何处理阻塞套接字和非阻塞套接字的信息,这是对如何处理I / O的不同方式的深入解释。套接字模式-http://dwise1.net/pgm/sockets/blocking.html

快速摘要:

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

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

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

答案 6 :(得分:2)

如果您想将 socket更改为非阻止,只需将 accept()更改为 non-Blocking 状态,然后

    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");
    }

}

答案 7 :(得分:1)

在C中将套接字设置为非阻塞的最佳方法是使用ioctl。接受的套接字设置为非阻塞的示例如下:

long on = 1L;
unsigned int len;
struct sockaddr_storage remoteAddress;
len = sizeof(remoteAddress);
int socket = accept(listenSocket, (struct sockaddr *)&remoteAddress, &len)
if (ioctl(socket, (int)FIONBIO, (char *)&on))
{
    printf("ioctl FIONBIO call failed\n");
}

答案 8 :(得分:1)

有时使用“ send / recv”系列系统调用会很方便。如果flags参数包含MSG_DONTWAIT标志,则每个调用的行为将类似于设置了O_NONBLOCK标志的套接字。

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

答案 9 :(得分:1)

在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;
}

接受连接时,可以使用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;
}

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