江门网站程序开发制作,做网站必须有框架么,网站空间免费 优帮云,小区住宅可以注册公司吗http://blog.csdn.net/li_ning_/article/details/52165993 fd_set 你终于还是来了#xff0c;能看到这个标题进来的#xff0c;我想#xff0c;你一定是和我遇到了一样的问题#xff0c;一样的疑惑#xff0c;接下来几个小时#xff0c;我一定竭尽全力#xff0c;写出我…http://blog.csdn.net/li_ning_/article/details/52165993 fd_set 你终于还是来了能看到这个标题进来的我想你一定是和我遇到了一样的问题一样的疑惑接下来几个小时我一定竭尽全力写出我想说的希望也正是你所需要的 关于Linux下I/O多路转接之select我不想太多的解释用较少的文章引出今天我要说的问题fd_set...自我感觉这个东西是理解select的关键。 一、关于select函数 以上只是截屏以保证本人说的是真话下面解释 系统提供select函数来实现多路复用输入/输出模型。select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。 程序会停在select这里等待直到被监视的文件句柄有一个或 多个发生了状态改变。关于文件句柄其实就是一个整数我们最熟悉的句柄是0、1、2三 个 0是标准输入1是标准输出2是标准错误输出。0、1、2是整数表示的对应的FILE * 结构的表示就是stdin、stdout、stderr。 1.参数nfds是需要监视的最大的文件描述符值1 2.rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合可写文件描述符的集合及异常文件描述符的集合。 3.struct timeval结构用于描述一段时间长度如果在这个时间内需要监视的描述符没有事件发生则函数返回返回值为0。 下面的宏提供了处理这三种描述词组的方式: 1. FD_CLR(inr fd,fd_set* set)用来清除描述词组set中相关fd 的位。 2. FD_ISSET(int fd,fd_set *set)用来测试描述词组set中相关fd 的位是否为真 。 3.FD_SETint fd,fd_set*set用来设置描述词组set中相关fd的位 。 4.FD_ZEROfd_set *set用来清除描述词组set的全部位 参数timeout为结构timeval用来设置select()的等待时间其结构定义如下 结构体成员两个第一个单位是秒第二个单位是微妙 作用是时间为两个之和 更多关于select的应用咱移驾看这位大神http://blog.sina.com.cn/s/blog_5c8d13830100pwaf.html 关于select的使用理解了也就好弄了关于应用后附代码 下面才是我想说的东西 二、fd_set: 1fd_set是什么 select()机制中提供一fd_set的数据结构可以理解为一个集合实际上是一个位图每一个特定位来标志相应大小文件描述符这个集合中存放的是文件描述符即就是文件句柄(不管是socket句柄还是其他文件或命名管道或设备句柄)建立联系建立联系的工作由程序员完成当调用select()时由内核根据IO状态修改fd_set的内容由此来通知执行了select()的进程哪一socket或文件可读。Unix下任何设备、管道、FIFO等都是文件形式全部包括在内所以毫无疑问一个socket就是一个文件socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作程序员通过操作4类宏来完成最fd_set的操作在上文已经提及。 2fe_set怎么表示 其中readfds、writefds等都是fd_set类型其中的每一位都表示一个fd,即文件描述符。 3fd_set用法 过去一个fd_set通常只能包含32的fd文件描述字因为fd_set其实只用了一个32位矢量来表示fd现在,UNIX系统通常会在头文件中定义常量FD_SETSIZE它是数据类型fd_set的描述字数量其值通常是1024这样就能表示1024的fd。根据fd_set的位矢量实现我们可以重新理解操作fd_set的四个宏 fd_set set; FD_ZERO(set); /*将set的所有位置0如set在内存中占8位则将set置为00000000*/ FD_SET(0, set); /* 将set的第0位置1如set原来是00000000则现在变为10000000这样fd1的文件描述字就被加进set中了 */ FD_CLR(4, set); /*将set的第4位置0如set原来是10001000则现在变为10000000这样fd4的文件描述字就被从set中清除了 */ FD_ISSET(5, set); /* 测试set的第5位是否为1如果set原来是10000100则返回非零表明fd5的文件描述字在set中否则返回0*/ 我们在回到原函数select int select(int nfds, fd_set *readset, fd_set *writesetfd_set* exceptset, struct timeval *timeout); 功能测试指定的fd可读、可写、有异常条件待处理。 参数 1.nfds 需要检查的文件描述字个数即检查到fd_set的第几位数值应该比三组fd_set中所含的最大fd值更大一般设为三组fd_set中所含的最大fd值加1如在上边例子中readset,writeset,exceptset中所含最大的fd为5则nfds6因为fd是从0开始的。设这个值是为提高效率使函数不必检查fd_set的所有1024位。 readset 用来检查可读性的一组文件描述字。 writeset 用来检查可写性的一组文件描述字。 exceptset 用来检查是否有异常条件出现的文件描述字。(注错误不包括在异常条件之内) timeout有三种可能 1. timeoutNULL阻塞直到有一个fd位被置为1函数才返回 2. timeout所指向的结构设为非零时间等待固定时间有一个fd位被置为1或者时间耗尽函数均返回 3. timeout所指向的结构时间设为0非阻塞函数检查完每个fd后立即返回 返回值 1.当监视的相应的文件描述符集中满足条件时比如说读文件描述符集中有数据到来时内核(I/O)根据状态修改文件描述符集并返回一个大于0的数。 2.当没有满足条件的文件描述符且设置的timeval监控时间超时时select函数会返回一个为0的值。 3.当select返回负值时发生错误。 备注 三组fd_set均将某些fd位置0只有那些可读可写以及有异常条件待处理的fd位仍然为1。 使用select函数的过程一般是 先调用宏FD_ZERO将指定的fd_set清零然后调用宏FD_SET将需要测试的fd加入fd_set接着调用函数select测试fd_set中的所有fd最后用宏FD_ISSET检查某个fd在函数select调用后相应位是否仍然为1。 以下是一个测试单个文件描述字可读性的例子 基于select实现的网络服务器和客户端 server.c [cpp] view plain copy #includestdio.h #includesys/types.h #includesys/socket.h #includeunistd.h #includestdlib.h #includeerrno.h #includearpa/inet.h #includenetinet/in.h #includestring.h #includesignal.h #includesys/wait.h /* *网络服务器select参与调度 * */ //ERR_EXIT(M)是一个错误退出宏 #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while (0) int main(void) { signal(SIGPIPE, SIG_IGN); //1.创建套接字 int listenfd; //被动套接字(文件描述符即只可以accept, 监听套接字 if ((listenfd socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) 0) ERR_EXIT(socket error); //调用上边的宏 struct sockaddr_in servaddr; //memset(servaddr, 0, sizeof(servaddr)); //三个结构体成员 //设置本地IP 和端口 servaddr.sin_family AF_INET; servaddr.sin_port htons(8080); servaddr.sin_addr.s_addr htonl(INADDR_ANY); /* servaddr.sin_addr.s_addr inet_addr(127.0.0.1); */ /* inet_aton(127.0.0.1, servaddr.sin_addr); */ //2.设置套接字属性 int on 1; if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on)) 0) ERR_EXIT(setsockopt error); //3.绑定 if (bind(listenfd, (struct sockaddr*)servaddr,sizeof(servaddr)) 0) ERR_EXIT(bind error); //4.监听 if (listen(listenfd, SOMAXCONN) 0) //listen应在socket和bind之后而在accept之前 ERR_EXIT(listen error); struct sockaddr_in peeraddr; //传出参数 socklen_t peerlen sizeof(peeraddr); //传入传出参数必须有初始值 int conn; // 已连接套接字(变为主动套接字即可以主动connect) int i; int client[FD_SETSIZE]; int maxi 0; // client数组中最大不空闲位置的下标 for (i 0; i FD_SETSIZE; i) client[i] -1; int nready; int maxfd listenfd; fd_set rset; fd_set allset; FD_ZERO(rset); FD_ZERO(allset); FD_SET(listenfd, allset); while (1) { rset allset; nready select(maxfd 1, rset, NULL, NULL, NULL); if (nready -1) { if (errno EINTR) continue; ERR_EXIT(select error); } if (nready 0) continue; if (FD_ISSET(listenfd, rset)) { conn accept(listenfd, (struct sockaddr*)peeraddr, peerlen); //accept不再阻塞 if (conn -1) ERR_EXIT(accept error); for (i 0; i FD_SETSIZE; i) { if (client[i] 0) { client[i] conn; if (i maxi) maxi i; break; } } if (i FD_SETSIZE) { fprintf(stderr, too many clients\n); exit(EXIT_FAILURE); } printf(recv connect ip%s port%d\n, inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); FD_SET(conn, allset); if (conn maxfd) maxfd conn; if (--nready 0) continue; } for (i 0; i maxi; i) { conn client[i]; if (conn -1) continue; if (FD_ISSET(conn, rset)) { char recvbuf[1024] {0}; int ret read(conn, recvbuf, 1024); if (ret -1) ERR_EXIT(readline error); else if (ret 0) { //客户端关闭 printf(client close \n); FD_CLR(conn, allset); client[i] -1; close(conn); } fputs(recvbuf, stdout); write(conn, recvbuf, strlen(recvbuf)); if (--nready 0) break; } } } return 0; } client.c [cpp] view plain copy #includestdio.h #includesys/types.h #includesys/socket.h #includeunistd.h #includestdlib.h #includeerrno.h #includearpa/inet.h #includenetinet/in.h #includestring.h #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while (0) int main(void) { int sock; if ((sock socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) 0) // listenfd socket(AF_INET, SOCK_STREAM, 0) ERR_EXIT(socket error); struct sockaddr_in servaddr; memset(servaddr, 0, sizeof(servaddr)); servaddr.sin_family AF_INET; servaddr.sin_port htons(8080); servaddr.sin_addr.s_addr inet_addr(127.0.0.1); /* inet_aton(127.0.0.1, servaddr.sin_addr); */ if (connect(sock, (struct sockaddr *)servaddr, sizeof(servaddr)) 0) ERR_EXIT(connect error); struct sockaddr_in localaddr; char cli_ip[20]; socklen_t local_len sizeof(localaddr); memset(localaddr, 0, sizeof(localaddr)); if( getsockname(sock,(struct sockaddr *)localaddr,local_len) ! 0 ) ERR_EXIT(getsockname error); inet_ntop(AF_INET, localaddr.sin_addr, cli_ip, sizeof(cli_ip)); printf(host %s:%d\n, cli_ip, ntohs(localaddr.sin_port)); fd_set rset; FD_ZERO(rset); int nready; int maxfd; int fd_stdin fileno(stdin); // if (fd_stdin sock) maxfd fd_stdin; else maxfd sock; char sendbuf[1024] {0}; char recvbuf[1024] {0}; while (1) { FD_SET(fd_stdin, rset); FD_SET(sock, rset); nready select(maxfd 1, rset, NULL, NULL, NULL); //select返回表示检测到可读事件 if (nready -1) ERR_EXIT(select error); if (nready 0) continue; if (FD_ISSET(sock, rset)) { int ret read(sock, recvbuf, sizeof(recvbuf)); if (ret -1) ERR_EXIT(read error); else if (ret 0) //服务器关闭 { printf(server close\n); break; } fputs(recvbuf, stdout); memset(recvbuf, 0, sizeof(recvbuf)); } if (FD_ISSET(fd_stdin, rset)) { if (fgets(sendbuf, sizeof(sendbuf), stdin) NULL) break; write(sock, sendbuf, strlen(sendbuf)); memset(sendbuf, 0, sizeof(sendbuf)); } } close(sock); return 0; } 赐教