7月14日作业
一、实现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};//设置地址族为ipv4addr.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;
}