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