并发服务器 select 编程

1,并发服务器,通过多路IO复用,能使得一个进程同时处理多路IO,提升服务器吞吐量。 在Linux支持epoll模型之前,都使用select/poll模型来实现IO多路复用。 
   Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、 accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发 生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。 
可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返 回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高) 方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况–读写或是异常。

 #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);

       void FD_CLR(int fd, fd_set *set);
       int  FD_ISSET(int fd, fd_set *set);
       void FD_SET(int fd, fd_set *set);
       void FD_ZERO(fd_set *set);

       #include <sys/select.h>

       int pselect(int nfds, fd_set *readfds, fd_set *writefds,
                   fd_set *exceptfds, const struct timespec *timeout,
                   const sigset_t *sigmask);

select 返回值

负值:select错误 
正值:某些文件可读写或出错 
0:等待超时,没有可读写或错误的文件 

 

服务端代码:

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

#define PORT  12345

typedef struct client_st {
	int fd;
	struct sockaddr_in caddr;
}Client;

char *s = "10.9.0.152";

int main(int argc, char *argv[])
{
	int listenfd,maxfd;
	int newfd;
	int ret,i,j,maxi,n;
	Client clients[1024];											//定义select支持的最多的套接字

	struct sockaddr_in seraddr;
	int ser_len = sizeof(struct sockaddr_in);
	
	listenfd = socket(AF_INET, SOCK_STREAM, 0);
	if(listenfd < 0) {
		perror("listenfd socket error");
		exit(-1);
	}
	
	printf("listenfd = %d\n",listenfd);
	
	memset(&seraddr,0,sizeof(struct sockaddr_in));
	seraddr.sin_family  = AF_INET;
	seraddr.sin_port	= htons(PORT);
	seraddr.sin_addr.s_addr = inet_addr(s);
	
	
	if(bind(listenfd,(struct sockaddr *)&seraddr,ser_len) == -1) {
		perror("bind error");
		exit(-1);
	}
	
	if(listen(listenfd, 5) == -1) {										//监听队列最多容纳5个
		perror("listen error");
		exit(-1);
	}

	char buff[1024]; 													//数据接收缓冲区
	fd_set allset;														//套接字操作符
	fd_set fdsr;														
	
								
	FD_ZERO(&allset);	
	FD_SET(listenfd,&allset);						 					//将监听套接字加入轮询
	maxfd = listenfd;
	maxi  = -1;
	
	for(i = 0; i < 1024; i++) {
		clients[i].fd = -1;
	}
	
	while(1) {
		struct sockaddr_in cusaddr;
		fdsr = allset;
		ret = select(maxfd + 1, &fdsr,NULL,NULL,0);	  					  //无限期阻塞,并测试文件描述符变动 
		if(ret < 1) {
			perror("select error");
		}
		printf("Select() break and the return num is %d. \n", ret);
		
		if(FD_ISSET(listenfd,&fdsr)) {									  //有新的连接到来
			printf("Accept a connection.\n"); 			
			int cus_len = sizeof(struct sockaddr_in);
			newfd = accept(listenfd,(struct sockaddr *)&cusaddr,&cus_len);
			printf("newfd %d\n",newfd);
			if(newfd < 0) {
				perror("accept error");
				continue;
			}
			
			for(j = 0; j < 256; j++) {									  //将新客户端加入到数组
				if(clients[j].fd < 0) {
					clients[j].fd = newfd;
					clients[j].caddr = cusaddr;
					break;
				}
			}
			FD_SET(newfd,&allset);										  //将client的套接字加入轮询
			if(newfd > maxfd)
				maxfd = newfd;
			if(j > maxi)
				maxi = j;
			if (--ret <= 0)  
                continue;       //如果没有新客户端连接,继续循环   
		}

		for(i = 0; i <(maxi + 1);i++)
		{
			if(clients[i].fd < 0)
				continue;
			
			if(FD_ISSET(clients[i].fd,&fdsr)) {
				printf("Receive from connect fd[%d].\n", i);  
				memset(buff,0,sizeof(buff));
                if ((n = recv(clients[i].fd, buff, sizeof(buff),0)) == 0)  
				{               											//从客户端socket读数据,等于0表示网络中断   
                    close(clients[i].fd);  									//关闭socket连接   
                    FD_CLR(clients[i].fd, &fdsr);    					    //从监听集合中删除此socket连接   
                    clients[i].fd = -1;  									//数组元素设初始值,表示没客户端连接   
                }  
                else  
                    printf("recv buff is %s\n",buff); 				//打印  
                if (--ret <= 0)  
                    break;      //如果没有新客户端有数据,跳出for循环回到while循环  
			}		
		}
	}
	
	close(newfd);
	close(listenfd);
			
	return 0;	
}

客户端:可以使用Windows 网络助手

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值