聊天室服务器:
#include
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__\n",__LINE__);\
perror(msg);\
}while(0);
#define IP "192.168.114.63"
//#define IP "10.102.145.189"
#define PORT 8888
typedef struct node
{
struct sockaddr_in sin;
struct node *next;
}Node,*LinkList_ptr;
typedef struct msg
{
char type;
char name[20];
char text[100];
}Msg;
void sendto_chat(int sfd,const char *buf,LinkList_ptr L,struct sockaddr_in cin)
{
LinkList_ptr q=L;
while(q->next!=NULL)
{
q=q->next;
if(memcmp(&cin,&(q->sin),sizeof(cin))!=0)
{
if(sendto(sfd,buf,strlen(buf),0,(struct sockaddr*)&(q->sin),sizeof(q->sin))<0)
{
ERR_MSG("sendto");
return;
}
}
}
}
void sendto_all(int sfd,struct sockaddr_in sin)
{
Msg sysmsg;
sysmsg.type='C';
strcpy(sysmsg.name,"system");
bzero(sysmsg.text,sizeof(sysmsg.text));
fgets(sysmsg.text,sizeof(sysmsg.text),stdin);
sysmsg.text[strlen(sysmsg.text)-1]=0;
if(sendto(sfd,&sysmsg,sizeof(sysmsg),0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return;
}
}
int list_empty(LinkList_ptr L)
{
if(NULL == L)
{
printf("所给链表不合法\n");
return -1;
}
return L->next == NULL;
}
void list_delete(LinkList_ptr L)
{
if(NULL == L || list_empty(L))
{
printf("头删失败\n");
return;
}
LinkList_ptr q=L->next;
L->next=L->next->next;
free(q);
q = NULL;
printf("头删成功\n");
}
void sendto_system(int sfd,struct sockaddr_in sin)
{
char buf[128]="";
Msg climsg;
while(1)
{
sendto_all(sfd,sin);
printf("发送系统消息成功\n");
}
}
void deal_L(int sfd,LinkList_ptr L,struct sockaddr_in cin,Msg climsg)
{
char buf[128]="";
LinkList_ptr p=(LinkList_ptr)malloc(sizeof(Node));
p->next=L->next;
L->next=p;
bzero(buf,sizeof(buf));
sprintf(buf,"%s玩家登录成功",climsg.name);
p->sin=cin;
sendto_chat(sfd,buf,L,cin);
printf("[%s:%d:%s]客户端连接成功\n",\
inet_ntoa(p->sin.sin_addr),ntohs(p->sin.sin_port),climsg.name);
}
void deal_C(int sfd,LinkList_ptr L,struct sockaddr_in cin,Msg climsg)
{
char buf[128]="";
bzero(buf,sizeof(buf));
sprintf(buf,"%s:%s",climsg.name,climsg.text);
sendto_chat(sfd,buf,L,cin);
printf("[%s:%d:%s]群聊消息发送成功\n",\
inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),climsg.name);
}
void deal_Q(int sfd,LinkList_ptr L,struct sockaddr_in cin,Msg climsg)
{
char buf[128]="";
LinkList_ptr q=L;
//找到要删除节点的上一个节点
while(memcmp(&cin,&q->next->sin,sizeof(cin))!=0)
{
q=q->next;
}
LinkList_ptr q1=q->next;
q->next=q->next->next;
free(q1);
q1=NULL;
bzero(buf,sizeof(buf));
sprintf(buf,"------------%s下线---------------",climsg.name);
sendto_chat(sfd,buf,L,cin);
printf("发送用户下线消息成功\n");
}
void ser_recvfrom(int sfd,LinkList_ptr L)
{
ssize_t recv_len;
char buf[128]="";
Msg climsg;
struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);
while(1)
{
recv_len=recvfrom(sfd,&climsg,sizeof(climsg),0,(struct sockaddr*)&cin,&addrlen);
if(recv_len < 0)
{
ERR_MSG("recvfrom");
return ;
}
switch(climsg.type)
{
case 'L':
deal_L(sfd,L,cin,climsg);
break;
case 'C':
deal_C(sfd,L,cin,climsg);
break;
case 'Q':
deal_Q(sfd,L,cin,climsg);
break;
}
}
}
void handler(int sig)
{
while(waitpid(-1,NULL,WNOHANG) > 0);
}
int main(int argc, const char *argv[])
{
if(signal(SIGCHLD,handler)<0)
{
ERR_MSG("signal");
return -1;
}
//创建报式套接字
int sfd;
if((sfd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
{
ERR_MSG("socket");
return -1;
}
printf("聊天室创建成功 sfd = %d\n",sfd);
//填充地址信息结构体给bind使用
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT); //服务器端口号
sin.sin_addr.s_addr = inet_addr(IP); //服务器IP
//绑定服务器地址信息bind
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("bind");
return -1;
}
printf("聊天室绑定IP端口成功\n");
//创建一个链表
LinkList_ptr L = (LinkList_ptr)malloc(sizeof(Node));
if(NULL == L)
{
ERR_MSG("create");
return -1;
}
L->next=NULL;
Msg climsg;
pid_t pid; //创造子进程用于发送系统消息
pid=fork();
if(pid<0)
{
ERR_MSG("fork");
return -1;
}
else if(0 == pid)
{
//子进程调用发送系统消息函数
sendto_system(sfd,sin);
exit(0);
}else
{
//父进程调用接受消息函数
ser_recvfrom(sfd,L);
}
//销毁链表
while(!list_empty(L))
{
list_delete(L);
}
free(L);
L=NULL;
//关闭文件
close(sfd);
return 0;
}
聊天室客户端:
#include
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__\n",__LINE__);\
perror(msg);\
}while(0)
#define IP "192.168.114.63"
//#define IP "10.102.145.189"
#define PORT 8888
//聊天信息结构体
typedef struct msg
{
char type; //聊天类型,L登录,C聊天,Q退出
char name[20]; //聊天室用户昵称
char text[100]; //群聊发送的消息
}Msg;
void cli_qq_L(int cfd,struct sockaddr_in sin,Msg* pclimsg)
{
pclimsg->type = 'L';
printf("请输入用户昵称:");
fgets(pclimsg->name,20,stdin);
pclimsg->name[strlen(pclimsg->name)-1]=0;
//发送登录请求,并让服务器保存昵称和地址信息
if(sendto(cfd,pclimsg,sizeof(Msg),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return ;
}
printf("发送登录请求和昵称成功\n");
}
void cli_qq_CQ(int cfd,struct sockaddr_in sin,Msg* pclimsg)
{
char buf[100]="";
while(1)
{
bzero(buf,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
if(strcmp(buf,"quit")==0)
{
pclimsg->type='Q';
if(sendto(cfd,pclimsg,sizeof(Msg),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return ;
}
printf("发送退出群聊消息成功\n");
return ;
}else{
pclimsg->type = 'C';
strcpy(pclimsg->text,buf);
}
if(sendto(cfd,pclimsg,sizeof(Msg),0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return ;
}
printf("发送群聊消息成功\n");
}
}
void cli_recvfrom(int cfd,struct sockaddr_in sin)
{
ssize_t ret = 0;
char buf[128]="";
socklen_t addrlen=sizeof(sin);
while(1)
{
bzero(buf,sizeof(buf));
if((ret = recvfrom(cfd,buf,sizeof(buf),0,NULL,NULL)) < 0)
{
ERR_MSG("recvfrom");
return ;
}
printf("%s\n",buf);
}
}
int main(int argc, const char *argv[])
{
//创建报式套接字
int cfd;
if((cfd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
{
ERR_MSG("socket");
return -1;
}
printf("socket success cfd = %d\n",cfd);
//非必须绑定
//填充地址信息结构体给sendto使用
struct sockaddr_in sin;
socklen_t addrlen=sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
Msg climsg;
cli_qq_L(cfd,sin,&climsg);
pid_t pid;
pid = fork();
if(pid < 0)
{
ERR_MSG("fork");
return -1;
}
else if(0 == pid)
{
cli_qq_CQ(cfd,sin,&climsg);
exit(0);
}else if(pid > 0)
{
cli_recvfrom(cfd,sin);
}
close(cfd);
return 0;
}