服务器端
//服务器端
//服务器端
//服务器端
#include<head.h>
#define ERR_MSG(MSG) do\
{printf("报错行号是:%d\n",__LINE__);\
perror(MSG);\
return -1;}\
while(0)
//结构体
typedef struct __MSG
{
char type;//L登录,C群聊,Q退出
char name[30];//用户名字
char text[256];//发送的消息
}msg_t;
//创建保存客户端链表
typedef struct __CLIADDR
{
struct sockaddr_in cliaddr;
struct __CLIADDR * next;
}node_t;
//登录的函数
void do_login(int sfd,msg_t msg,node_t * addr,struct sockaddr_in cliaddr)
{
node_t * p = addr;
//遍历链表
while(p->next != NULL)
{
p = p->next;
if((sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&p->cliaddr,sizeof(p->cliaddr))) == -1)
{
printf("sendto error");
}
}
//头插
node_t * newp = NULL;
if(((newp = ((node_t *)malloc(sizeof(node_t)))) == NULL))
{
printf("malloc error");
return;
}
newp->cliaddr = cliaddr;
newp->next = addr->next;
addr->next = newp;
printf("[%s]登录成功\n",msg.name);
return;
}
//群聊操作的函数
void do_chat(int sfd,msg_t msg,node_t * addr,struct sockaddr_in cliaddr)
{
node_t * p = addr;
//遍历链表
while(p->next != NULL)
{
p = p->next;
if(memcmp(&cliaddr,&(p->cliaddr),sizeof(cliaddr)))
{
//说明不是自己,就发送数据
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&(p->cliaddr),sizeof(p->cliaddr)) == -1)
{
printf("sendto error");
}
}
}
//发送用户聊天成功的消息
printf("[%s]CHAT成功\n",msg.name);
return;
}
//退出操作的函数
void do_quit(int sfd,msg_t msg,node_t * addr,struct sockaddr_in cliaddr)
{
node_t * p = addr;
//遍历链表 是自己就将自己在链表中删除
//不是自己就发送退出群聊的数据
node_t * del = NULL;
while(p->next != NULL)
{
//判断addr结构体是否相等
if(memcmp(&(p->next->cliaddr),&cliaddr,sizeof(cliaddr)))
{
//不是自己
p = p->next;
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr *)&(p->cliaddr),sizeof(p->cliaddr)) == -1)
{
printf("sendto error");
}
}
else
{
//是自己
//删除对应的链表结点
del = p->next;
p->next = del->next;
free(del);
del = NULL;
}
}
//发送用户退出的消息
printf("[%s]退出\n",msg.name);
return;
}
int main(int argc, const char *argv[])
{
//判断终端输入进来的ip和端口是否正确
if(3 != argc)
{
printf("输入错误\n");
return -1;
}
//创建套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd == -1)
{
ERR_MSG("socket");
}
//填充服务器结构体
struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(atoi(argv[2]));
seraddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t ser_len = sizeof(seraddr);
//将套接字绑定到结构体
if(bind(sfd,(struct sockaddr*)&seraddr,ser_len) == -1)
{
ERR_MSG("bind");
}
//填充客户端结构体
struct sockaddr_in cliaddr;
cliaddr.sin_family = AF_INET;
socklen_t cli_len = sizeof(cliaddr);
//定义接受消息的msg
msg_t msg;
//创建进程
pid_t pid = fork();
//进程错误消息
if(pid == -1)
{
printf("fork error");
}
//子进程
if(pid == 0)
{
//创建保存客户端的链表头节点
node_t * addr;
addr = ((node_t*)malloc(sizeof(node_t)));
if(addr == NULL)
{
printf("malloc error");
return -1;
}
//节点清空
memset(addr,0,sizeof(addr));
addr->next = NULL;
while(1)
{
//清空结构体
memset(&msg,0,sizeof(msg));
memset(&cliaddr,0,sizeof(cliaddr));
//接受客户端发送的消息,存放msg
if((recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cliaddr,&cli_len)) == -1)
{
ERR_MSG("recvfrom");
}
//判断type
switch(msg.type)
{
case 'L': //登录操作
do_login(sfd,msg,addr,cliaddr);
break;
case 'C'://群聊
do_chat(sfd,msg,addr,cliaddr);
break;
case 'Q'://退出
do_quit(sfd,msg,addr,cliaddr);
break;
}
}
}
else//父进程
{
//把名字改为系统消息
strcpy(msg.name,"系统消息");
//把类型改为聊天类型
msg.type = 'C';
while(1)
{
//清空消息
memset(msg.text,0,sizeof(msg.text));
//接受终端的消息
fgets(msg.text,sizeof(msg.text),stdin);
//把\n换成\0
msg.text[strlen(msg.text)-1] = 0;
//发送系统消息
if((sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&seraddr,ser_len)) == -1)
{
printf("sendto error");
}
}
}
//关闭套接字
close(sfd);
return 0;
}
客户端
//客户端
//客户端
//客户端
#include<head.h>
#define ERR_MSG(MSG) do\
{printf("报错行号是:%d\n",__LINE__);\
perror(MSG);\
return -1;}\
while(0)
//结构体
typedef struct __msg
{
//L是登录,C表示群聊,Q表示退出,S表示系统消息
char type;
char name[30];
char text[256];
}msg_t;
int main(int argc, const char *argv[])
{
//判断终端输入ip port是否正确
if(argc != 3)
{
printf("输入错误\n");
return -1;
}
//创建套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd == -1)
{
ERR_MSG("socket");
}
//填充结构体
struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(atoi(argv[2]));
seraddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t seraddr_len = sizeof(seraddr);
//定义结构体
msg_t msg;
memset(&msg,0,sizeof(msg));
printf("请输入用户名-->");
//从终端输入用户名
fgets(msg.name,sizeof(msg.name),stdin);
msg.name[strlen(msg.name)-1] = 0;
//使type变成登录
msg.type = 'L';
strcpy(msg.text,"加入群聊");
//给服务器发送登录的消息
if((sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&seraddr,seraddr_len)) == -1)
{
ERR_MSG("sendto");
}
//创建父子进程
pid_t pid = fork();
//子进程
if(pid == 0)
{
while(1)
{
//结构体清空
bzero(&msg,sizeof(msg));
//接收消息
if((recvfrom(sfd,&msg,sizeof(msg),0,NULL,NULL)) == -1)
{
printf("recvfrom error");
}
//打印名字和消息
printf("[%s]:%s\n",msg.name,msg.text);
}
}
//父进程
else if(pid > 0)
{
while(1)
{
//清空消息
memset(msg.text,0,sizeof(msg.text));
//终端输入消息
fgets(msg.text,sizeof(msg.text),stdin);
//使\n变成\0
msg.text[strlen(msg.text)-1] = 0;
//判断退出消息
if(!strcmp(msg.text,"quit"))
{
msg.type = 'Q';
strcpy(msg.text,"退出群聊");
}
else
{
//否则为聊天
msg.type = 'C';
}
//发送消息
if((sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&seraddr,seraddr_len)) == -1)
{
printf("sendto error");
}
if(!strcmp(msg.text,"退出群聊"))
{
break;
}
}
//给子进程发送
kill(pid,SIGKILL);
//回收子进程资源
wait(NULL);
}
//关闭套接字
close(sfd);
return 0;
}