select()实现io多路复用

select()方法实现io多路复用实现服务器编程模型

本文通过pthread多线程以及io多路复用中的select()方法实现两种服务器编程的模型。

1.初始化模型

//定义fd fd默认block--阻塞状态
	int listenfd = socket(AF_INET,SOCK_STREAM,0);
	if (listenfd == -1 ) return -1;

	//定义网络
	struct sockaddr_in servaddr;
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 地址	//INADDR_ANY  --0.0.0.0    
	servaddr.sin_port = htons(8080); //绑定端口 

	if(-1 == bind( listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))){
		return -2;
	}
	listen(listenfd,10);

2.多线程

主函数

while(1){

		struct sockaddr_in client;
		socklen_t len = sizeof(client);
		int clientfd = accept(listenfd,(struct sockaddr*)&client,&len);

		pthread_t threadid;
		pthread_create(&threadid,NULL,routine,&clientfd);

	}

实现函数

void *routine(void *arg){

	int clientfd =*(int *)arg;
	
	while(1){
		
		unsigned char buffer[BUFFER_LENGTH]={0};
		int ret = recv(clientfd,buffer,BUFFER_LENGTH,0);
		if(ret == 0){
			close(clientfd);
			break;
		}
		
		printf("buffer : %s , ret: %d\n", buffer,ret);

		send(clientfd,buffer,ret,0);
	}
}

3. select 方法实现 io多路复用

	fd_set rfds,wfds,rset,wset;

	FD_ZERO(&rfds);
	FD_SET(listenfd,&rfds);


	FD_ZERO(&wfds);

	int maxfd=listenfd;
	int ret=0;
	unsigned char buffer[BUFFER_LENGTH]={0};

	//int fd,
	while(1){
		FD_SET(listenfd,&rfds);
		rset = rfds;
		wset = wfds;

		int nready = select(maxfd+1,&rset,&wset,NULL,NULL); //返回的是描述符数目
		if(FD_ISSET(listenfd,&rset)){
			struct sockaddr_in client;
			socklen_t len = sizeof(client);
			int clientfd = accept(listenfd,(struct sockaddr*)&client,&len);  //accept()会自动清空读取的事件
			FD_SET(clientfd,&rfds);
			if(clientfd>maxfd) maxfd=clientfd;
		}
		
		int i=0;
		
		for(i=3;i<=maxfd;i++){
		
			if(FD_ISSET(i,&rset)){    //判断具体已就绪的文件描述,在rset里面查第i位是否已经就绪
				memset(buffer, 0, sizeof(buffer));  //重置缓冲区
				ret = recv(i,buffer,sizeof(buffer)-1,0);
				if(ret == 0){
					close(i);
					FD_CLR(i,&rfds);
					//break; 
				}else if(ret >0){
					
					printf("buffer : %s , ret: %d\n", buffer,ret);
					
					FD_SET(i,&wfds);
				}
			}
			if(FD_ISSET(i,&wset)){
				ret =send(i,buffer,ret,0);
				FD_CLR(i,&wfds);
				FD_SET(i,&rfds);
			}
		}
		  
	}

4.代码整理

#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<fcntl.h>
#include<unistd.h>
#include<pthread.h>

#define BUFFER_LENGTH 128


void *routine(void *arg){

	int clientfd =*(int *)arg;
	
	while(1){
		
		unsigned char buffer[BUFFER_LENGTH]={0};
		int ret = recv(clientfd,buffer,BUFFER_LENGTH,0);
		if(ret == 0){
			close(clientfd);
			break;
		}
		
		printf("buffer : %s , ret: %d\n", buffer,ret);

		send(clientfd,buffer,ret,0);
	}
}

//0,1,2
//stdin,stdout,stderr

int main(){

	//定义fd fd默认block--阻塞状态
	int listenfd = socket(AF_INET,SOCK_STREAM,0);
	if (listenfd == -1 ) return -1;

	//定义网络
	struct sockaddr_in servaddr;
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 地址	//INADDR_ANY  --0.0.0.0    
	servaddr.sin_port = htons(8080); //绑定端口 --- 进酒店大堂

	if(-1 == bind( listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))){
		return -2;
	}


#if 0  //nonblock
	//将fd设置成非阻塞的
	int flag = fcntl(listenfd,F_GETFL,0);
	flag |= O_NONBLOCK;
	fcntl(listenfd,F_SETFL,flag);

#endif
	
	listen(listenfd,10);


#if 0  //还是只有单个accept()可以执行,只有一个close()了,才能开启另一个accept()
while(1)
{
	struct sockaddr_in client;
	socklen_t len = sizeof(client);
	int clientfd = accept(listenfd,(struct sockaddr*)&client,&len); //accept()就是bind()以后分配fd --- 找大堂服务员的过程
	while(1){
		
		unsigned char buffer[BUFFER_LENGTH]={0};
		int ret = recv(clientfd,buffer,BUFFER_LENGTH,0);
		if(ret == 0){
				close(clientfd);
				break;
		}
		
		printf("buffer : %s , ret: %d\n", buffer,ret);

		send(clientfd,buffer,ret,0); //send()>0不等于发送成功,只是说明发送到协议栈了
	}
}
	
	//printf("clientfd: %d\n",clientfd);
#elif 0

	while(1){

		struct sockaddr_in client;
		socklen_t len = sizeof(client);
		int clientfd = accept(listenfd,(struct sockaddr*)&client,&len);

		pthread_t threadid;
		pthread_create(&threadid,NULL,routine,&clientfd);

	}
	

#else  //io多路复用

	fd_set rfds,wfds,rset,wset;

	FD_ZERO(&rfds);
	FD_SET(listenfd,&rfds);


	FD_ZERO(&wfds);

	int maxfd=listenfd;
	int ret=0;
	unsigned char buffer[BUFFER_LENGTH]={0};

	//int fd,
	while(1){
		FD_SET(listenfd,&rfds);
		rset = rfds;
		wset = wfds;

		int nready = select(maxfd+1,&rset,&wset,NULL,NULL); //select()管理io的秘书,返回的是描述符数目
		if(FD_ISSET(listenfd,&rset)){
			struct sockaddr_in client;
			socklen_t len = sizeof(client);
			int clientfd = accept(listenfd,(struct sockaddr*)&client,&len);  //accept()会自动清空读取的事件
			FD_SET(clientfd,&rfds);
			if(clientfd>maxfd) maxfd=clientfd;
		}
		
		int i=0;
		
		for(i=3;i<=maxfd;i++){
		
			if(FD_ISSET(i,&rset)){    //判断具体已就绪的文件描述,在rset里面查第i位是否已经就绪
				memset(buffer, 0, sizeof(buffer));  //重置缓冲区
				ret = recv(i,buffer,sizeof(buffer)-1,0);
				if(ret == 0){
					close(i);
					FD_CLR(i,&rfds);
					//break; 
				}else if(ret >0){
					
					printf("buffer : %s , ret: %d\n", buffer,ret);
					
					FD_SET(i,&wfds);
				}
			}
			if(FD_ISSET(i,&wset)){
				ret =send(i,buffer,ret,0);
				FD_CLR(i,&wfds);
				FD_SET(i,&rfds);
			}
		}
		  
	}

	


#endif

	
}












5. 运行截图

客户端发送:
客户端
服务端接收:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值