1.服务器端
#include "udp.h" int main(int argc, const char *argv[]) { printf("******************************************************\n"); printf("******************************************************\n"); printf("************************聊天室************************\n"); printf("******************************************************\n"); printf("******************************************************\n"); signal(SIGCHLD,handler); node_p H=create_H(); int sfd=socket(AF_INET,SOCK_DGRAM,0); if(sfd==-1) { perror("scoket error"); return -1; } // printf("socket success 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; } // printf("bind success\n"); struct sockaddr_in cin; socklen_t socklen=sizeof(cin); Msg recvmsg; char rbuf[128]; pid_t pid; pid=fork(); if(pid==0) { while(1) { memset(&recvmsg,0,sizeof(recvmsg)); if(recvfrom(sfd,&recvmsg,sizeof(recvmsg),0,\ (struct sockaddr*)&cin,&socklen)==-1) { perror("recvfrom error"); return -1; } switch(recvmsg.type) { case 'L': //向连接客户端所有用户遍历发送xx登录 //并且将用户信息添加进链表 //用户自己不需要接收自己登录信息,所以后插入 inset_usrmsg(sfd,H,recvmsg,cin); break; case 'C': //聊天信息将发送到除去发送用户的每一个人 sendto_chatmsg(sfd,H,recvmsg,cin); break; case 'Q': //用户退出信息 //遍历链表如果不是退出用户信息,将其他用户退出信息发送 //若果是此用户将其用户信息从链表中删除 sendto_quitmsg(sfd,H,recvmsg,cin); break; } } exit(EXIT_SUCCESS); } else if(pid>0) { //系统发送消息到所有客户端为C类消息 recvmsg.type='C'; strcpy(recvmsg.usrname,"系统消息"); while(1) { //将信息发送到子进程,由子进程转发 fgets(recvmsg.msgtext,sizeof(recvmsg.msgtext),stdin); recvmsg.msgtext[strlen(recvmsg.msgtext)-1]=0; if(sendto(sfd,&recvmsg,sizeof(recvmsg),0,(struct sockaddr*)&sin,sizeof(sin))==-1) { perror("sendto error"); return -1; } // printf("ok\n"); } } close(sfd); return 0; } //创建头结点 node_p create_H() { node_p H=(node_p)malloc(sizeof(node)); if(NULL==H) { printf("申请空间失败\n"); return NULL; } H->next=NULL; return H; } //申请新节点保存客户端信息数据 node_p create_new_point(node_p H,Msg recvmsg,struct sockaddr_in cin) { node_p new=(node_p)malloc(sizeof(node)); if(NULL==new) { printf("申请空间失败\n"); return NULL; } //将接收到的信息结构体中的信息保存到新节点中 strcpy(new->name,recvmsg.usrname); new->cin=cin; new->next=H->next; H->next=new; printf("%s:%s\n",recvmsg.usrname,recvmsg.msgtext); return new; } int inset_usrmsg(int sfd,node_p H,Msg recvmsg,struct sockaddr_in cin) { if(NULL==H) { printf("入参为空\n"); return -1; } //直接将 xx登录 消息发送给当前聊天室内成员 node_p p=H; while(p->next!=NULL) { p=p->next; if(sendto(sfd,&recvmsg,sizeof(recvmsg),0,(struct sockaddr*)&(p->cin),sizeof(p->cin))==-1) { perror("error sendto"); return -1; } } node_p new=create_new_point(H,recvmsg,cin); return 0; } int sendto_chatmsg(int sfd,node_p H,Msg recvmsg,struct sockaddr_in cin) { if(NULL==H) { printf("入参为空\n"); return -1; } node_p p=H; //遍历整条链表,如果不是发送节点用户,服务器将消息转发 while(p->next!=NULL) { p=p->next; if(memcmp(&(p->cin),&cin,sizeof(cin))) { if(sendto(sfd,&recvmsg,sizeof(recvmsg),0,(struct sockaddr*)&(p->cin),sizeof(p->cin))==-1) { perror("send sfd"); return -1; } } } printf("----------------%schat---------------\n",recvmsg.usrname); return 0; } int sendto_quitmsg(int sfd,node_p H,Msg recvmsg,struct sockaddr_in cin) { if(NULL==H) { printf("入参为空\n"); return -1; } node_p p=H; while(p->next!=NULL) { //遍历节点,先找到退出登录的用户节点的前一个节点 //其他节点直接转发用户突出登录信息 if(memcmp(&(p->next->cin),&cin,sizeof(cin))) { p=p->next; if(sendto(sfd,&recvmsg,sizeof(recvmsg),0,(struct sockaddr*)&(p->cin),sizeof(p->cin))==-1) { perror("error sfd"); return -1; } } else { node_p dele=p->next; p->next=dele->next; free(dele); dele->next=NULL; } } } void handler(int signum) { if(signum==SIGCHLD) { while(waitpid(-1,NULL,WNOHANG)>0); } }2.客户端
#include "udp.h" int main(int argc, const char *argv[]) { if(argc!=3) { printf("请绑定IP地址与端口号\n"); return -1; } printf("***************Jion:加入聊天室*************\n"); printf("***************Quit:我再想一想*************\n"); char chat[32]; scanf("%s",chat); while(getchar()!=10); if(chat[0]=='j'||chat[0]=='J') { printf("****WELCOME****\n"); } else { return 0; } int cfd=socket(AF_INET,SOCK_DGRAM,0); if(cfd==-1) { perror("socket error"); return -1; } // printf("socket success cfd=%d\n",cfd); struct sockaddr_in cin; cin.sin_family=AF_INET; cin.sin_port=htons(atoi(argv[2])); cin.sin_addr.s_addr=inet_addr(argv[1]); if(bind(cfd,(struct sockaddr*)&cin,sizeof(cin))==-1) { perror("bind error"); return -1; } // printf("bind success\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); //客户端运行用户登录 Msg msgsend; bzero(&msgsend,sizeof(msgsend)); //printf();//提示是否登录 printf("请输入用户名:"); scanf("%s",msgsend.usrname); while(getchar()!=10); char name[32]; bzero(name,32); strcpy(name,msgsend.usrname); printf("登陆成功\n"); printf("输入任意键清屏\n"); getchar(); system("clear"); strcat(msgsend.msgtext,"已登陆"); msgsend.type='L';//登录消息消息类型为L sendto(cfd,&msgsend,sizeof(msgsend),0,\ (struct sockaddr*)&sin,sizeof(sin));//向客户端发送登录信息 struct pollfd pfds[2]; pfds[0].fd=0; pfds[0].events=POLLIN; pfds[1].fd=cfd; pfds[1].events=POLLIN; char wbuf[128]; while(1) { int res=poll(pfds,2,-1); if(res==0) { printf("time out\n"); return -1; } else if(res==-1) { perror("poll error"); return -1; } if(pfds[0].revents==POLLIN) { //群聊信息 bzero(&msgsend,sizeof(msgsend)); strcpy(msgsend.usrname,name); fgets(msgsend.msgtext,sizeof(msgsend.msgtext),stdin); msgsend.msgtext[strlen(msgsend.msgtext)-1]=0; if(strcmp(msgsend.msgtext,"quit")) { msgsend.type='C'; sendto(cfd,&msgsend,sizeof(msgsend),0,\ (struct sockaddr*)&sin,sizeof(sin)); } else if(strcmp(msgsend.msgtext,"quit")==0) { msgsend.type='Q'; strcpy(msgsend.msgtext,"退出群聊"); if(sendto(cfd,&msgsend,sizeof(msgsend),0,\ (struct sockaddr*)&sin,sizeof(sin))==-1) { perror("sendto error"); return -1; } close(cfd); printf("您已经退出群聊\n"); break; } } else if(pfds[1].revents==POLLIN) { //接受服务器发送的消息 //包括服务器转发的其他用户消息,与系统消息 bzero(&msgsend,sizeof(msgsend)); if(recvfrom(cfd,&msgsend,sizeof(msgsend),0,NULL,NULL)==-1) { perror("recvfrom error"); return -1; } printf("%s:%s\n",msgsend.usrname,msgsend.msgtext); } } return 0; }3.头文件
#ifndef __UDP_H__ #define __UDP_H__ #include <myhead.h> #define SER_IP "192.168.125.20" #define SER_PORT 7171 typedef struct node { char name[32]; struct sockaddr_in cin; struct node* next; }node,*node_p; typedef struct msgTyp { char type;//L.C.Q char usrname[20]; char msgtext[1024]; }Msg; node_p create_H(); node_p create_new_point(node_p H,Msg recvmsg,struct sockaddr_in cin); int inset_usrmsg(int sfd,node_p H,Msg recvmsg,struct sockaddr_in cin); int sendto_chatmsg(int sfd,node_p H,Msg recvmsg,struct sockaddr_in cin); int sendto_quitmsg(int sfd,node_p H,Msg recvmsg,struct sockaddr_in cin); void handler(int signum); #endif