select服务器

select服务器端代码:

#include"mylib.h"
/********************************************************************
	select应用场合
	1、当客户处理多个套接字时--标准IO和网络套接字
	2、如果一个TCP服务器既要处理监听套接字又要处理已连接的套接字
	3、如果一个服务器既要处理TCP,又要处理UDP
	4、如果一个服务器要处理多个服务或者多个协议(inetd)
	上百 上千个用户
	不管是在网络套接字的场合,还是读写文件描述符,均可以使用
********************************************************************/
int ipv4_tcp_creat_socket(void)
{
	int opt;
	socklen_t len;
	int listenfd;
	struct sockaddr_in server;

	if((listenfd = socket(AF_INET,SOCK_STREAM,0))<0)
	{
		perror("Creat socket failed\n");
		return -1;
	}
	
	//监听套接字,地址可重用
	if((setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)))<0)
	{
		perror("Error,set socket reuse addr failed\n");
		return -2;
	}
	
	bzero(&server,sizeof(server));
	server.sin_family	= AF_INET;
	server.sin_port = htons(SERV_PROT);
	//server.sin_addr.s_addr = inet_addr("127.0.0.1");
	//inet_pton(AF_INET,"127.0.0.1",&server.sin_addr);
	server.sin_addr.s_addr =  htonl(INADDR_ANY);
	
	len = sizeof(struct sockaddr);
	if(bind(listenfd,(struct sockaddr*)(&server),len)<0)
	{
		perror("bind error\n");
		return -3;
	}
	
	listen(listenfd,MAX_LISTEN_QUE);
	
	return listenfd;
}

	//阻塞式I/O
	//非阻塞式I/O
	//I/O复用(select/epoll)
	//信号驱动I/O
	//异步I/O

int main(int argc,char *argv[])
{
	int listenfd,sockfd;
	struct sockaddr_in server,client;
	socklen_t len;
	int bytes = 0;
	fd_set global_rdfs,current_rdfs;
	int maxfd;
	int i;
	char read_buf[MAX_BUFFER_SIZE];
	char send_buf[MAX_BUFFER_SIZE];
	int client_fd[FD_SETSIZE];

	len = sizeof(struct sockaddr_in);
	
	//1024
	printf("FD_SETSIZE=%d\n",FD_SETSIZE);
	
	listenfd = ipv4_tcp_creat_socket();
	
	FD_ZERO(&global_rdfs);
	FD_SET(listenfd,&global_rdfs);
	maxfd = listenfd;

	//-1:客户端断开
	for(i = 0; i  < FD_SETSIZE; i++)
		client_fd[i] = -1;

	while(1)
	{
		/*
		  int select(int nfds, fd_set *readfds, fd_set *writefds,
                     fd_set *exceptfds, struct timeval *timeout);
                     
          fd_set      结构类型 1024/32==32 
          nfds        要监听的最大的套接字数目+1
          readfds     select关心的要读数据的套接字组
          writefds    select关心的要写数据的套接字组
          exceptfds   出现异常的套接字组
       	  timeout     不阻塞 调用一次返回一次
       				  永远等待(NULL)直到我关心的套接字有数据读或写
         			  给一定时间等待
        */
        
		/*
		   //在set中清除fd
			 void FD_CLR(int fd, fd_set *set);
		   //检测fd在set中是否有数据可读
		     int  FD_ISSET(int fd, fd_set *set);
		   //将fd设置在set中
		     void FD_SET(int fd, fd_set *set);
		   //set清零
		     void FD_ZERO(fd_set *set);
		*/
		
		current_rdfs = global_rdfs;
		if(select(maxfd + 1,&current_rdfs,NULL,NULL,NULL)<0)
		{
			perror("select error\n");
			return RT_ERR;
		}
		
		//监听套接字
		if(FD_ISSET(listenfd,&current_rdfs))
		{
			if((sockfd = accept(listenfd,(struct sockaddr*)&client,(socklen_t*)&len))<0)
				{
					perror("accept error\n");
					return RT_ERR;
				}	

				printf("sockfd = %d\n",sockfd);
				printf("IP:%s,Port:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
		
				FD_CLR(listenfd,&current_rdfs);// listenfd  i
					
				//最大的套接字(数字)是当前通信的fd?
				maxfd = maxfd > sockfd ? maxfd : sockfd;
				//将sockfd放到关注的套接字集合里
				FD_SET(sockfd,&global_rdfs);// global_rdfs ? global_rdfs
				//continue;
					
				for(i = 0; i < maxfd; i++)
				{
					//常数放==前面 当少输一个=号时,会报错
					if(-1 == client_fd[i])
					{
						client_fd[i] = sockfd;
						break;
					}
				}	
		}
		
		//某些已经断开链接的客户端不用检查
		//去除0、1、2 标准输入 标准输出 标准错误
		//某一次recv读不完数据的解决
		
		//更Z—stack中OSAL的思路有点像
		for(i = 0; i <= maxfd; i++)
		{
			if(-1 == client_fd[i])//说明这个没连接/断开连接
			{
				continue;
			}
			if(FD_ISSET(client_fd[i],&current_rdfs))
			{	
				//不是监听套接字,如:某一个客户端有数据来请求,或者送数据到服务器中
				printf("read socket:%d\n",client_fd[i]);
				bytes = recv(client_fd[i],read_buf,MAX_BUFFER_SIZE,0);
				if(bytes < 0)
				{
					perror("recv error\n");
					return RT_ERR;
				}
				//最后一个字节FIN 客户端退出
				if(bytes == 0)
				{
					printf("client connect closed\n");
					//从关注的套接字集中清除
					FD_CLR(client_fd[i],&global_rdfs);
					//关闭此套接字 断开了
					close(client_fd[i]);
					client_fd[i] = -1;
					//继续,还有其他客户端要通信
					continue;
				}
				printf("read_buf:%s\n",read_buf);
				//strncpy(send_buf,"hello world! \n",strlen("hello world! \n"));
				send(client_fd[i],read_buf,strlen(read_buf),0);
				//send(client_fd[i],send_buf,strlen(send_buf),0);
				memset(read_buf,0,MAX_BUFFER_SIZE);
				//memset(send_buf,0,MAX_BUFFER_SIZE);
			}			
		}
	}
	return 0;
}

实验结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值