【Linux学习笔记57】TCP服务器IO模型之多路复用(二)SELECT函数

引言

接着上一篇笔记的POLL函数实现TCP服务器的多路复用,本篇笔记主要是以SELECT函数实现服务器的多路复用。

实现思路

在这里插入图片描述

  • socket中有四个描述符,本质上都是阻塞,并且阻塞在一个端点上
  • 因此,使用文件描述符集合fd_set将关心的描述符放入集合中
  • 然后通过使用Select函数监听这个集合中的描述符
  • Select函数会不断的变化,因此当有连接来访问以及数据同时访问的时候,listenfd 以及 fd2就有反应了
  • Select函数中的描述符集合只留下了listenfd,fd2,然后通过使用FS_ISSET()检测Select函数中的描述符集合中的有效描述符
  • 通过检测得到listenfd,fd2,描述符有效。就会返回accept以及read。

select函数解剖
在这里插入图片描述

  • select函数总共有三个描述符集合:读就绪,写就绪,异常就绪
  • 然后将描述符关心的状态填入对应级集合即可。比如,关心listenfd的读状态,就把listenfd放入读就绪集合,如果fd2的读,写都关心,那么就要把fd2放入读就绪集合的同时也要放入写就绪的集合。如此类推

代码实现

server.c:

#include "head4sock.h"

int main(int argc, char **argv)
{
	if (argc !=2)
	{
		printf("Usage:%s <PORT>\n", argv[0]);
		exit(0);
	}
	int fd = Socket(AF_INET,SOCK_STREAM,0);

	struct sockaddr_in srvaddr;
	socklen_t len =sizeof(srvaddr);
	bzero(&srvaddr,len);

	srvaddr.sin_family = AF_INET;
	srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	srvaddr.sin_port = htons(atoi(argv[1]));

	Bind(fd,(struct sockaddr *)&srvaddr,len);
	Listen(fd,3);
	int connfd = Accept(fd,NULL,NULL);

	fd_set rset; //定义读就绪集合
	char buf[SIZE];
	while (1)
	{
		FD_ZERO(&rset); //集合清零
		FD_SET(connfd,&rset);//将connfd加入读就绪集合
		FD_SET(STDIN_FILENO,&rset);//将标准读操作加入读就绪集合
		select(connfd+1,&rset,NULL,NULL,NULL); //最大文件描述符+1,读就绪描述符集合,写就绪,异常就绪,超时就绪

		bzero(buf,SIZE);
		if(FD_ISSET(connfd,&rset)) //判断是否依然存在connfd,有无客户端数据
		{
			if(Read(connfd,buf,SIZE)==0)
			break;

			printf("from client:%s", buf);
		}
		if (FD_ISSET(STDIN_FILENO,&rset)) //判断键盘是否输入数据
		{
			fgets(buf,SIZE,stdin);
			write(connfd,buf,strlen(buf));
		}
	}
	close(fd);
	close(connfd);
	return 0;
}

client.c:

#include "head4sock.h"

int main(int argc, char *argv[])
{
	if (argc !=3)
	{
		printf("Usage:%s <IP> <PORT>\n", argv[0]);
		exit(0);
	}
	int fd = Socket(AF_INET,SOCK_STREAM,0);

	struct sockaddr_in srvaddr;
	socklen_t len =sizeof(srvaddr);
	bzero(&srvaddr,len);

	srvaddr.sin_family = AF_INET;
	inet_pton(AF_INET,argv[1],&srvaddr.sin_addr);
	srvaddr.sin_port = htons(atoi(argv[2]));

	Connect(fd,(struct sockaddr *)&srvaddr,len);

	fd_set rset; //定义读就绪集合
	char buf[SIZE];
	while (1)
	{
		FD_ZERO(&rset); //集合清零
		FD_SET(fd,&rset);//将connfd加入读就绪集合
		FD_SET(STDIN_FILENO,&rset);//将标准读操作加入读就绪集合
		select(fd+1,&rset,NULL,NULL,NULL); //最大文件描述符+1,读就绪描述符集合,写就绪,异常就绪,超时就绪

		bzero(buf,SIZE);
		if(FD_ISSET(fd,&rset)) //判断是否依然存在fd,有无服务器数据
		{
			if(Read(fd,buf,SIZE)==0)
			break;

			printf("from client:%s",buf);
		}
		if (FD_ISSET(STDIN_FILENO,&rset)) //判断键盘是否输入数据
		{
			fgets(buf,SIZE,stdin);
			write(fd,buf,strlen(buf));
		}
	}
	close(fd);
	return 0;
}

代码运行结果:

在这里插入图片描述

发布了91 篇原创文章 · 获赞 17 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/RayCongLiang/article/details/101062856
今日推荐