关于做网站的策划方案,想开个网站怎么做,外贸营销型网站建设的技巧,传媒公司创业http://blog.csdn.net/wqc_csdn/article/details/51583901 在之前我们实现的并发服务端时通过床将多个进程来实现的#xff0c;这种并实现并发的方式简单方便#xff0c;但是进程的创建和销毁是很消耗系统资源的#xff0c;在访问量大时服务器很容易出现资源不够用的情况。除…http://blog.csdn.net/wqc_csdn/article/details/51583901 在之前我们实现的并发服务端时通过床将多个进程来实现的这种并实现并发的方式简单方便但是进程的创建和销毁是很消耗系统资源的在访问量大时服务器很容易出现资源不够用的情况。除此之外由于每个进程有独立的内存空间所以进程间的通讯也相对比较复杂。因此我们可以考虑通过另一种方式来实现服务端的并发服务——IO复用。 复用 复用在通讯领域很常见一般常见”频分复用”,”时分复用”等名词。其实复用就是在一个通信频道内传递多个数据信号的技术。以频分复用为例其实就是在一个通信信道内发送端通过把信息加载在不同频率的波段上进行发送而接受端在接受到波时通过滤波装置把各中频率的波进行分离以此达到提高通信信道利用率的目的。 IO复用 IO复用其实也是通过对IO描述符的复用来减少进程的创建使得服务端始终只有一个进程从而节省了系统资源提高效率。 select()函数是最具有代表性的实现复用服务端的方法它可以将多个文件描述符集中到一起进行统一监视当监视到有文件描述符需要输入或者是输出时就选择该接口进行通讯通讯完成之后就回到之前监视的状态。 监视内容是否存在套接字接受数据无需阻塞传输数据的套接字有哪些哪些套接字发生了异常 int select(int maxfd,fd_set *read_set, *write_set,fd_set *except_set, const struct timeval *timeout)选择描述符进行通讯 maxfd监视数量监视对象文件描述符数量 read_set读取文件描述符集合的地址将所有关注”是否存在待读取数据”的文件描述符注册到fd_set集合中并传递地址值。也就是说select()函数会监视这个集合里边的文件描述符是是否有待读取的数据没有要监听的描述符时传0 write_set写入文件描述符集合的地址将所有关注”是否可传输无阻塞数据”的文件描述符注册到fd_set集合中并传递地址值。也就是说select()函数会监视这个集合里边的文件描述符是否能发送无阻塞数据没有要监听的描述符时传0 except_set发生异常文件描述符集合的地址将所有关注”是否可发生异常”的文件描述符注册到fd_set集合中并传递地址值。也就是说select()函数会监视这个集合里边的文件描述符是否发生异常没有要监听的描述符时传0 timeout超时位防止无限进入阻塞状态设置一个超时信息 发生错误时返回-1超时时返回0当所关注的事件发生时返回所发生事件的文件描述符数量 select()函数的使用比较复杂大体分为三步 参数设置 设置文件描述符使用select()函数能同时监听多个文件描述符首先要使用fd_set类型将这些文件描述符按照分类接收传输异常集中起来。 fd_set是一个存有0和1的位数组。从下标0开始一直到下标为当前文件描述符的最大序号为止依次表示该文件描述符是否被监听例如fd_set 变量fds[0]中的值为1时表示文件描述符0标准的输入流被监听。 针对fd_set的操作都是以位为单位的为此专门编写了用于fd_set读写的宏定义: FD_ZERO(fd_set *fdset)将fd_set的所有位初始化为0 FD_SET(int fd,fd_set *fdset)在fd_set中注册文件描述符fd的信息 FD_CLR(int fd,fd_set *fdset)从fd_set中清除文件描述符fd的信息 FD_ISSET(int fd,fd_set *fdset)查询fd_set中是否包含文件描述符fd的信息 指定监听范围指定监听文件描述符的范围其实也就是fd_set中的文件描述符数量由于每次新创建一个文件描述符时都会自动加1所以要传入的值为最大的文件描述符1加一是由于文件描述符的标号从0开始。 设置超时由于当文件描述符没有状态的改变时select()函数会始终处于阻塞状态设置超时时间就是为了防止无限制的等待。即使文件描述符没有发生变化只要过了指定时间函数会返回0。这样在函数调用时能知道当前的状态。 结构体timeval用于保存设置的超时时间每次在调用select()函数之前都要重新设置超时时间其结构体如下 struct timeval{long tv_sec;//秒数long tv_usec://毫秒数
}1234 调用select()函数监听注册的文件描述符的状态当有状态发生变化或者时超时时返回结果。 查看调用结果当select()函数返回值是大于0的整数时说明是所监听的文件描述符的状态发生了变化这时我们可以通过之前的fd_set变量来查看变化的结果。 当select()函数调用完之后向其传入的fd_set变量将发生变化原来为1的所有位均变为0但是发生变化的文件描述符对应位除外因此可以认为值仍为1的位置上的文件描述符发生了变化。 至此关于select()函数的介绍就结束了用起来比较复杂我们梳理一遍使用过程 准备工作 为select()设置要监视的文件描述符集合使用函数库提供的关于fd_set的宏定义设置fd_set 为select()设置监视范围即当前最大文件描述符1 位select(0设置超时时间把秒数填入timeval结构体的tv_sec成员中把毫秒数填入timeval结构体的tv_usec成员中每次在调用select()函数之前都要重新设置超时时间。 调用select()函数 查看调用结果根据fd_set调用前后的变化来确定发生变化的文件描述符调用之后fd_set中值为1的位所对应的文件描述符状态发生了变化 调用发生变化的文件描述符进行相应的操作 在大体了解了select()函数的使用过程之后我们就可以尝试着进行一下简单的应用
#includestdio.h
#includeunistd.h
#includesys/time.h
#includesys/select.h#define BUFF_SIZE 30int main(){//声明文件描述符集合fd_set read_set;fd_set temp_set;//保存函数的返回结果int select_res;//字符串长度int str_len;//字符缓冲char buff[BUFF_SIZE];//超时时间结构体struct timeval time_out;//初始化fd_set所有位都置0FD_ZERO(read_set);//设置fd_set使其监视文件描述符为0的文件描述符系统的标准输入流FD_SET(0,read_set);while(1){temp_set read_set;//设置超时时间time_out.tv_sec 5;time_out.tv_usec 0;//调用select()函数select_res select(1,temp_set,0,0,time_out);//根据返回值来判断是否变化if(select_res -1){puts(select() error);break;}else if(select_res 0){puts(select() timeout);}else{//检查是否含有要查询的描述符if(FD_ISSET(0,temp_set)){//从文件描述符为0的流中读取数据str_len read(0,buff,BUFF_SIZE);buff[str_len] 0;printf(message from console : %s ,buff);}}}return 0;
}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 IO复用的服务端
/************************************************************************* File Name: echo_select_server.c Author: xjhznick Mail: xjhznickgmail.com Created Time: 2015年03月26日 星期四 14时03分40秒 Description:使用select函数实现I/O复用服务器端************************************************************************/#includestdio.h
#includestdlib.h
#includestring.h
#includeunistd.h
#includearpa/inet.h
#includesys/socket.h
#includesys/time.h
#includesys/select.hvoid error_handling(char *message);#define BUFF_SIZE 32int main(int argc, char *argv[])
{int server_sock;int client_sock;struct sockaddr_in server_addr;struct sockaddr_in client_addr;socklen_t client_addr_size;char buff[BUFF_SIZE];fd_set reads, reads_init;struct timeval timeout, timeout_init;int str_len, i, fd_max, fd_num;if(argc!2){ //命令行中启动服务程序仅限一个参数端口号printf(Usage : %s port\n, argv[0]);exit(1);}//调用socket函数创建套接字server_sock socket(PF_INET, SOCK_STREAM, 0);if(-1 server_sock){error_handling(socket() error.);}memset(server_addr, 0, sizeof(server_addr));server_addr.sin_family AF_INET;server_addr.sin_addr.s_addr htonl(INADDR_ANY);server_addr.sin_port htons(atoi(argv[1]));//调用bind函数分配IP地址和端口号if( -1 bind( server_sock, (struct sockaddr*)server_addr, sizeof(server_addr)) ){error_handling(bind() error);}//监听端口的连接请求,连接请求等待队列size为5if( -1 listen(server_sock, 5) ){error_handling(listen() error);}//register fd_set varFD_ZERO(reads_init);FD_SET(server_sock, reads_init);//monitor socket: server_sockFD_SET(0, reads_init);// stdin also worksfd_max server_sock;//timeout_init.tv_sec 5;timeout_init.tv_usec 0;while(1){//调用select之后除发生变化的文件描述符对应的bit其他所有位置0所以需用保存初值通过复制使用reads reads_init;//调用select之后timeval成员值被置为超时前剩余的时间因此使用时也需要每次用初值重新初始化timeout timeout_init;fd_num select(fd_max1, reads, NULL, NULL, timeout);if(fd_num 0){fputs(Error select()!, stderr);break;}else if(fd_num 0){puts(Time-out!);continue;}for(i0; ifd_max; i){if(FD_ISSET(i, reads)){if(i server_sock){//connection request!//接受连接请求client_addr_size sizeof(client_addr);client_sock accept( server_sock, (struct sockaddr*)client_addr, client_addr_size );//accept函数自动创建数据I/0 socketif(-1 client_sock){error_handling(accept() error);//健壮性不佳程序崩溃退出} else{//注册与客户端连接的套接字文件描述符FD_SET(client_sock, reads_init);if(fd_max client_sock) fd_max client_sock;printf(Connected client : %d\n, client_sock);}}else{//read message!str_len read(i, buff, BUFF_SIZE);if(str_len){//echo to clientbuff[str_len] 0;printf(Message from client %d: %s, i, buff);write(i, buff, str_len);}else{ //close connectionFD_CLR(i, reads_init);close(i);printf(Disconnected client %d!\n, i);}}//end of iserver_sock}//end of if(FD_ISSET)}//end of for}//end of while//断开连接关闭套接字close(server_sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc(\n, stderr);exit(EXIT_FAILURE);
}