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

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

#define MAXFD 10

    这个函数用来初始化poll函数的第一个参数,这个参数是一个struct pollfd类型的结构体,结构体里面的三个成员分别存放着我们感兴趣的文件描述符、这个文件描述符注册的事件(我们关系的事件)、在这个描述符上实际发生的事(这个成员是由内核来填写的)。在初始化函数中,我们先将每一个结构体中的存放文件描述符的成员设置为”-1“,描述符注册事件成员设置为0 

void client_init(struct pollfd client[])
{
	int i = 0;
	for(; i < MAXFD; i++)
	{
		client[i].fd  = -1;
		client[i].events = 0;
	}
}

     这个函数用将我们感兴趣的文件描述符添加到结构体中让POLL函数监视,我们都知道,正确的文件描述符会是一个正整数,在初始化时,每个结构体中存放文件描述符的成员都被设置为了“-1”,所以只要某个结构体中存放文件描述符的成员初始值没有被改变,我们就可以将关心的文件描述符放进这个结构体中,同时设置对该文件描述符所关心的事件 

void client_add(struct pollfd client[], int fd)
{
	int i = 0;
	for( ; i < MAXFD; i++)
	{
		if( client[i].fd == -1)
		{
			client[i].fd = fd;
			client[i].events = POLLIN;//将事件设置是否读就绪
			return ;//注意;一旦发现有位置存放,存放完毕一定要退出循环,否则会将该描述符存放进每个空结构体中
		}
	}
}

     这个函数用来删除原本已被添加的文件描述符 

void client_del(struct pollfd client[], int fd)
{
	int i = 0;
	for( ; i <MAXFD; i++) //遍历整个结构体数组,找到我们想要删除的文件描述符的存放位置
	{
		if( client[i].fd == fd)
		{
			client[i].fd = -1; //找到后将存放文件描述符的成员设置为初始值“-1”
			client[i].events = 0;//将关心事件也设置为初始值“0”
			return ;//注意返回,原因同上
		}
	}
}

 下面是实现函数:

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

	struct sockaddr_in saddr, caddr;//创建两个IPV4专用地址结构体
	memset(&saddr, 0, sizeof(saddr));//清空地址结构里面存储的数据

	saddr.sin_family = AF_INET;//定义地址族
	saddr.sin_port = 6000;//定义端口号(要用网络字节序表示)
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");//定义IPV4地址(要用网络字节序表示)

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

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

	struct pollfd client[MAXFD];//定义poll函数的第一个参数(用来指定我们感兴趣的文件描述符上发生的事情)
	client_init(client);//初始化这个数组
	client_add(client, sockfd);//将我们自己的socket文件描述符添加进去

	while(1)
	{
		int n_ready = poll(client, MAXFD, 3000);//让poll函数开始监视client结构体数组
		if( n_ready == -1)//在一个时间周期内返回“-1”代表poll函数自身发生错误
		{
			printf("poll error1\n");
			continue;
		}
		if( n_ready == 0)//在一个时间周期内返回“0”,代表在这个时间周期内我们关心的所有文件描述符中没有一个就绪
		{
			printf("time out!\n");//打印一个超时提示
			continue;
		}
		int i = 0;//注意poll函数在监视过程中,返回的值只是就绪文件描述符的数量,我们并不知道是那个文件描述符就绪,因此要遍历寻找
		for( ; i < MAXFD; i++)//如果返回大于0的数,就开始遍历结构体数组,找到该文件描述符然后判断时有新的客户机连接还是已经
		{                     //连接上服务机的客户机有数据交互
			if(client[i].fd == -1) //以下代码分析详见博主“select实现服务机与多个客户机之间的交互”
			{
				continue;
			}
			if(client[i].revents & POLLIN)
			{
				if(client[i].fd == sockfd)
				{
					int len = sizeof(caddr);
					int c = accept(client[i].fd, (struct sockaddr*)&caddr, &len);
					if( c < 0)
					{
						printf("accept error!\n")
						coninue;
					}
					printf("accept = %d\n",c);
					client_add(client, c);
				}
				else
				{
					char buff[128] = {0};
					int num = recv(client[i].fd, buff, 127, 0);
					if( num <= 0)
					{
						close(client[i].fd);
						printf("one close!\n");
						client_del(client, client[i].fd);
					}
					else
					{
						printf("accept = %d  buff = %s\n",client[i].fd, buff);
						send(client[i].fd, "ok", 2, 0);
					}
				}
			}
		}
	}
	exit(0);
}

猜你喜欢

转载自blog.csdn.net/xing1584114471/article/details/83343021