最近在阅读陈硕先生的《Linux多线程服务器端编程》,学习了一些socket接口的新用法,在这里做一下笔记。
一步创建非阻塞socket
socket函数原型如下:
int socket(int domain, int type, int protocol);
自从Linux-2.6.27后,type参数除了可以传递诸如SOCK_STREAM、SOCK_DGRAM等标志外,还可以传递SOCK_NONBLOCK、SOCK_CLOEXEC。SOCK_NONBLOCK设置新创建的文件描述符为非阻塞;设置SOCK_CLOEXEC标志后,程序执行execl系列函数后,文件描述符将被关闭,不能再使用,但是执行fork调用后,文件描述符被子进程继承,在子进程中仍然可以使用。有了这两个标志后,可以使用socket一步创建非阻塞的socket,比如创建一个非阻塞的TCP socket:
int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NOBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
if (sockfd < 0)
{
perror("socket error")
}
accept4一步得到非阻塞socket
accept4在accept的基础上增加了flag参数,可以用来传递SOCK_NONBLOCK和SOCK_CLOEXEC。这样就可以让accept4直接返回非阻塞的连接描述符。
int connfd = accept4(sockfd, (struct sockaddr *)&addr, &addrlen, SOCK_NOBLOCK | SOCK_CLOEXEC);
if (connfd < 0)
{
perror("accept4 error");
}
设置文件描述符为非阻塞
// non-block
int flags = fcntl(sockfd, F_GETFL, 0);
flags |= O_NONBLOCK;
int ret = fcntl(sockfd, F_SETFL, flags);
if (ret < 0)
{
perror("fcntl O_NOBLOCK error");
}
// close-on-exec
flags = fcntl(sockfd, F_GETFD, 0);
flags |= FD_CLOEXEC;
ret = fcntl(sockfd, F_SETFD, flags);
if (ret < 0)
{
perror("fcntl FD_CLOEXEC");
}
这是传统获得非阻塞socket的方式,先通过socket和accept接口获得阻塞的socket,然后调用fcntl设置非阻塞标志。需要注意一点就是,设置前需要先获取以前的标志,然后把新标志添加进去,否则会把以前的标志清除,这自然会导致悲剧。