多客户端——服务器结构

服务器使用 select 模型实现接受多个客户端连接,以及转发消息
客户端要求:使用 poll 模型解决 技能够 read 读取服务器发来的消息,又能够scanf读取键盘输入的信息 客户端服务器不开启额外线程和进程

服务器代码:

#include <my_head.h>
#define MAX_CLIENTS 10
#define BUFFER_SIZE 128
void insert_client(int * client_arr,int *client_arr_len,int new_client)
{
	if((*client_arr_len) >= MAX_CLIENTS)
	{
		printf("client list full\n");
		return ;
	}
	client_arr[*client_arr_len] = new_client;
	(*client_arr_len) ++;
	return ;
}

void remove_client(int * client_arr,int * client_arr_len,int tar_client)
{
	int i,j,len = * client_arr_len;
	for(i=0;i<len;i++)
	{
		if(tar_client == client_arr[i])
		{
			for(j=i;j<len-1;j++)
			{
				client_arr[j] = client_arr[j+1];
			}
			client_arr[j] = -1;
			(*client_arr_len) --;
			break ;
		}
	}
	return ;
}

int main(int argc, const char *argv[])
{
	if(argc<2)
	{
		printf("请输入端口号\n");
		return -1;
	}
	//将外部传入字符串转化成整数
	short port = atoi(argv[1]);
	//创建服务器端套接字
	int sever = socket(AF_INET,SOCK_STREAM,0);
	if(-1==sever)
	{
		ERRORLOG("socket failed");
		return -1;
	}

	struct sockaddr_in addr = {0};
	addr.sin_family=AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr("0.0.0.0");  // 绑定到所有接口

	if(-1==bind(sever,(struct sockaddr *)&addr,sizeof(addr)))
	{
		perror("bind error");
		close(sever);
		return -2;
	}

	listen(sever,10);  //监控服务器
	printf("sever started on port\n");

	int client_arr[MAX_CLIENTS] = {0};
	int client_arr_len = 0;

	//创建接收客户消息监视列表和激活列表
	fd_set readfds,backup_readfds;  
	//监视列表清零
	FD_ZERO(&readfds);
	FD_SET(sever,&readfds);
	int max_fd = sever;

	while(1)
	{
		int activity = select(max_fd + 1, &backup_readfds, NULL, NULL, NULL);
		if (activity < 0) 
		{
			perror("select error");
			continue;
		}

		// 检查是否有新客户端连接
		if (FD_ISSET(sever, &backup_readfds)) 
		{
			struct sockaddr_in client_addr;
			socklen_t client_len = sizeof(client_addr);
			int client = accept(sever, (struct sockaddr *)&client_addr, &client_len);
			if (client == -1) 
			{
				perror("accept failed");
				continue;
			}
			printf("新客户端ip=%s\n",inet_ntoa(client_addr.sin_addr));
			printf("新客户端port=%d\n",ntohs(client_addr.sin_port));

			//将新客户端存入客户端数组,用于后续遍历数组查找目标客户端
			insert_client(client_arr,&client_arr_len,client);
			//将客户端加入监视列表
			FD_SET(client,&readfds); 

			if (client > max_fd) max_fd = client;
		}

		//遍历客户端数组,找出当前连接的客户端
		for(int i=0;i<client_arr_len;i++)
		{
			int client = client_arr[i];
			if(FD_ISSET(client,&backup_readfds))
			{
				char buf[BUFFER_SIZE] = {0};
				ssize_t res = read(client,buf,sizeof(buf));
					if(res<=0)
					{
						close(client);
						printf("客户端断开连接\n");
						//从监视列表中删除断开客户端
						FD_CLR(client,&readfds);
						//从客户端数组中删除断开客户端
						remove_client(client_arr,&client_arr_len,client);
					}
					else 
					{
						printf("客户端发来消息:%s\n",buf);
						for(int j=0;j<client_arr_len;j++)
						{
							int dest_fd = client_arr[j];
							if(client != dest_fd)
							{
								write(client,buf,res);
							}
						}
					}
			}
		}
	}
	close(sever);

	return 0;
}

客户端代码:

 

#include <my_head.h>
#define MAX_CLIENT 10
#define BUFFER_SIZE 128

void insert_fd(struct pollfd* list,int* list_len,struct pollfd new_fd)
{
	if((*list_len) >= MAX_CLIENT)
	{
		printf("poll list full");
	}
	list[*list_len] = new_fd;
	(*list_len) ++;
	return ;
}

void remove_fd(struct pollfd *list,int *list_len,int tar_fd)
{
	int i,j,len = *list_len;
	for(i=0;i<len;i++)
	{
		if(list[i].fd == tar_fd)
		{
			for(j=0;j<len-1;j++)
			{
				list[j] = list[j+1];
			}
			(*list_len)--;
			break;
		}
	}
	return ;
}

int main(int argc, const char *argv[])
{
	if(argc<2)
	{
		printf("请输入端口号\n");
		return -1;
	}
	//将外部传入字符串转化成整数
	short port = atoi(argv[1]);

	int client = socket(AF_INET,SOCK_STREAM,0);

	struct sockaddr_in addr = {0};
	addr.sin_family=AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr("192.168.127.97");

	connect(client,(struct sockaddr*)&addr,sizeof(addr));
	listen(client,10);

	struct pollfd list[MAX_CLIENT] = {0};
	int list_len = 0;

	struct pollfd poll_client ={.fd =client ,.events = POLL_OUT, .revents = 0 };

	insert_fd(list,&list_len,poll_client);

	char buf[BUFFER_SIZE];
	while(1)
	{	
		memset(buf,0,BUFFER_SIZE);
		poll(list,list_len,-1);
		for(int i=0;i<list_len;i++)
		{
			if(list[i].revents == 0)
			{ continue ;}
			else if(list[i].revents == POLL_OUT)
			{
				printf("请输入消息:\n");
				scanf("%s",buf);
				getchar();

				//写入数据
				write(client,buf,BUFFER_SIZE);
			}
			else if(list[i].revents == POLL_IN)
			{
				//读取数据
				read(client,buf,BUFFER_SIZE);
				printf("接收消息: %s\n",buf);
			}
		}
	}
	close(client);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值