文章目录
- 前言
- 一、select
- 二、API接口:
- 三、使用步骤
-
- 总结
前言
select的原理以及使用
提示:以下是本篇文章正文内容,下面案例可供参考
一、select
select系统调用的用途是:在一段指定时间内,监听用户感兴趣的文件描述符的可读、可写和异常等事件。
二、API接口:
1.int select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);//select 成功时返回就绪(可读、可写和异常)文件描述符的总数。如果在超时时间内没有任何文件描述符就绪,select 将返回 0。select 失败是返回-1.如果在select 等待期间,程序接收到信号,则 select 立即返回-1,并设置 errno 为 EINTR。**maxfd 参数指定的被监听的文件描述符的总数。**它通常被设置为 select 监听的所有文件描述符中的最大值+1,readfds、writefds 和 exceptfds 参数分别指向可读、可写和异常等事件对应的文件描述符集合。应用程序调用 select 函数时,通过这 3 个参数传入自己感兴趣的文件描述符。select 返回时,内核将修改它们来通知应用程序哪些文件描述符已经就绪。
2.void FD_ZERO(fd_set *fdset); // 清除 fdset 的所有位
3.void FD_SET(int fd, fd_set *fdset); // 设置 fdset 的位 fd
4.void FD_CLR(int fd, fd_set *fdset); // 清除 fdset 的位 fd
5. int FD_ISSET(int fd, fd_set *fdset);// 测试 fdset 的位 fd 是否被设置
三、使用步骤
1.服务器端
代码如下(示例):
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<sys/time.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#define MAX 10
fd_set fdset;
void Init_fds(int*fds)
{
assert(fds!=NULL);
for(int i=0;i<MAX;i++)
{
fds[i]=-1;
}
}
void Insert_fds(int*fds,int x)
{
assert(fds!=NULL);
for(int i=0;i<MAX;i++)
{
if(fds[i]==-1)
{
fds[i]=x;
break;
}
}
}
void Del_fds(int*fds,int x)
{
assert(fds!=NULL);
for(int i=0;i<MAX;i++)
{
if(fds[i]==x)
{
fds[i]=-1;
break;
}
}
}
int main()
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
assert(sockfd!=-1);
struct sockaddr_in saddr,caddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family=AF_INET;
saddr.sin_port=htons(6000);
saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res!=-1);
res=listen(sockfd,5);
assert(res!=-1);
int fds[MAX]={};
Init_fds(fds);
Insert_fds(fds,sockfd);
fd_set fdset;
while(1)
{
FD_ZERO(&fdset);
int max=-1;
for(int i=0;i<MAX;i++)
{
if(fds[i]!=-1)
{
FD_SET(fds[i],&fdset);
if(fds[i]>max)
{
max=fds[i];
}
}
}
struct timeval tv={5,0};
int n=select(max+1,&fdset,NULL,NULL,&tv);
if(n==0)
{
printf("time out\n");
continue;
}
if(n==-1)
{
printf("slect err\n");
continue;
}
else
{
for(int i=0;i<MAX;i++)
{
if(fds[i]==-1)
{
continue;
}
if(FD_ISSET(fds[i],&fdset))
{
if(fds[i]==sockfd)
{
int len=sizeof(caddr);
int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
if(c<0)
{
continue;
}
printf("accept(%d)\n",c);
Insert_fds(fds,c);
}
else
{
char buff[128]={0};
int num=recv(fds[i],buff,127,0);
if(num<=0)
{
close(fds[i]);
printf("%d is offline\n",fds[i]);
Del_fds(fds,fds[i]);
continue;
}
printf("recv(%d): %s\n",num,buff);
send(fds[i],"ok",2,0);
}
}
}
}
}
}
2.客户端
代码如下(示例):
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
int main()
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
assert(sockfd!=-1);
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family=AF_INET;
saddr.sin_port=htons(6000);
saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res!=-1);
char buff[128]={0};
printf("input: \n");
while(1)
{
fgets(buff,128,stdin);
if(strncmp(buff,"end",3)==0)
{
break;
}
send(sockfd,buff,strlen(buff),0);
memset(&buff,0,sizeof(buff));
recv(sockfd,buff,127,0);
printf("buff=%s\n",buff);
}
close(sockfd);
exit(0);
}
总结
1.select需要手动的添加或删除文件描述符。
2.select集合的大小是1024,因为是用每一个bit位表示一个描述符。
3.当服务器设置每次只收一个字节的数据时,当接收缓冲区的数据没有被收完时,该文件描述符上会一直有读事件就绪,不会造成数据丢失。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)