简体   繁体   English

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

[英]How do I change a TCP socket to be non-blocking?

How do you make a socket non-blocking?你如何使套接字非阻塞?

I am aware of the fcntl() function, but I've heard it's not always reliable.我知道fcntl()函数,但我听说它并不总是可靠的。

fcntl() has always worked reliably for me. fcntl()对我来说一直很可靠。 In any case, here is the function I use to enable/disable blocking on a socket:无论如何,这是我用来启用/禁用套接字阻塞的函数:

#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
}

You're misinformed about fcntl() not always being reliable.你被误导了fcntl()并不总是可靠的。 It's untrue.这是不真实的。

To mark a socket as non-blocking the code is as simple as:要将套接字标记为非阻塞,代码非常简单:

// 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
}

Under Linux, on kernels > 2.6.27 you can also create sockets non-blocking from the outset using socket() and accept4() .在 Linux 下,在内核 > 2.6.27 上,您还可以使用socket()accept4()从一开始就创建非阻塞socket()

eg例如

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

It saves a little bit of work, but is less portable so I tend to set it with fcntl() .它节省了一点工作,但便携性较差,因此我倾向于使用fcntl()进行设置。

What do you mean by "not always reliable"? “并不总是可靠的”是什么意思? If the system succeeds in setting your socket non non-blocking, it will be non-blocking.如果系统成功地将您的套接字设置为非阻塞,它将是非阻塞的。 Socket operations will return EWOULDBLOCK if they would block need to block (eg if the output buffer is full and you're calling send/write too often).如果套接字操作需要阻塞(例如,如果输出缓冲区已满并且您过于频繁地调用发送/写入),则套接字操作将返回EWOULDBLOCK

This forum thread has a few good points when working with non-blocking calls.在处理非阻塞调用时,这个论坛线程有几个优点。

fcntl() or ioctl() are used to set the properties for file streams. fcntl()ioctl()用于设置文件流的属性。 When you use this function to make a socket non-blocking, function like accept() , recv() and etc, which are blocking in nature will return error and errno would be set to EWOULDBLOCK .当您使用此函数使套接字非阻塞时,像accept()recv()等本质上阻塞的函数将返回错误并且errno将设置为EWOULDBLOCK You can poll file descriptor sets to poll on sockets.您可以轮询文件描述符集以轮询套接字。

I know it's an old question, but for everyone on google ending up here looking for information on how to deal with blocking and non-blocking sockets here is an in depth explanation of the different ways how to deal with the I/O modes of sockets - http://dwise1.net/pgm/sockets/blocking.html .我知道这是一个老问题,但对于谷歌上的每个人来说,最终在这里寻找有关如何处理阻塞和非阻塞套接字的信息,这是对如何处理套接字 I/O 模式的不同方法的深入解释- http://dwise1.net/pgm/sockets/blocking.html

Quick summary:快速总结:

  • So Why do Sockets Block?那么为什么套接字会阻塞呢?

  • What are the Basic Programming Techniques for Dealing with Blocking Sockets?处理阻塞套接字的基本编程技术有哪些?

    • Have a design that doesn't care about blocking有一个不关心阻塞的设计
    • Using select使用选择
    • Using non-blocking sockets.使用非阻塞套接字。
    • Using multithreading or multitasking使用多线程或多任务

If you want to change socket to non blocking , precisely accept() to NON-Blocking state then如果你想改变插座非阻塞,正是接受()非阻塞状态,则

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

}

On Linux and BSD you can directly create the socket in non-blocking mode ( https://www.man7.org/linux/man-pages/man7/socket.7.html ):在 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;
}

When accepting connections you can use the accept4 function to directly accept new connections in non-blocking mode ( https://man7.org/linux/man-pages/man2/accept.2.html ):接受连接时,您可以使用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;
}

I don't know why the accepted answer doesn't mention this.我不知道为什么接受的答案没有提到这一点。

Generally you can achieve the same effect by using normal blocking IO and multiplexing several IO operations using select(2) , poll(2) or some other system calls available on your system.通常,您可以通过使用普通阻塞 IO并使用select(2)poll(2)或系统上可用的其他一些系统调用来复用多个 IO 操作来实现相同的效果。

See The C10K problem for the comparison of approaches to scalable IO multiplexing.有关可扩展 IO 多路复用方法的比较,请参阅C10K 问题

The best method for setting a socket as non-blocking in C is to use ioctl.在 C 中将套接字设置为非阻塞的最佳方法是使用 ioctl。 An example where an accepted socket is set to non-blocking is following:接受的套接字设置为非阻塞的示例如下:

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

It is sometimes convenient to employ the "send/recv" family of system calls.有时使用“send/recv”系列系统调用会很方便。 If the flags parameter contains the MSG_DONTWAIT flag, each call will behave similar to a socket having the O_NONBLOCK flag set.如果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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM