系统提供select函数来实现多路复用输入/输出模型。
作用:select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。
函数原型:
参数说明:
int nfds:需要监视的最大文件描述符值+1;
fd_set *readfds & *writefds & *exceptfds:指向文件描述符的指针;这三个描述符集说明了我们关心的可读、可写或处于异常条件的各个描述符;
struct timeval *timeout:需要等待的时间,为NULL,则select一直阻塞,知道某个文件描述符发生了事件,为0,仅仅检测描述集合的状态,立即返回,不等待外部事件的发生。
struct timeval{
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};函数返回值:
成功:返回就绪描述符的个数,0表示timeout结束,没有描述符完成就绪;
失败:-1,此时状态描述参数和timeout都变成不可预测的,错误信息存在errno中。
对fd_set数据类型可以进行的处理是:
分配一个这种类型的变量;
将这种类型的一个变量值赋予同类型的另一个变量;或对于这种类型的变量使用下列四个函数中的一个。
void FD_CLR(int fd, fd_set *set); //清除描述词组set中相关的fd的位 int FD_ISSET(int fd, fd_set *set); //测试描述词组set中相关fd的位是否为真 void FD_SET(int fd, fd_set *set); //设置描述词组set中相关的fd位 void FD_ZERO(fd_set *set); //清除描述词组set的全部位
代码:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <netinet/in.h>
int fds[128];
const int len = 128;
static void usage(const char* proc)
{
printf("Usage: %s [ip] [port]\n",proc);
}
int startup(const char *_ip, int _port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if( sock < 0){
perror("socket");
exit(2);
}
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = inet_addr(_ip);
if( bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0){
perror("bind");
exit(3);
}
if( listen(sock, 5) < 0){
perror("listen");
exit(4);
}
return sock;
}
int main(int argc, char *argv[])
{
if(argc != 3){
usage(argv[0]);
exit(1);
}
int i = 0;
for(; i < len; i++){
fds[i] = -1;
}
int listen_sock = startup(argv[1], atoi(argv[2]));
//FD_SET(listen_soxk, &rfds);
//int max_fd = listen_sock;
fd_set rfds;//read fd set;
fds[0] = listen_sock;
int done = 0;
while(!done){
int max_fd = -1;
FD_ZERO(&rfds);
for(i = 0; i < len; i++){
if( fds[i] != -1){
FD_SET(fds[i], &rfds);
}
max_fd = fds[i] > max_fd?fds[i]:max_fd;
}
struct timeval timeout = {5, 0};
switch(select(max_fd+1, &rfds, NULL, NULL, NULL)){
case 0:
printf("timeout\n");
break;
case -1:
perror("select");
break;
default:
{
if( FD_ISSET(listen_sock, &rfds) ){
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int new_fd = accept(listen_sock, (struct sockaddr *)&peer, &len);
if(new_fd > 0){
printf("get a new client-> %s:%d\n",inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));
for(i - 0; i < len; i++){
if(fds[i] == -1){
fds[i] = new_fd;
break;
}
}
if( i == len){
close(new_fd);
}
}
}else{
char buf[1024];
for( i = 0; i < len; i++){
if( i != -1 && FD_ISSET(fds[i], &rfds) ){
ssize_t _s = read(fds[i], buf, sizeof(buf));
if(_s > 0){
buf[_s] = ‘\0‘;
printf("client %d is closed...\n", fds[i]);
close(fds[i]);
fds[i] = -1;
}else{
perror("read");
}
}
}
}
}
break;
}
}
return 0;
}select的缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件描述符数量太小了,默认是1024
select的优点:
(1)相较于之前多线程的方法,使用select不用创建线程,更方便
(2)select目前几乎在所有的平台上都支持,其良好跨平台支持也是它的一个优点
本文出自 “七百七十七快” 博客,谢绝转载!
原文地址:http://10324228.blog.51cto.com/10314228/1837181