select实现多个客户机与服务器之间的通讯

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

#define MAXFD 10 //数组的大小用宏定义出来

void fds_init(int fds[])//  初始化存放描述符的数组,因为正确的描述符都是正数,
{                       //所以将数组中的所有元素都初始化为“-1”
	int i = 0;
	for( ; i < MAXFD; i++)
	{
		fds[i] = -1;
	}
}

void fds_add(int fds[], fd)//将新描述符添加到描述符数组中
{
	int i = 0;
	for(; i < MAXFD; i++)//   遍历数组中,只要发现某一个元素是初始值,就代表没有描述符存放
	{                    //在该位置,所以就可以将新的描述符存进去
		if( fds[i] == -1)
		{
			fds[i] = fd;//   注意,一旦找到存放位置,在存放后就要返回,如果不返回,i就会遍历
			return ;    //整个数组,将新描述符存进整个数组
		}
	}
}

void fds_del(int fds[], int fd)//将数组中与fd相等的描述符删除
{
	int i = 0;
	for( ; i < MAXFD; i++)
	{
		if(fds[i] == fd)
		{
			fds[i] = -1;//注意,一旦找到目标描述符,删掉后就要返回。原因同存放描述符函数相同
			return ;
		}
	}
}

int main()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0); //创建套接字
	assert(sockfd != NULL);

	struct sockaddr_in saddr, caddr;
	memset(&saddr, 0, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(6000);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //设置协议族端口号和ip地址

	int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); //命名套接字
	assert(res != -1);

	listen(sockfd, 5);//监听套接字

	int fds[MAXFD];//定义一个数组用来存放描述符
	fds_init(fds);//初始化数组
	fds_add(fds, sockfd);//将sockfd先放进数组中

	while(1)
	{
		fd_set fdset; //给select创建一个“集合”
		FD_ZERO(&fdset);//把集合里面清零

		int maxfd = -1; //设置一个记录最大描述符的变量,可以告诉select函数监视的描述符范围,减少时间浪费
		int i = 0;
		for( ; i < MAXFD; i++)//开始遍历整个存放描述符的数组
		{ 
			if(fds[i] == -1)//    如果数组中的元素的初始值被更改,见代表存放了描述符进去,如果初始值没有
			{               //更改,就代表没有存放描述符进去,
				break;
			}
			FD_SET(fds[i], &fdset);//按照描述符的大小在集合中偏移后做标记
			if(fds[i] > maxfd)
			{
				maxfd = fds[i]; //集合中每进入一个描述符就将最大的用maxfd标记起来
			}
		}
		struct timeval tv = {5, 0}; //给设置select函数设置监视集合的时间
		int n = select(maxfd + 1, &fdset, NULL, NULL, &tv); //时间、大小、描述符存放好后开始让select监视
		if( n == -1) //如果在规定的时间内select返回值为“-1”就代表select函数发生错误,返回去继续监视集合
		{
			printf("select error!\n");
			continue;
		}
		if( n == 0)// 如果在规定时间内select监视的集合中没有描述符上面有数据传来,就打超时时一次,继续监视
		{          
			printf("time out!\n");
			continue;
		}
		else//如果在监视的时间内发现有描述符传来数据
		{
			int i = 0;
			for( ; i < MAXFD; i++)//遍历整个数组,找存放的描述符
			{
				if(fds[i] == -1)
				{
					continue;
				}
				if(FD_ISSET(fds[i] , &fdset))//每找到一个描述符就测试fds[i]是否可读,即是否网络上有数据 
				{
					if(fds[i] == sockfd)//   如果fds[i]有数据传来 ,就得判断是新连接的客户机还是已经建立
					{                   //连接的客户机发送给服务器的数据
						int len = sizeof(caddr);
						int c = accept(sockfd, (struct sockaddr*)caddr, &len); //   如果是新连接的客户机,
						if( c < 0)                                             //就将客户机的端口号(文件
						{                                                      //描述符)存在数组中
							continue;
						}
						printf("accept = %d\n",c); //将新客户机描述符打印出
						fds_add(fds, c); //将新文件描述符存进数组
					}
					else
					{
						char buff[128] = {0};   //如果是已经建立连接的客户机发来的数据,就将数据接收在buff中
						int num = recv(fds[i], buff, 127, 0);
						if( num <= 0) //   如果recv接受数据后返回错误值“-1”就代表接收失败,如果返回“0”
						{             //就代表客户端断开了与服务器的连接,此时服务器端也断开个描述符所对应的客户端
							close(fds[i]);
							fds_del(fds, fds[i]); //将这个描述符从数组中拿出去丢掉
							printf("one client over!\n");
						}
						else
						{
							printf("recv(%d) = %s\n",fds[i],buff);//如果recv返回的值大于0,就代表客户端发来了数据,输出服务器发送来数据
							send(fds[i], "ok", 2, 0);//给客户端发送数据接收成功确认
						}
					}
				}
			}
		}
	}
	exit(0);
}

猜你喜欢

转载自blog.csdn.net/xing1584114471/article/details/83215953
今日推荐