7月14日作业

发布于:2025-07-15 ⋅ 阅读:(12) ⋅ 点赞:(0)

一、实现2个客户端之间互相聊天

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

服务端代码

#include <25041head.h>
struct Client
{
	int fd;  //发送者id端口号
	int id;  //接收者端口号
	char mtext[1024];  //发送的消息内容
};
struct Client_arr
{
	int fd;
	int id;
};


void insert_client(struct Client_arr *client_arr,int *client_arr_len,int newclient)
{
	client_arr[*client_arr_len].fd=newclient;
	client_arr[*client_arr_len].id=*(client_arr_len);

	(*client_arr_len)++;
}

void rm_client(struct Client_arr *client_arr,int *client_arr_len,int client)
{
	int i=0,j=0;
	for(i=0;i<*client_arr_len;i++)
	{			struct Client new_client;

		if(client_arr[i].fd==client)
			break;
	}
	for(j=i;j<*client_arr_len-1;j++)
	{
		client_arr[j]=client_arr[j+1];
	}
	(*client_arr_len)--;
}


int main(int argc, const char *argv[])
{
	if(argc<2)
	{
		printf("请输入端口号!\n");
		return 1;
	}
	short port=atoi(argv[1]);

	//创建服务器套接字
	int server=socket(AF_INET,SOCK_STREAM,0);

	//准备好要写入的套接字信息结构体,初始化信息
	struct sockaddr_in addr={0};
	//设置地址族为ipv4
	addr.sin_family=AF_INET;
	//设置监听端口号
	addr.sin_port=htons(port);
	//设定接收的ip地址,0为接收所有
	addr.sin_addr.s_addr=inet_addr("0.0.0.0");

	//将结构体信息(ip和port)写入套接字中
	if(bind(server,(struct sockaddr*)&addr,sizeof(addr))==-1)
	{
		perror("bind error..");
		return -1;
	}

	//监听
	listen(server,10);

	fd_set list;  //创建监视列表
	FD_ZERO(&list);  //初始化信息

	FD_SET(server,&list);  //服务器写入监视列表中
	FD_SET(0,&list);  //监视输入流
	int fd_count=3;

	//申请数组存客户端套接字
	struct Client client_msg={0};
	struct Client_arr client_arr[100]={0};
	int client_arr_len=0;

	while(1)
	{
		//备份监视列表,select激活列表和监视列表是同一张
		fd_set backup=list;	
		int fd;

		select(fd_count+1,&backup,NULL,NULL,NULL);  //list是监视列表,backup是激活列表

		if(FD_ISSET(server,&backup))
		{

			//接收
			//准备好客户端结构体
			struct sockaddr_in client_addr;
			int client_len = sizeof(client_addr);
			//接收客户端,返回客户端套接字
			int client = accept(server,(struct sockaddr*)&client_addr,&client_len);

			if(client==-1)
			{
				ERRLOG("客户端接受失败");
			}
			printf("有客户端连接成功!  ip=%s   port=%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
			if(client>fd_count)
				fd_count=client;

			//监视新客户端套接字,加入监视列表中
			FD_SET(client,&list);
			//将套接字加入数组中
			insert_client(client_arr,&client_arr_len,client);
		}
		if(FD_ISSET(0,&backup))
		{
			char buf[128]="";
			scanf("%s",buf);
			getchar();
			printf("键盘输入数据:%s\n",buf);
		}
		//某客户端激活,调用该客户端套接字读,遍历
		for(int i=0;i<client_arr_len;i++)
		{
			int client=client_arr[i].fd;
			if(FD_ISSET(client,&backup))
			{
				struct Client client_t;
				int res=read(client,&client_t,sizeof(struct Client));
				if(res==0)
				{
					printf("客户端断开连接!\n");
					FD_CLR(client,&list);   //监视列表移除该客户端
					rm_client(client_arr,&client_arr_len,client);   //数组中删除该客户端
					continue;
				}
				//转发消息
				printf("客户端发来消息:%s\n",client_t.mtext);
				for(int i=0;i<client_arr_len;i++)
				{
					if(client_t.id == client_arr[i].id && client_arr[i].fd != client)
					{
						write(client_arr[i].fd,&client_t,sizeof(struct Client));
						continue;
					}
				}
			}

		}
	}
	close(server);
	return 0;
}

客户端代码

#include <25041head.h>
struct Client
{
	int fd;  //发送者id端口号
	int id;  //接收者端口号
	char mtext[1024];  //发送的消息内容
};

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("127.0.0.1");

	int a=connect(client,(struct sockaddr*)&addr,sizeof(addr));
	if(a==-1)
	{
		printf("连接失败\n");
		return 0;
	}
	printf("链接成功\n");
	struct pollfd fds[2];

	// 初始化第一个元素(标准输入)
	fds[0].fd = 0;
	fds[0].events = POLLIN | POLLOUT;
	fds[0].revents = 0;

	// 初始化第二个元素(客户端套接字)
	fds[1].fd = client;
	fds[1].events = POLLIN;
	fds[1].revents = 0;

	while(1)
	{
		poll(fds,2,-1);
		if(fds[0].revents & POLLOUT)
		{
			printf("请输入接收消息客户端的id:");
			fflush(stdout);
			fds[0].events=POLLIN;
			continue;
		}
		if(fds[0].revents & POLLIN)
		{
			int id_t;
			char mtext_t[128]="";
			scanf("%d",&id_t);
			printf("请输入发送的消息内容:");
			scanf("%s",mtext_t);
			getchar();
			struct Client client_msg={.fd=client,.id=id_t};
			strcpy(client_msg.mtext,mtext_t);
			write(client,&client_msg,sizeof(struct Client));
			fds[0].events=POLLIN|POLLOUT;

		}
		if(fds[1].revents & POLLIN)  // 接收消息
		{
			struct Client rev_msg;
			int res=read(client,&rev_msg,sizeof(struct Client));
			if(res==0)
			{
				printf("客户端已断开\n");
				break;
			}

			printf("接收到来自客户端id为:%d 的消息:%s\n",rev_msg.id,rev_msg.mtext);
			continue;
		}

	}
	close(client);    
	return 0;
}

运行结果


网站公告

今日签到

点亮在社区的每一天
去签到