项目需求:
- 如果有用户登录,其他用户可以收到这个人的登录信息
- 如果有人发送信息,其他用户可以收到这个人的群聊信息
- 如果有人下线,其他用户可以收到这个人的下线信息
- 服务器可以发送系统信息

server.c
#include <myhead.h>
struct Node
{char usrName[20]; //用户名struct sockaddr_in cin;struct Node *next; //指针域
};struct msgTyp
{char type;char usrName[20];char msgText[1024];
};int main(int argc,const char *argv[])
{if(argc!=3){printf("请输入IP地址和端口号\n");return -1;}//创建用于通信的服务器套接字文件描述符int sfd = socket(AF_INET,SOCK_DGRAM,0);if(sfd==-1){perror("socket error");return -1;}//为套接字绑定ip地址和端口号struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(atoi(argv[2]));sin.sin_addr.s_addr = inet_addr(argv[1]);if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))==-1){perror("bind error");return -1;} struct msgTyp buf;struct sockaddr_in cin;socklen_t addrlen = sizeof(cin);recvfrom(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&cin,&addrlen);struct Node *L = (struct Node *)malloc(sizeof(struct Node));if(buf.type=='L'){strcpy(L->usrName,buf.usrName);L->cin = cin;L->next = NULL;printf("%s[%s:%d]登陆成功\n",buf.usrName,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));}//创建父子进程pid_t pid = fork();if(pid<0){perror("fork error");return -1;}if(pid==0){//子进程,用于发送系统消息while(1){memset(&buf,0,sizeof(buf));char msgText[1024]="";fgets(msgText,sizeof(msgText),stdin);msgText[strlen(msgText)-1]=0;buf.type = 'S';strcpy(buf.usrName,"**system**");strcpy(buf.msgText,msgText);//给父进程发送一个消息,在由父进程转发给用户if(sendto(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1){perror("发送error");return -1;}printf("%s[%s:%s]chat成功\n",buf.usrName,argv[1],argv[2]);}exit(EXIT_SUCCESS);}//父进程,用于收消息struct Node *P=L;while(1){memset(&buf,0,sizeof(buf));recvfrom(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&cin,&addrlen);if(buf.type=='L') //表示发送的是登陆信息{//遍历链表,告知其他人有人登陆了struct Node *Q=L;struct sockaddr_in sin;while(Q!=NULL){sin = Q->cin;if(sendto(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1){perror("发送error");return -1;}Q=Q->next;}//将信息放入链表struct Node *F = (struct Node *)malloc(sizeof(struct Node));strcpy(F->usrName,buf.usrName);F->cin = cin;F->next = NULL;P->next = F;P = F;printf("%s[%s:%d]登陆成功\n",buf.usrName,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));}else if(buf.type=='C'||buf.type=='S') //表示发送的是聊天信息{//遍历链表,将聊天信息告诉链表中的所有人struct Node *Q=L;struct sockaddr_in sin;while(Q!=NULL){sin = Q->cin;if(strcmp(Q->usrName,buf.usrName)==0){Q=Q->next;continue;}if(sendto(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1){perror("发送error");return -1;}Q=Q->next;}if(buf.type=='C'){printf("%s[%s:%d]chat成功\n",buf.usrName,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));}}else if(buf.type=='Q') //表示发送的是退出信息{//遍历链表,告知其他人此用户已经退出struct Node *Q=L;struct sockaddr_in sin;while(Q!=NULL){sin = Q->cin;if(sendto(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1){perror("发送error");return -1;}Q=Q->next;}//遍历链表,删除退出用户的链表信息Q=L;if(strcmp(Q->usrName,buf.usrName)==0){L=L->next;free(Q);Q=NULL;}else{while(strcmp(Q->next->usrName,buf.usrName)!=0){Q=Q->next;}struct Node *B=Q->next;Q->next = B->next;free(B);B=NULL;}}}close(sfd);return 0;
}
client.c
#include <myhead.h>struct msgTyp
{char type;char usrName[20];char msgText[1024];
};int main(int argc,const char *argv[])
{if(argc!=3){printf("请输入IP地址和端口号\n");return -1;}//创建用于通信的服务器套接字文件描述符int cfd = socket(AF_INET,SOCK_DGRAM,0);if(cfd==-1){perror("socket error");return -1;}//给服务器发送一个登陆信息struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(atoi(argv[2]));sin.sin_addr.s_addr = inet_addr(argv[1]);char usrname[20]="";printf("请输入姓名>>>");fgets(usrname,sizeof(usrname),stdin);usrname[strlen(usrname)-1]=0;struct msgTyp buf;buf.type = 'L';strcpy(buf.usrName,usrname);if(sendto(cfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1){perror("发送error");return -1;}//创建父子进程pid_t pid = fork();if(pid<0){perror("fork error");return -1;}if(pid==0){//子进程,用于发送消息while(1){memset(&buf,0,sizeof(buf));char msgText[1024]="";fgets(msgText,sizeof(msgText),stdin);msgText[strlen(msgText)-1]=0;if(strcmp(msgText,"quit")==0){buf.type = 'Q';}else{buf.type = 'C';}strcpy(buf.usrName,usrname);strcpy(buf.msgText,msgText);if(sendto(cfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1){perror("发送error");return -1;}if(buf.type == 'Q'){break;}}exit(EXIT_SUCCESS);}//父进程,用于收消息while(1){memset(&buf,0,sizeof(buf));recv(cfd,&buf,sizeof(buf),0);if(buf.type=='L') //表示有其他人登陆了{printf("-----%s登录成功-----\n",buf.usrName);}else if(buf.type=='C'||buf.type=='S') //表示有人发送了聊天信息{printf("%s:%s\n",buf.usrName,buf.msgText);}else if(buf.type=='Q') //表示有人发送了退出信息{if(strcmp(buf.usrName,usrname)==0){break;}printf("-----%s 已下线-----\n",buf.usrName);}}wait(NULL);close(cfd);return 0;
}
