网络聊天室
服务器端
#include<myhead.h>
#define PORT 8888//服务器端口
#define IP "192.168.125.61"//服务器ip
//用户发送的消息属性结构体
typedef struct {
char type; // 'L':登陆 'C':群聊 'q':下线
char name[24]; // 用户名
char text[128]; // 信息内容
} msg_c;
//用户地址信息信息链表
typedef struct node {
struct sockaddr_in cin; // 客户端地址信息
struct node* next; // 指向下一个节点
} linklist_t;
//链表函数声明
linklist_t* linklist_create();
linklist_t* node_buy(struct sockaddr_in cin);
int head_insert(linklist_t*q,struct sockaddr_in cin);
//服务器函数声明
void login(linklist_t* q,int sfd,msg_c msg,struct sockaddr_in cin);
void chat(linklist_t* q,int sfd,msg_c msg,struct sockaddr_in cin);
void quit(linklist_t* q,int sfd,msg_c msg,struct sockaddr_in cin);
void system_msg(int sfd,msg_c msg,struct sockaddr_in sin);
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
printf("socket create success\n");
//填写服务器地址信息结构体给bind使用
struct sockaddr_in sin;
sin.sin_family=AF_INET;//地址族
sin.sin_port=htons(PORT);//服务器端口
sin.sin_addr.s_addr=inet_addr(IP);//服务器IP
//绑定服务器地址信息
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("bind");
return -1;
}
printf("bind success\n");
//创建客户端地址信息结构体
struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);
//创建单项循环链表存储客户端地址信息
linklist_t* q =linklist_create();
if(NULL==q)
{
return -1;
}
//创建子进程(父进程负责接收子进程负责发送)
pid_t cpid=fork();
//接收消息
if(cpid>0)
{
msg_c msg;
while(1)
{
if(recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,&addrlen)<0)
{
ERR_MSG("recvfrom");
return -1;
}
//打印接收信息
printf("%c %s\n",msg.type,msg.name);
//判断接收消息类型
switch(msg.type)
{
case 'L':
//登录函数
login(q,sfd,msg,cin);
break;
case 'C':
//群发函数
chat(q,sfd,msg,cin);
break;
case 'q':
//下线函数
quit(q,sfd,msg,cin);
break;
default:
printf("输入错误\n");
break;
}
}
}
//发送服务器消息
else if(0==cpid)
{
msg_c msg;
msg.type='C';
strcpy(msg.name,"<系统消息>");
while(1)
{
scanf("%s",msg.text);
while(getchar()!=10);
//发送系统信息函数
system_msg(sfd,msg,sin);
}
}
else
{
ERR_MSG("fork");
return -1;
}
//关闭文件描述符
close(sfd);
return 0;
}
//----------------------------------链表相关函数----------------------------------
//申请普通结点函数
linklist_t* node_buy(struct sockaddr_in cin)
{
//申请结点
linklist_t* n=(linklist_t*)malloc(sizeof(linklist_t));
if(NULL==n)
{
printf("申请失败\n");
return NULL;
}
n->cin=cin;
n->next=NULL;
return n;
}
//申请头结点函数
linklist_t* linklist_create()
{
linklist_t* q=(linklist_t*)malloc(sizeof(linklist_t));
if(NULL==q)
{
printf("创建失败\n");
return NULL;
}
//初始化头结点
q->next=q;
return q;
}
//链表头插函数
int head_insert(linklist_t* q,struct sockaddr_in cin)
{
if(NULL==q)
{
printf("insert error\n");
return -1;
}
//创建普通结点
linklist_t* newNode=node_buy(cin);
if(NULL==newNode)
{
printf("create error\n");
return -1;
}
//插入节点
newNode->next=q->next;
q->next=newNode;
return 0;
}
//----------------------------------链表相关函数----------------------------------
//----------------------------------服务器相关函数----------------------------------
//客户端登录函数
void login(linklist_t* q,int sfd,msg_c msg,struct sockaddr_in cin)
{
//校验是否存在在线用户
if(q->next)
{
//设置用户消息属性
msg.type='C';
//遍历当前在线客户端
linklist_t* temp=q;
while(temp->next!=q)
{
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&temp->next->cin,\
sizeof(temp->next->cin))==-1)
{
ERR_MSG("sendto");
continue;
}
//跳到下一个用户
temp=temp->next;
}
}
head_insert(q,cin);
printf("用户[%s]:登录信息广播成功\n",msg.name);
}
//客户端群发信息函数
void chat(linklist_t* q,int sfd,msg_c msg,struct sockaddr_in cin)
{
//将客户端信息转发给其他在线的客户端
//校验是否存在在线用户
if(q->next)
{
linklist_t* temp=q;
while(temp->next!=q)
{
//判断当前用户是否是发消息的用户
if(temp->next->cin.sin_port==cin.sin_port)
{
temp=temp->next;//跳过当前用户
continue;//跳过本次循环
}
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&temp->next->cin,\
sizeof(temp->next->cin))==-1)
{
ERR_MSG("sendto");
continue;
}
temp=temp->next;
}
}
printf("用户[%s]:客户端群聊信息成功广播\n",msg.name);
}
//客户端下线函数
void quit(linklist_t* q,int sfd,msg_c msg,struct sockaddr_in cin)
{
//将客户端下线信息转发给其他在线的客户端
//校验是否存在在线用户
if(q->next)
{
msg.type='C';
strcpy(msg.text,"老子下线了");
//遍历当前在线客户端
linklist_t* temp=q;
while(temp->next!=q)
{
//判断当前用户是否是要下线的用户(遍历判断)
if(temp->next->cin.sin_port==cin.sin_port)
{
linklist_t* delete_c=temp->next;//标记下线用户
temp->next=temp->next->next;//孤立
free(delete_c);//删除用户
delete_c=NULL;//指针置空防止野指针
continue;
}
//给所有用户发送该用户的下线信息
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&temp->next->cin,\
sizeof(temp->next->cin))==-1)
{
ERR_MSG("sendto");
continue;
}
temp=temp->next;
}
}
printf("用户[%s]:客户端下线信息广播成功\n",msg.name);
}
//服务器端发送信息函数
void system_msg(int sfd,msg_c msg,struct sockaddr_in sin)
{
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
ERR_MSG("sendto");
}
}
//----------------------------------服务器相关函数----------------------------------
客户端代码
#include<myhead.h>
#define PORT 8888//服务器端口
#define IP "192.168.125.61"//服务器ip
//用户发送的消息属性结构体
typedef struct {
char type; // 'L':登陆 'C':群聊 'q':下线
char name[24]; // 用户名
char text[128]; // 信息内容
} msg_c;
// 子进程捕捉父进程发送的10号信号
void signalhandler(int sig)
{
exit(0);
}
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
printf("socket create success\n");
struct sockaddr_in sin;
sin.sin_family=AF_INET;//地址族
sin.sin_port=htons(PORT);//服务器端口
sin.sin_addr.s_addr=inet_addr(IP);//服务器IP
//录入登录信息
msg_c msg;
//初始化用户消息属性为登录属性
msg.type='L';
//初始化用户名
printf("请输入用户名\n");
scanf("%s",msg.name);
//吸收垃圾字符
while(getchar()!=10);
//登录描述信息
strcpy(msg.text,"我特么来辣");
msg_c copy=msg;
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
ERR_MSG("sendto");
return -1;
}
printf("登录成功\n");
//创建地址信息结构体用于接收信息
struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);
//创建子进程(父进程负责接收子进程负责发送)
pid_t cpid=fork();
//接收消息
if(cpid>0)
{
msg.type='C';
while(1)
{
//终端获取要发送的信息内容
scanf("%s",msg.text);
while(getchar()!=10);
//比对消息内容是否为quit
if(strcmp(msg.text,"quit")==0)
{
msg.type='q';
}
//发送给服务器该用户的下线消息
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
ERR_MSG("sendto");
continue;
}
//执行完群发下线信息后退出子进程
if (msg.type == 'q')
{
if(kill(cpid,SIGUSR1)==-1)
{
ERR_MSG("kill");
}
wait(NULL);
break;
}
}
}
//发送服务器消息
else if(0==cpid)
{
if(signal(SIGUSR1,signalhandler)==SIG_ERR)
{
ERR_MSG("signal");
exit(0);
}
while(1)
{
if(recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,&addrlen)<0)
{
ERR_MSG("recvfrom");
return -1;
}
printf("[%s]:%s\n",msg.name,msg.text);
}
}
else
{
ERR_MSG("fork");
return -1;
}
//关闭文件描述符
close(sfd);
return 0;
}