博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Server Develop (四) select实现非阻塞sever
阅读量:6171 次
发布时间:2019-06-21

本文共 4762 字,大约阅读时间需要 15 分钟。

Select server

  linux 的socket函数分为阻塞和非阻塞两种方式,比如accept函数,在阻塞模式下,它会一直等待有客户连接。而在非阻塞情况下,会立刻返回。我们一般都希望程序能够运行在非阻塞模式下。一种方法就是做一个死循环,不断去查询各个socket的状态,但是这样会浪费大量的cpu时间。解决这个问题的一个方法就是使用select函数。使用select函数可以以非阻塞的方式和多个socket通信。当有socket需要处理时,select函数立刻返回,期间并不会占用cpu时间。 

  select函数原型:

//Select 函数原型 int select(nfds, readfds, writefds, exceptfds, timeout) //nfds: select监视的文件句柄数,视进程中打开的文件数而定,一般设为呢要  监视各文件中的最大文件号加一 //readfds:select监视的可读文件句柄集合 //writefds:select监视的可写文件句柄集合。 //exceptfds:select监视的异常文件句柄集合。 //timeout:本次select的超时结束时间。

   当readfds或writefds中映象的文件可读或可写或超时,本次select() 就结束返回。程序员利用一组系统提供的宏在select()结束时便可判 断哪一文件可读或可写。对Socket编程特别有用的就是readfds.

  select相关函数:

FD_ZERO(fd_set *fdset)//清空fdset与所有文件句柄的联系。 FD_SET(int fd, fd_set *fdset)//建立文件句柄fd与fdset的联系。 FD_CLR(int fd, fd_set *fdset)//清除文件句柄fd与fdset的联系。 FD_ISSET(int fd, fdset *fdset)//检查fdset联系的文件句柄fd是否可读写,>0表示可读写。 

  具体步骤: 

//大致如下:…… int sockfd;fd_set fdR; int sock_fd[N];//save client_sockfd, size is Nstruct timeval timeout; while(1){     FD_ZERO(&fdR);    FD_SET(sockfd, &fdR);    switch (select(sockfd + 1,&fdR, NULL, &timeout)) {      case -1:error;      case 0:timeout;      default:      if (FD_ISSET(sockfd, &fdR)) {                 now  read or recv something;       }     }  /* if sock_serv is in &fdR, now accept() */  if( FD_ISSET(sock_serv,&fdR) ){      sock_fd[..] = now accept();    }}

  具体代码:

void select_server::starListen(){    int flag = listen(sock_serv, 5);    if(flag < 0){        perror("listen error!\n");        close(sock_serv);        return;    }    printf("listen!\n");    fd_set client_fdset;    //监控文件描述符集合    int maxsock;            //监控文件描述符中最大的文件号    struct timeval tv;        //超时返回时间    int client_sockfd[5];   //存放活动的sockfd    bzero((void*)client_sockfd,sizeof(client_sockfd));    int conn_amount = 0;    //用来记录描述符数量    maxsock = sock_serv;    char buffer[1024];    while(1){        //初始化文件描述符号到集合        FD_ZERO(&client_fdset);        //加入服务器描述符        FD_SET(sock_serv,&client_fdset);        //设置超时时间        tv.tv_sec = 30; //30秒        tv.tv_usec = 0;        //把活动的句柄加入到文件描述符中        for(int i = 0; i < 5; ++i){
//程序中Listen中参数设为5,故i必须小于5 if(client_sockfd[i] != 0){ FD_SET(client_sockfd[i], &client_fdset); } } //printf("put sockfd in fdset!\n"); //select函数 flag = select(maxsock+1, &client_fdset, NULL, NULL, &tv); if(flag < 0){ perror("select error!\n"); break; } else if(flag == 0){ printf("timeout!\n"); continue; } //轮询各个文件描述符 for(int i = 0; i < conn_amount; ++i){ //FD_ISSET检查client_sockfd是否可读写,>0可读写 if(FD_ISSET(client_sockfd[i], &client_fdset)){ printf("start recv from client[%d]:\n",i); flag = recv(client_sockfd[i], buffer, 1024, 0); if(flag <= 0){ printf("client[%d] close\n", i); close(client_sockfd[i]); FD_CLR(client_sockfd[i], &client_fdset); client_sockfd[i] = 0; } else{ printf("recv from client[%d] :%s\n", i, buffer); } } } //检查是否有新的连接,如果收,接收连接,加入到client_sockfd中 if(FD_ISSET(sock_serv, &client_fdset)){ //接受连接 struct sockaddr_in client_addr; size_t size = sizeof(struct sockaddr_in); int sock_client = accept(sock_serv, (struct sockaddr*)(&client_addr), (unsigned int*)(&size)); if(sock_client < 0){ perror("accept error!\n"); continue; } //把连接加入到文件描述符集合中 if(conn_amount < 5){ client_sockfd[conn_amount++] = sock_client; bzero(buffer,1024); strcpy(buffer, "this is server! welcome!\n"); send(sock_client, buffer, 1024, 0); printf("new connection client[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); bzero(buffer,sizeof(buffer)); flag = recv(sock_client, buffer, 1024, 0); if(flag < 0){ perror("recv error!\n"); close(sock_serv); return; } printf("recv : %s\n",buffer); if(sock_client > maxsock){ maxsock = sock_client; } else{ printf("max connections!!!quit!!\n"); break; } } } } for(int i = 0; i < 5; ++i){ if(client_sockfd[i] != 0){ close(client_sockfd[i]); } }}

  源码:

转载于:https://www.cnblogs.com/coder2012/archive/2013/06/16/3138530.html

你可能感兴趣的文章
深入浅出 Java 并发编程 (1)
查看>>
【神器】可视化创建骨架屏
查看>>
数组左边减去右边数值的最大差值
查看>>
SVN用法
查看>>
js中的promise和then
查看>>
队列组 iOS之多线程GCD(二)
查看>>
Flutter滚动, 中间显示整图, 前后露出部分图
查看>>
Flutter入坑指南:开发环境搭建
查看>>
跨Navigation跳转(类似微信)方案二
查看>>
JavaScript 复习之 对象的继承
查看>>
从开源小白到 Apache Member,我的成长之路
查看>>
logstash简介
查看>>
Java多线程之synchronized理论
查看>>
Android NestedScrolling解决滑动冲突问题(2) - fling问题与NestedScroll++
查看>>
Tomcat和JVM的性能调优总结
查看>>
硬件线程和软件线程的区别
查看>>
时间戳前
查看>>
11月22日晚上海交大《PMI敏捷实践指南解读》线上沙龙欢迎你的参与!
查看>>
初识 Linux (VMware、CentOS 7)
查看>>
使用SpringMVC完成文件上传
查看>>