io多路复用之select

select函数介绍:
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>


int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);
nfds     :监视的文件描述符数量
readfds  :将是否有可读的数据注册到fd_set型变量中
writefds :将是否有可写的数据注册到fd_set型变量中
exceptfds:将是异常的数据注册到fd_set型变量中
timeout  :超时(为防止无休止的阻塞,传递超时信息)

select用来检测三种监视项的变化情况,根据监视项的三个fd_set类型变量,
分别向其注册文件描述符信息。

fd_set类型变量相关介绍:
void FD_CLR(int fd, fd_set *set);//将描述符fd从集合set中去除
int  FD_ISSET(int fd, fd_set *set);//集合set中描述符fd是否发生变化
void FD_SET(int fd, fd_set *set);//将描述符fd添加到集合set中
void FD_ZERO(fd_set *set);//将集合中的变量全部清0

超时结构体struct timeval介绍:
struct timeval{
        _time_t tv_sec;//秒
        _suseconds_t tv_usec;//毫秒
};
(Linux下查看该结构体的原型可以输入指令 vi -t timeval,前提是需要先安装ctags)

 

 

#include <stdio.h>
#include <sys/types.h>	       
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#define Max 1024
#define Error -1

static int sfd = 0;
const ushort port = 49999;
//服务器的初始化
int init_server()
{
	//建立流式套接字
	sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd < 0){
		perror("socket");
		goto SET_ERR;
	}
	//服务端的地址结构(ipv4)
	//htons进行大小端转化
	//INADDR_ANY表示任意ip地址,系统提供的宏
	struct sockaddr_in server_addr  = {
				    	.sin_family = AF_INET,
					 	.sin_port   = htons(port),
						.sin_addr   = {
						.s_addr     = htonl(INADDR_ANY),
						},
	};
	//绑定地址
	if(bind(sfd,(struct sockaddr*)&server_addr,sizeof(server_addr))<0){
		perror("bind");
		goto SET_ERR;
	}
	//设置监听套接字,允许最大连接的套接字
	if(listen(sfd,10) < 0){
		perror("listen");
		goto SET_ERR;
	}
	return sfd;
SET_ERR:
	close(sfd);
	return Error;

}

int main(int argc, const char *argv[])
{
	int fd = init_server();
	int maxfd = fd;
	int cfd;
	int i = 0;
	//客户端地址信息
	struct sockaddr_in client_addr;
	int len = sizeof(client_addr);
	//一般需要两个集合
	fd_set rfds,tempfds;
	//清空集合
	FD_ZERO(&rfds);
	FD_ZERO(&tempfds);
	//将套接字fd放到集合rfds中
	FD_SET(fd,&rfds);
	//设置超时信息,为5s
	struct timeval time  = {
				.tv_sec  = 5,
				.tv_usec = 0,
	};
	struct timeval temptv;
	printf("waiting for connect...\n");
	while(1){
		tempfds = rfds;
		temptv = time;
		//进行select轮询
		int arg = select(maxfd+1,&tempfds,NULL,NULL,&temptv);
		if(arg < 0){
			perror("select");
			goto SET_ERR;
		}else if(arg == 0){
			//超时
			printf("time out...\n");
		}
		//循环遍历集合
		for(i=0;i<=maxfd;i++){
			if(FD_ISSET(i,&tempfds)){
				if(i == fd){//如果描述符就绪了,表示有客户端进行连接
					cfd = accept(fd,(struct sockaddr*)&client_addr,&len);
					if(cfd < 0){
						perror("accept");
						goto SET_ERR;
					}
					printf("the client ip is %s\n",inet_ntoa(client_addr.sin_addr));
					FD_SET(cfd,&rfds);//将得到的客户端描述符添加到集合中
					maxfd = (maxfd<cfd)?(cfd):(maxfd);//更新最大的文件描述符
				}else{//客户端发送数据
					char buf[Max] = {0};
					memset(buf,0,sizeof(buf));
					//注意:这里接收数据的时候,第一个参数不能填fd,
					//如果填的fd,则只能接收到一个客户端发送的数据
					size_t nByte = recv(i,buf,sizeof(buf),0);
					if(nByte < 0){
						perror("recv");
						close(cfd);
						return Error;
					}else if(nByte == 0){
						printf("the client is disconnect...\n");
						FD_CLR(i,&rfds);//客户端退出之后,将其文件描述符从集合中去除
					}else{
						printf("%s\n",buf);
					}

				}
			}
		}
	}

	return 0;
SET_ERR:
	close(fd);
	return Error;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值