ioctl、fcntl、setsockopt 的区别
ioctl、fcntl、setsockopt 这三个系统函数都是用来设置文件描述符fd属性的,在Linux中,一切设备皆文件,这些文件范围广泛,有些属性是所有文件都具有的共性,而另外一些属性则是某个种类专有的特性。
Linux使用ioctl设置与IO设备高度相关的属性,使用fcntl设置文件描述符的属性,使用setsockopt设置网络套接字的属性,总体而言设备范围逐渐缩小,所设置的属性更具有网络编程针对性。
1. ioctl:
函数原型:
#include <sys/ioctl.h> int ioctl(int fd, unsigned long request, ...);
ioctl用于硬件设备I/O通道控制,控制命令和参数都与设备高度相关,通常也与系统高度相关。
ioctl是底层的系统调用,所以跨平台性不好(可能某些平台的IO属性在其他平台上并不具有);而fcntl是封装好的函数,各个OS都是支持的。
在网络socket中,它们都可以用于设置socket描述字的某些属性,例如非阻塞:
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK)); ioctl(sockfd, FIONBIO, 1); //1表示非阻塞,0表示阻塞
2. fcntl:
函数原型:
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ...);
fcntl的操作对象是文件描述符,用来控制文件描述符属性的通用方法(例如 读写属性、是否阻塞等),setsockopt是用来专门设置套接字描述符的属性,针对网络编程中常见的属性设置的需求。
只要是文件描述符,就可以使用fcntl;setsockopt偏向于设置IP、TCP协议对应的套接字属性。
fcntl可以设置的几个常见的类型:
O_APPEND / O_CREAT / O_RDONLY / O_WRONLY / O_RDWR / O_NONBLOCK
fcntl的典型应用:设置非阻塞IO:
int setnonblocking(int fd) {
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return new_option;
}
//另一种简洁写法:
int setnonblocking(int fd) {
int option = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
return option;
}
3. setsockopt:
函数原型:
#include <sys/types.h> #include <sys/socket.h> int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
其中,level 指定要操作的协议层:
SOL_SOCKET : 通用socket选项,与协议无关 IPPROTO_IP : 设置IP层协议,IPv4 IPPROTO_IPV6 : IPv6 IPPROTO_TCP : 设置TCP层选项
option_name 指定具体的选项,每个level下有各自的option,level 和 option_name 这两个参数联合唯一确定对sockfd的操作内容。
setsockopt的典型举例:设置套接字的超时时间:
int main_client() {
int sockfd = socket():
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0; //设置超时时间
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
ret = connect(sockfd, &serveraddr, sizeof(servaddr));
if(ret == -1) {
if(errno == EINPROGRESS) {
//connect超时返回-1,并设置errno为EINPROGRESS
printf("connect timeout
");
}
return -1;
}
}
setsockopt可用的套接字选项汇总:
参考内容:
