推荐聊城做网站,顺企网企业黄页,网页价格表,wordpress pluto主题http://blog.csdn.net/y396397735/article/details/55004775 select函数的功能和调用顺序 使用select函数时统一监视多个文件描述符的#xff1a; 1、 是否存在套接字接收数据#xff1f; 2、 无需阻塞传输数据的套接字有哪些? 3、 哪些套接字发生了异常#xff1f; sel…http://blog.csdn.net/y396397735/article/details/55004775 select函数的功能和调用顺序 使用select函数时统一监视多个文件描述符的 1、 是否存在套接字接收数据 2、 无需阻塞传输数据的套接字有哪些? 3、 哪些套接字发生了异常 select函数调用过程 由上图知调用select函数需要一些准备工作调用后还需要查看结果。 设置文件描述符 select可以同时监视多个文件描述符套接字。 此时需要先将文件描述符集中到一起。集中时也要按照监视项接收传输异常进行区分即按照上述3种监视项分成三类。 使用fd_set数组变量执行此项操作该数组是存有0和1的位数组。 最左端的位表示文件描述符0位置。如果该位值为1则表示该文件描述符是监视对象。 图上显然监视对象为fd1和fd3。 “是否应当通过文件描述符的数字直接将值注册到fd_set变量” 当然不是操作fd_set的值由如下宏来完成 FD_ZERO(fd_set* fdset) 将fd_set变量的所有位初始化为0。 FD_SETint fd, fd_set* fdset:在参数fdset指向的变量中注册文件描述符fd的信息。 FD_CLR(int fd, fd_set* fdset)参数fdset指向的变量中清除文件描述符fd的信息。 FD_ISSET(int fd, fd_set* fdset): 若参数fdset指向的变量中包含文件描述符fd的信息则返回真。 画图解释 设置监视范围及超时 select函数
#include sys/select.h
#include sys/time.hint select(int maxfd, fd_set* readset, fd_set* writeset, fd_set* exceptset, const struct timeval* timeout); 1 2 3 4 5 6 7 8 其中参数和返回值 maxfd监视对象文件描述符数量。 readset: 将所有关注“是否存在待读取数据”的文件描述符注册到fd_set变量并传递其地址值。 writeset: 将所有关注“是否可传输无阻塞数据”的文件描述符注册到fd_set变量并传递其地址值。 exceptset: 将所有关注“是否发生异常”的文件描述符注册到fd_set变量并传递其地址值。 timeout: 调用select后为防止陷入无限阻塞状态传递超时信息。 返回值错误返回-1超时返回0。因关注的事件返回时返回大于0的值该值是发生事件的文件描述符数。 select函数用来验证3种监视项的变化情况。根据监视项声明3个fd_set变量分别向其注册文件描述符信息并把变量的地址传递到函数的第二到第四个参数。但是在调用select函数前需要决定2件事 “文件描述符的监视范围是” “如何设定select函数的超时时间” 第一文件描述符的监视范围与第一个参数有关实际上,select函数要求通过第一个参数传递监视对象文件描述符的数量。因此需要得到注册在fd_set变量中的文件描述符数。但每次新建文件描述符时其值都会增1故只需将最大的文件描述符值加1再传递到select函数即可。加1是因为文件描述符的值从0开始 第二超时时间与 最后一个参数有关其中timeval结构体如下
struct timeval
{long tv_sec;long tv_usec;
}; 1 2 3 4 5 本来select函数只有在监视文件描述符发生变化时才返回未发生变化会进入阻塞状态。指定超时时间就是为了防止这种情况发生。 将上述结构体填入时间值然后将结构体地址值传给select函数的最后一个参数此时即使文件描述符中未发生变化只要过了指定时间也可以从函数返回。不过这种情况下select函数返回0。 不想设置超时最后一个参数只需要传递NULL。 调用select函数后查看结果 如果select返回值大于0说明文件描述符发生了变化。
关于文件描述符变化文件描述符变化是指监视的文件描述符中发生了相应的监视事件。例如通过select的第二个参数传递的集合中存在需要读取数据的描述符时就意味着文件描述符发生变化。 1 2 3 怎样获知哪些文件描述符发生了变化向select函数的第二到第四个参数传递的fd_set变量中将产生变化如下图 select函数调用完成后向其传递的fd_set变量中将发生变化。原来为1的所有位均变为0但发生变化的文件描述符对应位除外。因此可以认为值为1的位置上的文件描述符发生了变化。 select函数调用实例
#include stdio.h
#include unistd.h
#include sys/time.h
#include sys/select.h#define BUF_SIZE 30int main(int argc, char* argv[])
{fd_set reads, temps;int result, str_len;char buf[BUF_SIZE];struct timeval timeout;FD_ZERO(reads);FD_SET(0, reads);//监视文件描述符0的变化, 即标准输入的变化/*超时不能在此设置因为调用select后结构体timeval的成员tv_sec和tv_usec的值将被替换为超时前剩余时间.调用select函数前每次都需要初始化timeval结构体变量.timeout.tv_sec 5;timeout.tv_usec 5000;*/while(1){/*将准备好的fd_set变量reads的内容复制到temps变量因为调用select函数后除了发生变化的fd对应位外剩下的所有位都将初始化为0为了记住初始值必须经过这种复制过程。*/temps reads;//设置超时timeout.tv_sec 5;timeout.tv_usec 0;//调用select函数. 若有控制台输入数据则返回大于0的整数如果没有输入数据而引发超时返回0.result select(1, temps, 0, 0, timeout);if(result -1){perror(select() error);break;}else if(result 0){puts(timeout);}else{//读取数据并输出if(FD_ISSET(0, temps)){str_len read(0, buf, BUF_SIZE);buf[str_len] 0;printf(message from console: %s, buf);}}}return 0;
}程序运行结果
/*
nihao
message from console: nihao
goodbye
message from console: goodbye
timeout
timeout
*/ select函数实现I/O复用服务端 服务端代码
//server.c
#include stdio.h
#include string.h
#include unistd.h
#include stdlib.h
#include arpa/inet.h
#include sys/time.h
#include sys/socket.h
#include sys/select.h#define BUF_SIZE 100void error_handing(char* buf);int main(int argc, char* argv[])
{int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;struct timeval timeout;fd_set reads, cpy_reads;socklen_t adr_sz;int fd_max, str_len, fd_num, i;char buf[BUF_SIZE];if(argc ! 2){printf(Usage: %s port \n, argv[0]);exit(1);}serv_sock socket(PF_INET, SOCK_STREAM, 0);memset(serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family AF_INET;serv_adr.sin_addr.s_addr htonl(INADDR_ANY);serv_adr.sin_port htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*)serv_adr, sizeof(serv_adr)) -1)error_handing(bind() error);if(listen(serv_sock, 5) -1)error_handing(listen() error);FD_ZERO(reads);FD_SET(serv_sock, reads); //将服务端套接字注册入fd_set,即添加了服务器端套接字为监视对象fd_max serv_sock;while(1){cpy_reads reads;timeout.tv_sec 5;timeout.tv_usec 5000;//无限循环调用select 监视可读事件if((fd_num select(fd_max1, cpy_reads, 0, 0, timeout)) -1){perror(select error);break;}if(fd_num 0)continue;for(i 0; i fd_max 1; i){if(FD_ISSET(i, cpy_reads)){/*发生状态变化时,首先验证服务器端套接字中是否有变化.①若是服务端套接字变化接受连接请求。②若是新客户端连接注册与客户端连接的套接字文件描述符.*/if(i serv_sock){adr_sz sizeof(clnt_adr);clnt_sock accept(serv_sock, (struct sockaddr*)clnt_adr, adr_sz);FD_SET(clnt_sock, reads);if(fd_max clnt_sock)fd_max clnt_sock;printf(connected client: %d \n, clnt_sock);}else {str_len read(i, buf, BUF_SIZE);if(str_len 0) //读取数据完毕关闭套接字{FD_CLR(i, reads);//从reads中删除相关信息close(i);printf(closed client: %d \n, i);}else{write(i, buf, str_len);//执行回声服务 即echo}}}}}close(serv_sock);return 0;
}void error_handing(char* buf)
{fputs(buf, stderr);fputc(\n, stderr);exit(1);
} 客户端代码
//client.c
#include stdlib.h
#include stdio.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.h
#include string.h
#define BUF_SIZE 1024void error_handling(char* message);int main(int argc, char* argv[])
{int sock;char message[BUF_SIZE];int str_len;struct sockaddr_in serv_adr;if(argc ! 3){printf(Usage: %s IP port \n, argv[0]);exit(1);}sock socket(PF_INET, SOCK_STREAM, 0);if(sock-1)error_handling(socket error);memset(serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family AF_INET;serv_adr.sin_addr.s_addr inet_addr(argv[1]);serv_adr.sin_port htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)serv_adr, sizeof(serv_adr))-1)error_handling(connect() error!);elseputs(connected....);while(1){fputs(Input message:(输入Q退出):, stdout);fgets(message, BUF_SIZE, stdin);if(!strcmp(message, q\n) || !strcmp(message, Q\n))break;write(sock, message, strlen(message));str_len read(sock, message, BUF_SIZE-1);message[str_len] 0;printf(Message from server: %s, message);}close(sock);return 0;
}void error_handling(char* message)
{fputs(message, stderr);fputc(\n, stderr);exit(1);
} 编译执行测试
编译程序
gcc server.c –o server
gcc client.c –o client启动服务端
yuubuntu:~/qtProjects/echo_selectserv$ ./server 8899启动客户端1
yuubuntu:~/qtProjects/echo_selectserv$ ./client 127.0.0.1 8899
connected....
Input message:(输入Q退出):你好哇2017年2月12日20:25:43
Message from server: 你好哇2017年2月12日20:25:43
Input message:(输入Q退出):你好哇2017-02-12 20:25:52
Message from server: 你好哇2017-02-12 20:25:52
Input message:(输入Q退出):q启动客户端2
yuubuntu:~/qtProjects/echo_selectserv$ ./client 127.0.0.1 8899
connected....
Input message:(输入Q退出):你好2017年2月12日20:25:11
Message from server: 你好2017年2月12日20:25:11
Input message:(输入Q退出):你好2017年2月12日20:25:24
Message from server: 你好2017年2月12日20:25:24
Input message:(输入Q退出):q服务端情况
yuubuntu:~/qtProjects/echo_selectserv$ ./server 8899
connected client: 4
connected client: 5
closed client: 5
closed client: 4 学习自《TCP/IP网络编程》