select函数 异步多路复用

使用select函数就可以实现非阻塞编程:异步

//服务端


/*fd_set其实这是一个数组的宏定义,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(socket、文件、管道、设备等)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪个句柄可读。*/



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

#define N 64

typedef struct sockaddr SA;

int main(int argc, char *argv[])
{
	int listenfd, connfd;
	char buf[N];
	fd_set global_rdfs, current_rdfs;
	struct sockaddr_in myaddr;

	if (argc < 3)
	{
		printf("Usage : %s <my_ip> <my_port>\n", argv[0]);
		exit(-1);
	}

	if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("fail to socket");
		exit(-1);
	}

	bzero(&myaddr, sizeof(myaddr));
	myaddr.sin_family = PF_INET;
	myaddr.sin_port = htons(atoi(argv[2]));
	myaddr.sin_addr.s_addr = inet_addr(argv[1]);

	if ((bind(listenfd, (SA *)&myaddr, sizeof(myaddr))) < 0)
	{
		perror("fail to bind");
		exit(-1);
	}

	if (listen(listenfd, 5) < 0)
	{
		perror("fail to listen");
		exit(-1);
	}

	FD_ZERO(&global_rdfs);
	
	FD_SET(listenfd, &global_rdfs);
	
	int i, maxfd = listenfd;

	while ( 1 )
	{
		//一开始 默认listenfd 准备就绪 select筛选listenfd
		current_rdfs = global_rdfs; 
		
		printf("select start...\n");
		
		/*再此阻塞 直到 i/o准备就绪 即客户端请求连接了*/
		
		if (select(maxfd+1, ¤t_rdfs, NULL, NULL, NULL) < 0)
		{
			perror("fail to select");
			exit(-1);
		}
		
		printf("select after...\n");
		
		printf("maxfd = %d\n", maxfd);
		
		#if 1
		/* 证明了由内核根据IO状态修改current_rdfs的内容,由此来通知执行了select()的进程哪个句柄可读*/
		if (FD_ISSET(3, ¤t_rdfs)) {
			printf("3 in current_rdfs\n");
		}else printf("3 is not in current_rdfs\n");
		
		if (FD_ISSET(3, &global_rdfs)) {
			printf("3 in global_rdfs\n");
		}else printf("3 is not in global_rdfs\n");
		
		#endif
		
		for (i=0; i <= maxfd; i++)
		{
			printf("for times %d...\n", i);
			
			if (FD_ISSET(i, ¤t_rdfs))
			{
				printf("i = %d in current_rdfs...\n", i);
				
				if (i == listenfd)
				{
					printf("i == %d listenfd start...\n", i);
					
					if ((connfd = accept(i, NULL, NULL)) < 0)
					{
						perror("fail to accept");
						continue;
					}
					
					printf("kkkkk...connfd = %d\n", connfd);
					
					FD_SET(connfd, &global_rdfs);
					
					//maxfd 改变
					maxfd = (maxfd < connfd ? connfd : maxfd);
				}
				else
				{
					printf("listenfd = %d .... ooooo\n" , listenfd);
					
					if (recv(i, buf, N, 0) <= 0)
					{
						close(i); //掉线关闭套接字描述符
						FD_CLR(i, &global_rdfs); //清楚集合中的该套接字描述符
						printf("peer closed\n");
					}
					else
					{
						send(i, buf, N, 0);
					}
				}
			}
		}   // end for
		printf("start while()...\n");
	}  // end while

	return 0;
}
//客户端

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

#define N 64

typedef struct sockaddr SA;

int main(int argc, char *argv[])
{
	int sockfd;
	char buf[N];
	struct sockaddr_in servaddr;

	if (argc < 3)
	{
		printf("Usage : %s <serv_ip> <serv_port>\n", argv[0]);
		exit(-1);
	}

	if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("fail to socket");
		exit(-1);
	}

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = PF_INET;
	servaddr.sin_port = htons(atoi(argv[2]));
	servaddr.sin_addr.s_addr = inet_addr(argv[1]);

	if ((connect(sockfd, (SA *)&servaddr, sizeof(servaddr))) < 0)
	{
		perror("fail to connect");
		exit(-1);
	}

	while ( 1 )
	{
		printf("<client> ");
		fgets(buf, N, stdin);
		if (strncmp(buf, "quit", 4) == 0)
		{
			close(sockfd);
			exit(0);
		}
		printf("sockfd = %d\n",sockfd);
		send(sockfd, buf, N, 0);
		
		recv(sockfd, buf, N, 0);
		
		printf("<from server> %s", buf);
	}

	return 0;
}

分析以上select函数: 当建立连接时

    再建立一个连接时:



可以方发现多个连接:服务端监听套接字不变,连接套接字 4 5 6...... 加, 并且当某一个连接的客户 io准备就绪时,内核会选择具体的current_rdfs集合中的某一个,实现多路复用。

比如 :客户端套接字3 与服务端连接套接字5的通信:


比如:客户端套接字3 与服务端连接套接字4的通信

另外扩展:点击打开链接


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值