博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux C的select函数的使用
阅读量:5862 次
发布时间:2019-06-19

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

hot3.png

1、select函数简介

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval*timeout);

当服务器响应多个客户端连接的时候,需要定义一个线程函数,在每一个线程函数里面处理该连接,进行数据的读写,且connect、accept、recv或recvfrom这样的函数都是阻塞的。

现在想不用线程函数就实现服务器响应多个客户端的连接,就可以使用select函数,且是非阻塞的,可以查询是哪个客户端的响应。

参数maxfd是需要监视的最大的文件描述符值+1; 

rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合。 
struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。 
fd_set(它比较重要所以先介绍一下)是一组文件描述字(fd)的集合,它用一位来表示一个fd(下面会仔细介绍),对于fd_set类型通过下面四个宏来操作: 
FD_ZERO(fd_set *fdset);将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。 
FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。 
FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。 
FD_ISSET(int fd,fd_set *fdset);用于指定的文件描述符是否在该集合中。

2、用select实现服务器响应多个客户端的连接。

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(){ int nSocketFd = 0; int clifd = 0; struct sockaddr_in stServAddr,stClientAddr; socklen_t socketAddrLen; int nRet = 0; char szBuff[BUFSIZ] = {0}; int nReadSocketLen = 0; pthread_t tid; int isReuse = 1; struct timeval tv; int maxfd = 0; int retval = 0; fd_set readfds; int selectFd[100] = {0}; int selectCount = 0; int index = 0; int nRdSocketLen = 0; char szIP[100][20] = {0}; /** 产生一个套接口的描数字 */ nSocketFd = socket(AF_INET,SOCK_STREAM,0); memset(&stServAddr,0,sizeof(struct sockaddr_in)); stServAddr.sin_family = AF_INET; stServAddr.sin_addr.s_addr = htonl(INADDR_ANY); stServAddr.sin_port = htons(8080); setsockopt(nSocketFd,SOL_SOCKET,SO_REUSEADDR,(const char*)&isReuse,sizeof(isReuse)); /** 把这个套接字描述符和本地地址绑定起来 */ nRet = bind(nSocketFd,(struct sockaddr*)&stServAddr,sizeof(stServAddr)); if(-1 == nRet) { perror("bind failed :"); close(nSocketFd); return -1; } /** 设置该套接口的监听状态, */ listen(nSocketFd,1024); FD_ZERO(&readfds); FD_SET(nSocketFd,&readfds); tv.tv_sec = 10; tv.tv_usec = 0; maxfd = nSocketFd; while(1) { FD_ZERO(&readfds); FD_SET(nSocketFd,&readfds); tv.tv_sec = 10; tv.tv_usec = 0; maxfd = nSocketFd; /* 把所有的sock描述符都放入到这个描述符集中去 */ for(index = 0;index < selectCount;index++) { FD_SET(selectFd[index],&readfds); if(selectFd[index] > maxfd) { maxfd = selectFd[index]; } } retval = select(maxfd+1,&readfds,NULL,NULL,&tv); /* 出错 */ if(retval < 0) { perror("select"); } /* 当没有响应 */ if(retval == 0) { continue; } memset(szBuff,0,BUFSIZ); /* 判断是哪个客户端的响应 */ for(index = 0; index < selectCount;index++) { if(FD_ISSET(selectFd[index],&readfds)) { nRdSocketLen = read(selectFd[index],szBuff,BUFSIZ); /* 数据回发 */ write(selectFd[index],szBuff,strlen(szBuff)); if(nRdSocketLen > 0) { printf("read data from %s : %s\n",szIP[index],szBuff); } } } /* 当有新的客户端连接进来 */ if(FD_ISSET(nSocketFd,&readfds)) { /** 监听连接,如果有主机要连接过来,则建立套接口连接 */ socketAddrLen = sizeof(struct sockaddr_in); clifd = accept(nSocketFd,(struct sockaddr*)&stClientAddr,&socketAddrLen); if(-1 == clifd) { perror("accept error: "); return -1; } else { selectFd[selectCount] = clifd; /* 把每一个ip地址存放起来*/ strncpy(szIP[selectCount],inet_ntoa(stClientAddr.sin_addr),20); selectCount++; printf("commect %s %d successful\n",inet_ntoa(stClientAddr.sin_addr),ntohs(stClientAddr.sin_port));//ntohs(stClientAddr.sin_port) } } } close(nSocketFd); return 0;}

另一个例程判断是从终端读取数据,还是socket发过来的数据。

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char* argv[]){ int nSocketFd = 0; char szBuff[BUFSIZ] = {0}; struct hostent *pstHostName = NULL; int nRdSocketLen = 0; int nRet = 0; struct sockaddr_in stClientAddr; pthread_t tid; fd_set rfds; struct timeval tv; int maxfd = 0; int retval = 0; int nWrSocketLen = 0; /** 判断有没有输入ip地址 */ if(2 != argc) { printf("please input telnet ipaddress\n"); return -1; } /** 把输入的主机名变为主机名结构体 */ pstHostName = gethostbyname(argv[1]); if(NULL == pstHostName) { perror("gethostbyname failed\n"); return -1; } /** 获得socket的文件描述符 */ nSocketFd = socket(AF_INET,SOCK_STREAM,0); if(-1 == nSocketFd) { perror("create socket failed : "); return -1; } /** 把socket的文件描述符的结构体清空 */ memset(&stClientAddr,0,sizeof(struct sockaddr_in)); /** 给该结构体赋值 */ stClientAddr.sin_family = AF_INET; stClientAddr.sin_port = htons(8080); stClientAddr.sin_addr =*((struct in_addr *)pstHostName->h_addr); /** 连接目标的主机的ip */ nRet = connect(nSocketFd,(struct sockaddr *)&stClientAddr,sizeof(stClientAddr)); if(-1 == nRet) { perror("connect "); return -1; } else { printf("connect hostname %s successful\n",argv[1]); } if(nSocketFd > maxfd) { maxfd = nSocketFd; } while(1) { FD_ZERO(&rfds); FD_SET(0,&rfds);//设置键盘响应 FD_SET(nSocketFd,&rfds); tv.tv_sec = 1; tv.tv_usec = 0; retval = select(maxfd+1,&rfds,NULL,NULL,&tv); if(retval == -1) { perror("select:"); return 0; } if(retval == 0) //没有响应 { continue; } memset(szBuff,0,BUFSIZ); if(FD_ISSET(0,&rfds)) //是键盘响应 { /** 从终端读取数据 */ read(STDIN_FILENO,szBuff,BUFSIZ); /** 把从终端读取的数据发送出去 */ nWrSocketLen = write(nSocketFd,szBuff,strlen(szBuff)); if(nWrSocketLen > 0) { printf("send message successful\n"); } } if(FD_ISSET(nSocketFd,&rfds)) { /** 把服务器端的数据发送过来 */ nRdSocketLen = read(nSocketFd,szBuff,BUFSIZ); if(nRdSocketLen > 0) { printf("read data:%s\n",szBuff); } } } return 0;}

转载于:https://my.oschina.net/mickelfeng/blog/910358

你可能感兴趣的文章
数据结构之链表【上】
查看>>
拆轮子系列--RxJava理解(三)--observeOn
查看>>
设计模式系列之鸣人VS比尔.雷泽
查看>>
Java类方法和实例方法的区别
查看>>
Android View 的滑动方式
查看>>
android触摸事件分发机制,曾困惑你我的地方
查看>>
oracle业务硬盘出现故障无法访问,提示需要重新格式化后解决方法
查看>>
vue axios 二次封装+BaseUrl打包后自定义+api接口封装
查看>>
重新认识前端开发使用的『图片』
查看>>
利用树莓派和闲置硬盘,搭建起家中的个人网盘
查看>>
基于Github Page 搭建博客(hexo框架)
查看>>
块级元素+行内元素+空元素
查看>>
各个时间点的心态
查看>>
将你的小册制作成一整本PDF
查看>>
vue组件通信
查看>>
Git 常用命令总结
查看>>
一个毕业6年的程序员工作经历和成长感悟
查看>>
pandas常用操作总结【持续更新】
查看>>
css动画实现div内图片逆时针旋转
查看>>
Spring Boot 中关于自定义异常处理的套路!
查看>>