I/O复用——select的实现

I/O复用

I/O复用是指一个进程或一个线程能够同时对多对文件描述符(sockfd)提供服务。那么服务器上的进程或线程如何对多个文件描述符统一监听,当任意一个文件描述符上有事件发生,其都能及时处理?

有三种方法,今天我着重介绍一下第一种

1.select

2.poll

3.epoll

select

int  select(int  nfds,fd_set  *readfds,fd_set  *writefds,fd_set  *exceptfds,struct  timeval  *timeout);

其中,参数的意义是:nfds:最大文件描述符的值+1

readfds:用户感兴趣的可读事件的文件描述符集合

writefds:可读事件的文件描述符的集合

exceptfds:异常事件的文件描述符的集合

timeout:设置超时时间,如果timeout为NULL,则select一直阻塞

返回值:>0         返回就绪文件描述符的个数

              ==0       超时

              ==-1      出错

流程:1.将文件描述符设置到readfds,writefds,exceptfds

           2.select返回后,知道哪些文件描述符就绪

其中想将文件描述符设置到readfds,writefds,exceptfds上面必须调用几个系统调用函数:

              1.FD_ZERO(fd_set   *set)                        清空set的所有位

               2.FD_SET(int   fd,fd_set   *set)               设置set的位fd 

               3.FD_CLR(int   fd,fd_set   *set)               清除set的位fd

而想知道哪些文件描述符已经就绪要借助下面的系统调用函数

               4.FD_ISSET(int   fd,fd_set   *set)            测试set的位fd是否被设置

fd_set

   fd_set的结构: 

   

typedef   struct

 {

         int   fd_bits[32];   

 }fd_set;

实现

代码如下:

服务器:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<assert.h>

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/select.h>
#include<arpa/inet.h>
#include<netinet/in.h>

void Init(int *fds,int len)   //初始化
{
	int i=0;
	for(;i<len;i++)
	{
		fds[i]=-1;
	}
}

void Insert(int *fds,int fd,int len)   //插入
{
	int i=0;
	for(;i<len;i++)
	{
		if(fds[i]==-1)
		{
			fds[i]=fd;
			break;
		}
	}
}

void Delete(int *fds,int fd,int len)    //删除
{
	int i=0;
	for(;i<len;i++)
	{
		if(fds[i]!=-1)
		{
			if(fds[i]==fd)
			{
				fds[i]=-1;
				break;
			}
		}
	}
}

int main()
{
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd!=-1);
	struct sockaddr_in ser,cli;
	memset(&ser,0,sizeof(ser));

	ser.sin_family=AF_INET;
	ser.sin_port=htons(8000);
	ser.sin_addr.s_addr=inet_addr("127.0.0.1");

	int ret=bind(sockfd,(struct sockaddr*)(&ser),sizeof(ser));
	assert(ret!=-1);
	listen(sockfd,5);

	int fds[100];
	Init(fds,100);       
    Insert(fds,sockfd,100);     //将sockfd插入到fds数组中
	fd_set readfds;
		
	while(1)
	{
		FD_ZERO(&readfds);
		int maxfd=-1;
		int i=0;
		for(;i<100;i++)
		{
			if(fds[i]!=-1)
			{
				if(fds[i]>maxfd)
				{
					maxfd=fds[i];            //找到最大的文件描述符
				}
				FD_SET(fds[i],&readfds);     //将fds数组中的文件描述符设置到readfds上面
			}
		}
		int n=select(maxfd+1,&readfds,NULL,NULL,NULL);      //启动select
		if(n<=0)
		{
			printf("error\n");
			continue;
		}
	    i=0;
		for(;i<100;i++)
		{
			if(fds[i]!=-1 && FD_ISSET(fds[i],&readfds))    //探测到就绪文件描述符后,判断文件描述符是sockfd还是不是,如果是sockfd表示有客户端完成了三次握手,否则是表示客户端有数据到达
			{
				if(fds[i]==sockfd)
				{
					int client=sizeof(cli);
					int c=accept(sockfd,(struct sockaddr*)(&cli),&client);
					if(c<=0)
					{
						printf("error");
						continue;
					}
					else
					{
						Insert(fds,c,100);         //将accept所返回的套接字也要插入到fds中
					}
				}
				else
				{
					char buff[128]={0};           //客户端有数据到达,读取数据
					int c=fds[i];
					int n=recv(c,buff,127,0);
					if(n<=0)
					{
						close(c);                 //如果其中没有数据,就关闭文件描述符
						Delete(fds,c,100);        //并从fds删除这个文件描述符
						continue;
					}
					else
					{
						printf("%d:  %s\n",c,buff);    //读取数据
						send(c,"OK",2,0);              //发送ok
					}
				}
			}
		}     
	}
}

客户端:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<assert.h>

int main()
{
    int sockfd=socket(PF_INET,SOCK_STREAM,0);
    assert(sockfd != -1);
    struct sockaddr_in ser,cli;

    ser.sin_family=AF_INET;
    ser.sin_port=htons(8000);
    ser.sin_addr.s_addr=inet_addr("127.0.0.1");

    connect(sockfd,(struct sockaddr*)(&ser),sizeof(ser));
    while(1)
    {
        printf("please input: ");
        fflush(stdout);
        char buff[128]={0};
        fgets(buff,128,stdin);
        buff[strlen(buff)-1] = '\0';
        send(sockfd,buff,127,0);
        if(strcmp(buff,"end")==0)
        {
            break;
        }
        char recvbuff[128]={0};
        recv(sockfd,recvbuff,127,0);
        printf("%s\n",recvbuff);
    }
    close(sockfd);
}

结果如下:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值