How do I change a TCP socket to be non-blocking?
You're misinformed about fcntl()
not always being reliable. 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()
.
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);
It saves a little bit of work, but is less portable so I tend to set it with 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 (e.g. if the output buffer is full and you're calling send/write too often).
This forum thread has a few good points when working with non-blocking calls.
fcntl()
has always worked reliably for me. 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
}