思维导图
作业
使用UDP实现一个多人简易聊天室
服务器
#include#define SER_PORT 8080 #define SER_IP "10.102.145.16" /* #define SER_IP "192.168.122.153" */ #define LOGIN 0; //表示登录消息 #define CHAT 1; //表示聊天消息 #define LOGOUT 2; //表示退出消息 //定义关于客户端发送信息的结构体 typedef struct M{ int message_type; //表示所发送的消息类型 char user_name[20]; //表示用户名 char text[128]; //用来存储所发送的消息 }Msg,*pMsg; enum A { FALSE=-1, SUCCESS }; typedef struct Node { char user_name[20]; int state;//表示这个客户端此时的状态,0为不在线,1为在线 struct sockaddr_in cin;//存储客户端的信息 struct Node *next; }*p_cli_msg,cli_msg; int null(p_cli_msg p) //判断链表是否为空函数 { return p==NULL?FALSE:SUCCESS; } p_cli_msg create_node() //创建节点函数 { p_cli_msg p=(p_cli_msg)malloc(sizeof(cli_msg)); if(null(p)) return NULL; strcpy(p->user_name,""); p->state=-1; p->next=NULL; return p; } p_cli_msg insert_head(p_cli_msg head,struct sockaddr_in cin,const char *name,int state)//头插函数 { p_cli_msg s=create_node(); if(null(s)) return head; strcpy(s->user_name,name); s->state=state; s->cin=cin; s->next=head; head=s; return head; } int main(int argc, char const *argv[]) { //创建套接字 int sfd=socket(AF_INET,SOCK_DGRAM,0); if(sfd==-1) { perror(""); return -1; } printf("sfd=%d\n",sfd); //将端口号快速重用函数 int reuse =1; if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1) { perror("setsockopt error"); return -1; } printf("端口号快速重用成功\n"); //绑定 struct sockaddr_in sin; sin.sin_family=AF_INET; sin.sin_port=htons(SER_PORT); sin.sin_addr.s_addr=inet_addr(SER_IP); if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1) { perror("bind error"); return -1; } //定义一个存储消息的容器 Msg msg_buf; msg_buf.message_type=-1;//初始化一下消息类型 //定义一个存放客户端地址信息的结构体 p_cli_msg Cli_addr_head=NULL; //用来存储cin大小 struct sockaddr_in cin; int cin_len=sizeof(cin); //准备一个文件描述符容器 fd_set readfds,tempfds; //清空容器 FD_ZERO(&readfds); //将要监测的文件描述符放入集合 FD_SET(0,&readfds); FD_SET(sfd,&readfds); while(1) { tempfds = readfds; //使用select函数对容器中的文件描述符进行赋值 int res = select(sfd+1,&tempfds,0,0,0); if(res==-1) { perror("select error"); return -1; } else if(res==0) { printf("timeout\n"); return -1; } for(int fd=0;fd<=sfd;fd++) { if(!FD_ISSET(fd,&tempfds)) { continue; } if(fd==0) { char wbuf[128]=""; scanf("%s",wbuf); printf("触发了终端输入事件---\n"); if(strcmp(wbuf,"quit")==0) { strcpy(msg_buf.user_name,"--system--"); strcpy(msg_buf.text,"已退出"); p_cli_msg p=Cli_addr_head; while(p!=NULL) { if(p->state==1) { sendto(sfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin)); } p=p->next; } goto END; } //将消息发送给所有客户端 strcpy(msg_buf.text,wbuf); strcpy(msg_buf.user_name,"--system--"); p_cli_msg p=Cli_addr_head; while(p!=NULL) { if(p->state==1) { sendto(sfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin)); } p=p->next; } } if(fd==sfd) { //bzero(recv_buf->data.text,sizeof(recv_buf->data.text)); //读取信息结构体,并将客户端地址信息结构体存储到cin中 recvfrom(fd,&(msg_buf),sizeof(msg_buf),0,(struct sockaddr*)&(cin),&cin_len); if(msg_buf.message_type==0) { Cli_addr_head=insert_head(Cli_addr_head,cin,msg_buf.user_name,1); p_cli_msg p=Cli_addr_head; strcpy(msg_buf.text,"已上线"); while(p!=NULL) { if(p->state==1) { sendto(fd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin)); } p=p->next; } printf("[%s]已上线\n",msg_buf.user_name); } else if(msg_buf.message_type==2) { p_cli_msg p=Cli_addr_head; strcpy(msg_buf.text,"已下线"); while(p!=NULL) { if(p->user_name==msg_buf.user_name) { p->state=0; break; } } p=Cli_addr_head; while(p!=NULL) { if(p->state==1) { sendto(fd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin)); } p=p->next; } } else if(msg_buf.message_type==1) { p_cli_msg p=Cli_addr_head; while(p!=NULL) { if(p->state==1) { sendto(fd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin)); } p=p->next; } } } } } END: close(sfd); return 0; }
客户端
#include#define SER_PORT 8080 #define SER_IP "10.102.145.16" /* #define SER_IP "192.168.122.153" */ #define LOGIN 0; //表示登录消息 #define CHAT 1; //表示聊天消息 #define LOGOUT 2; //表示退出消息 //定义关于客户端发送信息的结构体 typedef struct M{ int message_type; //表示所发送的消息类型 char user_name[20]; //表示客户端的用户名 char text[128]; //用来存储客户端所发送的消息 }Msg,*pMsg; int main(int argc, char const *argv[]) { Msg msg_buf; char local_user_name[20]; printf("请输入用户名>>>"); scanf("%s",local_user_name); strcpy(msg_buf.user_name,local_user_name); msg_buf.message_type=-1;//初始化一下消息类型 //创建套接字 int cfd=socket(AF_INET,SOCK_DGRAM,0); if(cfd==-1) { perror(""); return -1; } printf("cfd=%d\n",cfd); //绑定 /* struct sockaddr_in cin; cin.sin_family=AF_INET; cin.sin_port=htons(SER_PORT); cin.sin_addr.s_addr=inet_addr(SER_IP); */ //填充服务器地址信息结构体 struct sockaddr_in sin; sin.sin_family=AF_INET; sin.sin_port=htons(SER_PORT); sin.sin_addr.s_addr=inet_addr(SER_IP); int ser_len=sizeof(sin); //准备一个文件描述符容器 fd_set readfds,tempfds; //清空容器 FD_ZERO(&readfds); //将要监测的文件描述符放入集合 FD_SET(0,&readfds); FD_SET(cfd,&readfds); //4.收发数据 msg_buf.message_type=0; sendto(cfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&sin,sizeof(sin)); puts("aaa"); while (1) { tempfds = readfds; //使用select函数对容器中的文件描述符进行赋值 int res = select(cfd+1,&tempfds,0,0,0); if(res==-1) { perror("select error"); return -1; } else if(res==0) { printf("timeout\n"); return -1; } for(int fd=0;fd<=cfd;fd++) { if(!FD_ISSET(fd,&tempfds)) { continue; } if(fd==cfd) { //接收服务器发送的消息 recvfrom(cfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&sin,&ser_len); //自己的消息就不要打印出来了 if(strcmp(msg_buf.user_name,local_user_name)!=0) { printf("[%s]:%s\n",msg_buf.user_name,msg_buf.text); } } else if(fd==0) { //因为收服务器发来的消息时,用户名会发生改变,所以我们先重置一下用户名 strcpy(msg_buf.user_name,local_user_name); printf("请输入>>>"); fgets(msg_buf.text,sizeof(msg_buf.text),stdin); msg_buf.text[strlen(msg_buf.text)-1]='\0'; //发送给服务器 msg_buf.message_type=1; if (strcmp(msg_buf.text,"quit")==0) { msg_buf.message_type=2; sendto(cfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&sin,ser_len); goto END; } sendto(cfd,&msg_buf,sizeof(msg_buf),0,(struct sockaddr*)&sin,ser_len); } } } END: close(cfd); return 0; }
不过,这段代码还是有些许问题,改进中。。。
猜你喜欢
网友评论
- 搜索
- 最新文章
- 热门文章